From 19bcebba636ee9e6337a25a6e4539a3cd78929ef Mon Sep 17 00:00:00 2001 From: Cano1997 <1978141412@qq.com> Date: Fri, 31 Oct 2025 15:56:11 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E6=9B=B4=E5=A4=9A=E6=8C=89=E9=92=AE=EF=BC=8C=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E5=A4=9A=E6=95=B0=E6=8D=AE=E3=80=81=E5=8D=A1=E7=89=87?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E6=9B=B4=E5=A4=9A=E6=A8=A1=E5=BC=8F=E4=BD=BF?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + src/common/add-more/add-more.scss | 14 ++++++++++++++ src/common/add-more/add-more.tsx | 20 ++++++++++++++++++++ src/common/index.ts | 3 +++ 4 files changed, 38 insertions(+) create mode 100644 src/common/add-more/add-more.scss create mode 100644 src/common/add-more/add-more.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index e4ac9164039..eff17b4cd23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ - 新增树部件加载更多和节点绘制器 - 标签编辑器支持转化为代码项文本 - 表单视图消息支持直接内容展示,无效配置为对象格式 +- 新增加载更多按钮,用于多数据、卡片加载更多模式使用 ### Change diff --git a/src/common/add-more/add-more.scss b/src/common/add-more/add-more.scss new file mode 100644 index 00000000000..eee6a2efc7d --- /dev/null +++ b/src/common/add-more/add-more.scss @@ -0,0 +1,14 @@ +$add-more: ( + color-bg: getCssVar(color, fill, 0), + color-text: getCssVar(color, primary), +); +@include b('add-more') { + @include set-component-css-var('add-more', $add-more); + width: 100%; + .van-button { + width: 100%; + border: none; + background-color: getCssVar(add-more, color-bg); + color: getCssVar(add-more, color-text); + } +} diff --git a/src/common/add-more/add-more.tsx b/src/common/add-more/add-more.tsx new file mode 100644 index 00000000000..9721ea58c3f --- /dev/null +++ b/src/common/add-more/add-more.tsx @@ -0,0 +1,20 @@ +import { defineComponent } from 'vue'; +import { useNamespace } from '@ibiz-template/vue3-util'; +import './add-more.scss'; + +export const IBizAddMore = defineComponent({ + name: 'IBizAddMore', + setup() { + const ns = useNamespace('add-more'); + return { ns }; + }, + render() { + return ( +
+ + {ibiz.i18n.t('control.common.loadMore')} + +
+ ); + }, +}); diff --git a/src/common/index.ts b/src/common/index.ts index 6a116581efc..dfd6b5ddf8c 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -31,6 +31,7 @@ import { IBizSplit } from './split/split'; import { IBizSplitTrigger } from './split-trigger/split-trigger'; import { IBizMdAdvanedSearchfrom } from './md-advaned-searchform/md-advaned-searchform'; import { IBizAddBtn } from './add-btn/add-btn'; +import { IBizAddMore } from './add-more/add-more'; export * from './col/col'; export * from './row/row'; @@ -38,10 +39,12 @@ export * from './keep-alive/keep-alive'; export * from './md-sort-setting/md-sort-setting'; export * from './md-advaned-searchform/md-advaned-searchform'; export * from './add-btn/add-btn'; +export * from './add-more/add-more'; export const IBizCommonComponents = { install: (v: App): void => { v.component(IBizAddBtn.name!, IBizAddBtn); + v.component(IBizAddMore.name!, IBizAddMore); v.component(IBizMdAdvanedSearchfrom.name!, IBizMdAdvanedSearchfrom); v.component(IBizSplit.name!, IBizSplit); v.component(IBizSplitTrigger.name!, IBizSplitTrigger); -- Gitee From c05136eaba9f9a298ac4ec23b505a157f781a934 Mon Sep 17 00:00:00 2001 From: Cano1997 <1978141412@qq.com> Date: Fri, 31 Oct 2025 15:58:50 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=E5=A4=9A=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E3=80=81=E5=8D=A1=E7=89=87=E9=80=9A=E7=94=A8=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E6=8F=90=E5=8F=96=EF=BC=8C=E6=94=AF=E6=8C=81=E6=BB=9A=E5=8A=A8?= =?UTF-8?q?=E5=8A=A0=E8=BD=BDloading=E3=80=81=E5=8A=A0=E8=BD=BD=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E6=8F=90=E7=A4=BA=E3=80=81=E5=88=86=E7=BB=84=E9=94=9A?= =?UTF-8?q?=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + src/locale/en/index.ts | 5 +- src/locale/zh-CN/index.ts | 6 +- src/util/index.ts | 1 + .../list-util}/list-render-util.tsx | 233 ++++++++++++++---- 5 files changed, 187 insertions(+), 59 deletions(-) rename src/{control/list => util/list-util}/list-render-util.tsx (40%) diff --git a/CHANGELOG.md b/CHANGELOG.md index eff17b4cd23..8ab2d6aef38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,6 +86,7 @@ - 优化搜索栏组件样式,不直接使用基础css变量,组件定义专属变量 - 统一处理界面行为按钮按钮类型、按钮行为级别、按钮样式 - 优化多数据选择编辑器的呈现样式 +- 多数据、卡片通用逻辑提取,支持滚动加载loading、加载完成提示、分组锚点 ### Fixed diff --git a/src/locale/en/index.ts b/src/locale/en/index.ts index f1155eaf4a0..b585af81875 100644 --- a/src/locale/en/index.ts +++ b/src/locale/en/index.ts @@ -79,7 +79,9 @@ export default { // 部件 control: { common: { - loadMore: 'Load more', + loadMore: 'Load more...', + loadFinish: 'I have made it to the end', + loadError: 'Loading failed. Click to reload', addbtn: 'Add', }, appmenu: { @@ -88,7 +90,6 @@ export default { customNav: 'Customize Navigation', save: 'Save', }, - dataView: { end: 'The end~' }, form: { noSupportDetailType: 'Form detail type not supported: {detailType} or corresponding provider cannot be found', diff --git a/src/locale/zh-CN/index.ts b/src/locale/zh-CN/index.ts index c3f0a3cc236..1196bf57c99 100644 --- a/src/locale/zh-CN/index.ts +++ b/src/locale/zh-CN/index.ts @@ -63,10 +63,11 @@ export default { // 部件 control: { common: { - loadMore: '加载更多', + loadMore: '加载更多...', + loadFinish: '我已经到底啦', + loadError: '加载失败,点击重新加载', addbtn: '新增', }, - dataView: { end: '我已经到底啦~' }, appmenu: { more: '更多', bottomNav: '底部导航', @@ -111,7 +112,6 @@ export default { list: { expand: '展开', selectedData: '选中数据', - end: '我已经到底啦~', }, searchBar: { confirm: '确认', diff --git a/src/util/index.ts b/src/util/index.ts index 4fc48e9be04..f5a33d7d797 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -13,3 +13,4 @@ export * from './store'; export { usePopstateListener } from './use-popstate-util/use-popstate-util'; export { QrcodeUtil } from './qrcode-util/qrcode-util'; export { convertBtnType } from './button-util/button-util'; +export { useListRender } from './list-util/list-render-util'; diff --git a/src/control/list/list-render-util.tsx b/src/util/list-util/list-render-util.tsx similarity index 40% rename from src/control/list/list-render-util.tsx rename to src/util/list-util/list-render-util.tsx index 37e584e55e9..bdc82f78fbb 100644 --- a/src/control/list/list-render-util.tsx +++ b/src/util/list-util/list-render-util.tsx @@ -1,34 +1,91 @@ import { Namespace } from '@ibiz-template/core'; -import { ListController, MDCtrlController } from '@ibiz-template/runtime'; -import { VNode } from 'vue'; -import { ILayoutPanel } from '@ibiz/model-core'; +import { + ControlVO, + MDControlController, + MDCtrlController, +} from '@ibiz-template/runtime'; +import { computed, Ref, ref, VNode, watch } from 'vue'; +import { IDEMobMDCtrl, ILayoutPanel } from '@ibiz/model-core'; import { JSX } from 'vue/jsx-runtime'; -/** - * 列表绘制工具 - * - * @author zk - * @date 2023-12-06 03:12:00 - * @export - * @param {IData} props - * @param {(IMobMDCtrlController | IListController)} c - * @param {Namespace} ns - * @return {*} {({ - * renderItem: (row: IData) => VNode | undefined; - * render: () => VNode | null; - * renderNoData: () => VNode | undefined; - * })} - */ export function useListRender( props: IData, - c: MDCtrlController | ListController, + c: MDControlController, ns: Namespace, ): { + enableLoadMore: Ref; renderItem: (row: IData) => VNode | undefined; renderNoData: () => VNode | undefined; renderLoadMore: () => JSX.Element | null; renderAddItem: (group?: IData) => JSX.Element | null; + renderScrollList: (slots: IData) => JSX.Element | null; + renderGroup: (slots: IData) => JSX.Element; } { + const { + name, + enablePagingBar, + pagingMode, + controlStyle, + itemLayoutPanel, + controls = [], + emptyText, + emptyTextLanguageRes, + } = c.model as IDEMobMDCtrl; + + // 是否加载失败,用于列表下拉加载失败时点击重新加载 + const isLoadError = ref(false); + + // 是否加载完成,用于判断数据加载(启用分页栏或加载更多时禁用滚动加载) + const isLodeFinished = ref(enablePagingBar === true || pagingMode === 3); + + // 是否加载更多,根据已加载数据与总数据条数判断 + const enableLoadMore = computed(() => { + return c.state.items.length < c.state.total && c.state.total > c.state.size; + }); + + // 加载完成后计算是否存在剩余数据 + c.evt.on('onLoadSuccess', () => { + if (!enablePagingBar && pagingMode !== 3) { + isLodeFinished.value = + c.state.items.length >= c.state.total || c.state.total <= c.state.size; + } + }); + + c.evt.on('onLoadError', () => { + isLoadError.value = true; + // 加载失败时重新加载当前页 + c.state.curPage -= 1; + }); + + // 本地数据模式 + const initSimpleData = (): void => { + if (!props.data) { + return; + } + c.state.items = (props.data as IData[]).map(item => new ControlVO(item)); + c.afterLoad({}, c.state.items as ControlVO[]); + }; + + c.evt.on('onCreated', async () => { + if (props.isSimple) { + initSimpleData(); + c.state.isSimple = true; + c.state.isLoaded = true; + } + }); + + watch( + () => props.data, + () => { + if (props.isSimple) { + initSimpleData(); + } + }, + { + deep: true, + }, + ); + const isSelect = (row: IData) => { const findIndex = c.state.selectedData.findIndex(data => { return data.srfkey === row.srfkey; @@ -47,10 +104,9 @@ export function useListRender( Object.assign( cardStyle, ns.cssVarBlock({ - 'item-bg-color': `${row.bgcolor || ''}`, - 'item-font-color': `${row.fontcolor || ''}`, - 'item-hover-color': `${row.hovercolor || ''}`, - 'item-active-color': `${row.activecolor || ''}`, + 'color-bg': `${row.bgcolor || ''}`, + 'color-text': `${row.fontcolor || ''}`, + 'color-item-active': `${row.activecolor || ''}`, }), ); return cardStyle; @@ -94,6 +150,9 @@ export function useListRender( const { context, params } = c; const itemClass = calcItemClass(item); const itemStyle = calcItemStyle(item); + if (controlStyle !== 'EXTVIEW1') { + itemClass.push('van-hairline--bottom'); + } const content = ( +
{ - const panel = c.model.itemLayoutPanel; - return props.modelData.name !== 'simplelist' && panel - ? renderPanelItem(row, panel) + return props.modelData.name !== 'simplelist' && itemLayoutPanel + ? renderPanelItem(row, itemLayoutPanel) : renderItemContent(row); }; @@ -137,8 +195,8 @@ export function useListRender( if (!isLoaded) { return; } - const ctrlModel = c.model.controls?.find(item => { - return item.name === `${c.model.name!}_quicktoolbar`; + const ctrlModel = controls.find(item => { + return item.name === `${name!}_quicktoolbar`; }); if (ctrlModel) { return ( @@ -153,47 +211,114 @@ export function useListRender( return ( isLoaded && ( ) ); }; - // 加载更多 - const loadMoreIcon = () => { - return ( -
- c.loadMore()}> - {ibiz.i18n.t('control.common.loadMore')} - -
- ); - }; - - // 分页模式为点击加载时并且当前数量小于总数 + // 分页模式为加载更多时并且当前数量小于总数 const renderLoadMore = () => { let icon = null; - const loadMore = - c.state.items.length < c.state.total && c.state.total > c.state.size; - if (loadMore) { - icon = loadMoreIcon(); + if (pagingMode === 3 && enableLoadMore.value) { + icon = c.loadMore()}>; } return icon; }; // 添加项 const renderAddItem = (group?: IData) => { + if (!(c as MDCtrlController).enableNew) { + return null; + } + return ( + { + (c as MDCtrlController).onClickNew(event, group?.key); + }} + > + ); + }; + + const renderScrollList = (slots: IData) => { + if (!c.state.isLoaded) { + return null; + } return ( -
- { - c.onClickNew(event, group?.key); - }} - > + c.loadMore()} + > + {slots} + {renderLoadMore()} + + ); + }; + + const renderGroup = (slots: IData) => { + const showGroupAnchor = + c.state.groups.length > 1 && (c as MDCtrlController).showGroupAnchor; + const content = ( +
+
+ {c.state.groups.map(group => { + let header = ( +
{group.caption}
+ ); + if (showGroupAnchor) { + header = ( + + {header} + + ); + } + return ( +
+ {header} +
+ {slots.children && slots.children(group.children)} + {renderAddItem(group)} +
+
+ ); + })} +
); + if (showGroupAnchor) { + const indexList = c.state.groups.map(x => x.caption); + return ( + + {content} + + ); + } + return content; }; - return { renderNoData, renderItem, renderLoadMore, renderAddItem }; + return { + enableLoadMore, + renderNoData, + renderItem, + renderLoadMore, + renderAddItem, + renderScrollList, + renderGroup, + }; } -- Gitee From 4c1c43055324da39cc1fb40b775ab2c237e2a44c Mon Sep 17 00:00:00 2001 From: Cano1997 <1978141412@qq.com> Date: Fri, 31 Oct 2025 15:59:54 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E7=BC=96=E8=BE=91=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E6=94=AF=E6=8C=81=E5=88=86=E7=BB=84=E9=94=9A=E7=82=B9?= =?UTF-8?q?=E3=80=81=E8=A1=A8=E5=8D=95=E9=A1=B9=E9=94=9A=E7=82=B9=EF=BC=8C?= =?UTF-8?q?=E9=94=9A=E7=82=B9=E4=BD=8D=E7=BD=AE=E6=94=AF=E6=8C=81=E5=8F=B3?= =?UTF-8?q?=E4=BE=A7=E4=B8=AD=E9=97=B4=E3=80=81=E5=8F=B3=E4=B8=8A=E8=A7=92?= =?UTF-8?q?=E3=80=81=E5=8F=B3=E4=B8=8B=E8=A7=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + src/control/form/edit-form/edit-form.scss | 23 +++++++++++ src/control/form/edit-form/edit-form.tsx | 38 +++++++++++++++++-- .../form-group-panel/form-group-panel.tsx | 7 +++- .../form-item-container.tsx | 9 ++++- 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ab2d6aef38..3f5932011cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ - 标签编辑器支持转化为代码项文本 - 表单视图消息支持直接内容展示,无效配置为对象格式 - 新增加载更多按钮,用于多数据、卡片加载更多模式使用 +- 编辑表单支持分组锚点、表单项锚点,锚点位置支持右侧中间、右上角、右下角 ### Change diff --git a/src/control/form/edit-form/edit-form.scss b/src/control/form/edit-form/edit-form.scss index e2a3acbdf2f..440c6240500 100644 --- a/src/control/form/edit-form/edit-form.scss +++ b/src/control/form/edit-form/edit-form.scss @@ -1,3 +1,26 @@ @include b(control-edit-form) { width: 100%; + @include e(anchor) { + position: relative; + .van-index-anchor { + padding: 0; + } + // 右下角 + @include m(bottomright) { + .van-index-bar__sidebar { + bottom: 0; + transform: none; + top: auto; + } + } + // 右上角 + @include m(topright) { + .van-index-bar__sidebar { + position: absolute; + top: 0; + transform: none; + bottom: auto; + } + } + } } diff --git a/src/control/form/edit-form/edit-form.tsx b/src/control/form/edit-form/edit-form.tsx index f2178e85a1d..5ea69026b69 100644 --- a/src/control/form/edit-form/edit-form.tsx +++ b/src/control/form/edit-form/edit-form.tsx @@ -3,7 +3,7 @@ import { EditFormController, IControlProvider } from '@ibiz-template/runtime'; import { useControlController, useNamespace } from '@ibiz-template/vue3-util'; import { IDEEditForm } from '@ibiz/model-core'; import { debounce } from 'lodash-es'; -import { defineComponent, PropType, reactive, ref, watch } from 'vue'; +import { defineComponent, PropType, reactive, Ref, ref, watch } from 'vue'; import './edit-form.scss'; export const EditFormControl: ReturnType = @@ -63,6 +63,9 @@ export const EditFormControl: ReturnType = const filter = ref(undefined); + // 所有启用了锚点的表单项 + const anchorList: Ref = ref([]); + if (props.isSimple) { if (props.simpleDataIndex || props.simpleDataIndex === 0) { c.setSimpleDataIndex(props.simpleDataIndex); @@ -99,6 +102,7 @@ export const EditFormControl: ReturnType = const detail = c.details[key]; detail.state = reactive(detail.state); }); + anchorList.value = c.anchorData; }); const handleInput = debounce( @@ -110,12 +114,18 @@ export const EditFormControl: ReturnType = { leading: true }, ); - return { c, ns, filter, handleInput }; + return { + c, + ns, + filter, + handleInput, + anchorList, + }; }, render() { - const { enableItemFilter } = this.c.model; - return ( + const { enableItemFilter, showFormNavBar } = this.c.model; + const content = (
{enableItemFilter && ( =
); + if (showFormNavBar) { + const { navBarSysCss, navBarPos } = this.c.model; + const items = this.anchorList.filter( + (item: IData) => item.pageId === this.c.state.activeTab, + ); + return ( + x.title)} + sticky={false} + class={[ + this.ns.e('anchor'), + navBarSysCss, + this.ns.em('anchor', navBarPos?.toLowerCase()), + ]} + > + {content} + + ); + } + return content; }, }); diff --git a/src/control/form/form-detail/form-group-panel/form-group-panel.tsx b/src/control/form/form-detail/form-group-panel/form-group-panel.tsx index 08abbd07c7b..6df6fdaa3ad 100644 --- a/src/control/form/form-detail/form-group-panel/form-group-panel.tsx +++ b/src/control/form/form-detail/form-group-panel/form-group-panel.tsx @@ -46,6 +46,7 @@ export const FormGroupPanel = defineComponent({ }, render() { const { state } = this.controller; + const { enableAnchor } = this.modelData; const defaultSlots: VNode[] = this.$slots.default?.() || []; const content = ( @@ -147,7 +148,11 @@ export const FormGroupPanel = defineComponent({ class={[classArr, this.ns.is('loading', this.controller.state.loading)]} onClick={(event: MouseEvent) => this.controller.onClick(event)} > - {header} + {enableAnchor ? ( + {header} + ) : ( + header + )}
{content}
{footer} {this.controller.state.loading ? ( diff --git a/src/control/form/form-detail/form-item/form-item-container/form-item-container.tsx b/src/control/form/form-detail/form-item/form-item-container/form-item-container.tsx index 019a5f615b2..5e181836256 100644 --- a/src/control/form/form-detail/form-item/form-item-container/form-item-container.tsx +++ b/src/control/form/form-detail/form-item/form-item-container/form-item-container.tsx @@ -99,6 +99,7 @@ export const IBizFormItemContainer = defineComponent({ }; const renderLabel = () => { + const { enableAnchor } = c.model; return ( )} - {c.labelCaption} + {enableAnchor ? ( + + {c.labelCaption} + + ) : ( + {c.labelCaption} + )}
); }, -- Gitee From f330d02c7112bc3ecb8b99154d3a3d5e951c5194 Mon Sep 17 00:00:00 2001 From: Cano1997 <1978141412@qq.com> Date: Fri, 31 Oct 2025 16:05:51 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E5=A4=9A=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E3=80=81=E5=8D=A1=E7=89=87=E5=8F=98=E9=87=8F=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E8=A7=84=E8=8C=83=E8=B0=83=E6=95=B4=EF=BC=8C=E9=83=A8=E4=BB=B6?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=BD=BF=E7=94=A8useListRender?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + src/control/data-view/data-view.scss | 168 ++++++---------- src/control/data-view/data-view.tsx | 266 ++++---------------------- src/control/list/list/list.tsx | 2 +- src/control/list/md-ctrl/md-ctrl.scss | 188 +++++++----------- src/control/list/md-ctrl/md-ctrl.tsx | 215 +++------------------ 6 files changed, 189 insertions(+), 651 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f5932011cf..06bf6ec5b45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,7 @@ - 统一处理界面行为按钮按钮类型、按钮行为级别、按钮样式 - 优化多数据选择编辑器的呈现样式 - 多数据、卡片通用逻辑提取,支持滚动加载loading、加载完成提示、分组锚点 +- 多数据、卡片变量样式规范调整,部件统一使用useListRender ### Fixed diff --git a/src/control/data-view/data-view.scss b/src/control/data-view/data-view.scss index 2faf3ee74be..5d99d07eb8a 100644 --- a/src/control/data-view/data-view.scss +++ b/src/control/data-view/data-view.scss @@ -1,26 +1,20 @@ $control-dataview: ( - text-color: getCssVar(color, text, 0), - item-padding: getCssVar(spacing, base-tight) getCssVar(spacing, base), - padding: getCssVar(spacing, tight), - item-gap: getCssVar(spacing, base, tight), - item-bg-color: transparent, - active-text-color: getCssVar(color, primary), - active-bg-color: getCssVar(color, primary, light, default), - group-font-size: getCssVar(font-size, header-6), - group-bg-color: getCssVar(color, bg, 0), - group-text-color: getCssVar(color, text, 1), - group-header-padding: getCssVar(spacing, base), - group-padding: 0 getCssVar(spacing, tight), - group-anchor-bg-color: getCssVar(color, bg, 1), - group-anchor-border-radius: getCssVar(border, radius, small), - group-anchor-right: getCssVar('spacing', 'tight'), - group-anchor-item-padding: getCssVar('spacing', 'tight') getCssVar('spacing', 'base'), - item-shadow: getCssVar(shadow, elevated), - button-padding: getCssVar(spacing, tight) 0, - button-bg: getCssVar(color, fill, 0), - button-active-bg: getCssVar(color, fill, 2), - button-color: getCssVar(color, primary), - add-border: 2px dashed getCssVar(color, border), + // Color + color-text: getCssVar(color, text, 0), + color-bg: getCssVar(color, bg, 0), + color-item-active: getCssVar(color, primary, light, default), + color-group-bg: getCssVar(color, bg, 0), + color-group-text: getCssVar(color, text, 1), + // Spacing + spacing-item-padding: getCssVar(spacing, base-tight) getCssVar(spacing, base), + spacing-padding: getCssVar(spacing, tight) getCssVar(spacing, base), + spacing-item-gap: getCssVar(spacing, base, tight), + spacing-group-padding: getCssVar(spacing, tight) getCssVar(spacing, base), + // Font + font-group-fontSize: getCssVar(font-size, header-6), + font-group-lineHeight: getCssVar(height-control, default), + // Other + shadow: getCssVar(shadow, elevated), ); @include b(control-dataview) { @@ -32,73 +26,79 @@ $control-dataview: ( @include e(content-container) { height: 100%; width: 100%; - padding: getCssVar(control-dataview, padding); - overflow-y: auto; } @include e(content) { display: flex; flex-wrap: wrap; - height: 100%; - gap: getCssVar(control-dataview, item-gap); + max-height: 100%; + gap: getCssVar(control-dataview, spacing-item-gap); + overflow-y: auto; + padding: getCssVar(control-dataview, spacing-padding); - @include when(enable-anchor) { - height: 100%; + .van-list__loading,.van-list__finished-text,.van-list__error-text { + width: 100%; } } @include e(row) { gap: 0; + margin: calc(-1 * getCssVar(control-dataview, spacing-item-gap)/ 2); .#{bem(control-dataview, item-col)} { - padding: calc(getCssVar(control-dataview, item-gap) / 2); + padding: calc(getCssVar(control-dataview, spacing-item-gap) / 2); } } - @include when(enable-page) { + @include when(enable-pagination) { display: flex; flex-direction: column; .#{bem(control-dataview, content-container)} { flex: auto; + height: 0; } .#{bem(control-dataview, pagination)} { flex: none; } } - @include when(enable-pagination) { + @include when(hidden-finished) { + // 启用分页时隐藏列表加载完成提示 + .van-list__finished-text { + display: none; + } + } + + @include when(enable-group) { .#{bem(control-dataview, content)} { - height: auto; + padding: 0; + } + .#{bem(control-dataview-group, item)} { + padding: getCssVar(control-dataview, spacing-padding); } } - @include e(load-more) { - padding: getCssVar(control-dataview, button-padding); - .van-button { - width: 100%; - border: none; - background-color: getCssVar(control-dataview, button-bg); - color: getCssVar(control-dataview, button-color); - &:active { - background-color: getCssVar(control-dataview, button-active-bg); - } + @include e(anchor) { + width: 100%; + .van-index-anchor { + padding: 0; } } } @include b(control-dataview-item) { flex: none; - padding: getCssVar(control-dataview, item-padding); - box-shadow: getCssVar(control-dataview, item-shadow); - background-color: getCssVar(control-dataview, item-bg-color); - color: getCssVar(control-dataview, text-color); + padding: getCssVar(control-dataview, spacing-item-padding); + box-shadow: getCssVar(control-dataview, shadow); + color: getCssVar(control-dataview, color-text); + background-color: getCssVar(control-dataview, color-bg); width: 100%; @include when(active) { - background-color: getCssVar(control-dataview, - item-active-color - ); + background-color: getCssVar(control-dataview, color-item-active); } >.van-card { - padding: 0 + padding: 0; + background-color: transparent; + color: inherit; } } @@ -109,77 +109,21 @@ $control-dataview: ( @include e('container') { width: 100%; height: 100%; - padding: getCssVar(control-dataview, group-padding); - overflow-y: auto; } @include e(item) { display: flex; flex-direction: column; - gap: getCssVar(control-dataview, item-gap); + gap: getCssVar(control-dataview, spacing-item-gap); } // 分组标题样式 @include e('caption') { - padding: getCssVar(control-dataview, group-header-padding); - font-size: getCssVar(control-dataview, group-font-size); - color: getCssVar(control-dataview, group-text-color); - background-color: getCssVar(control-dataview, group-bg-color); - } - - // 分组锚点容器样式 - @include e('anchor-container') { - position: absolute; - right: getCssVar(control-dataview, group-anchor-right); - top: 45%; - transform: translateY(-50%); - border-radius: getCssVar(control-dataview, group-anchor-border-radius); - overflow: hidden; - display: flex; - flex-direction: column; - background-color: getCssVar(control-dataview, group-anchor-bg-color); - box-shadow: - getCssVar(control-dataview, box-shadow-inner), - getCssVar(control-dataview, box-shadow-outer); - } - - // 分组锚点项样式 - @include e('anchor-item') { - width: 100%; - text-align: center; - padding: getCssVar(control-dataview, group-anchor-item-padding); - - @include when(active) { - color: getCssVar(control-dataview, active-text-color); - background-color: getCssVar(control-dataview, active-bg-color); - } - } -} - -// 分组锚点容器样式 -@include e('anchor-container') { - position: absolute; - right: getCssVar(control-dataview, group-anchor-right); - top: 45%; - transform: translateY(-50%); - border-radius: getCssVar(control-dataview, group-anchor-border-radius); - overflow: hidden; - display: flex; - flex-direction: column; - background-color: getCssVar(control-dataview, group-anchor-bg-color); - box-shadow: - getCssVar(control-dataview, item-shadow); -} - -// 分组锚点项样式 -@include e('anchor-item') { - width: 100%; - text-align: center; - padding: getCssVar(control-dataview, group-anchor-item-padding); - - @include when(active) { - color: getCssVar(control-dataview, active-text-color); - background-color: getCssVar(control-dataview, active-bg-color); + padding: getCssVar(control-dataview, spacing-group-padding); + font-size: getCssVar(control-dataview, font-group-fontSize); + line-height: getCssVar(control-dataview, font-group-lineHeight); + color: getCssVar(control-dataview, color-group-text); + background-color: getCssVar(control-dataview, color-group-bg); } } diff --git a/src/control/data-view/data-view.tsx b/src/control/data-view/data-view.tsx index 1d28739e80d..3e0cb60b1ed 100644 --- a/src/control/data-view/data-view.tsx +++ b/src/control/data-view/data-view.tsx @@ -1,17 +1,15 @@ import { useControlController, useNamespace } from '@ibiz-template/vue3-util'; -import { computed, defineComponent, PropType, ref, VNode, watch } from 'vue'; +import { defineComponent, PropType, VNode } from 'vue'; import { IDEDataView, ILayoutPanel, IUIActionGroupDetail, } from '@ibiz/model-core'; import { - ControlVO, DataViewControlController, IControlProvider, } from '@ibiz-template/runtime'; -import { createUUID } from 'qx-util'; -import { usePagination } from '../../util'; +import { useListRender, usePagination } from '../../util'; import './data-view.scss'; export const DataViewControl = defineComponent({ @@ -58,106 +56,16 @@ export const DataViewControl = defineComponent({ (...args) => new DataViewControlController(...args), ); const ns = useNamespace(`control-${c.model.controlType!.toLowerCase()}`); - - const isUpdating = ref(false); - - const scrollContainer = ref(); - - // 是否可以加载更多 - const isLodeMoreDisabled = computed(() => { - if (c.model.enablePagingBar === true) { - return true; - } - if (c.model.pagingMode !== 2) { - return true; - } - return ( - c.state.items.length >= c.state.total || - c.state.isLoading || - c.state.total <= c.state.size - ); - }); - - const scrollKey = createUUID(); - const selectScrollKey = ref(); - - // 处理分组锚点项点击 - const handleGroupAnchorClick = (_id: string) => { - // 获取目标元素和滚动容器 - const targetElement = document.getElementById(_id); - const el = scrollContainer.value; - - if (targetElement && el) { - const targetTop = targetElement.offsetTop; - const containerTop = el.offsetTop; - const relativePosition = targetTop - containerTop; - - // 基于滚动容器进行滚动 - el.scrollTo({ - top: relativePosition, - behavior: 'smooth', - }); - selectScrollKey.value = _id; - } - }; - - // 本地数据模式 - const initSimpleData = (): void => { - if (!props.data) { - return; - } - c.state.items = (props.data as IData[]).map(item => new ControlVO(item)); - c.afterLoad({}, c.state.items); - }; - - // 添加动画帧,反正加载多次 - c.evt.on('onLoadSuccess', () => { - isUpdating.value = true; - window.requestAnimationFrame(() => { - isUpdating.value = false; - }); - selectScrollKey.value = ''; - }); - - c.evt.on('onCreated', async () => { - if (props.isSimple) { - initSimpleData(); - c.state.isSimple = true; - c.state.isLoaded = true; - } - }); - - watch( - () => props.data, - () => { - if (props.isSimple) { - initSimpleData(); - } - }, - { - deep: true, - }, - ); + const { + enableLoadMore, + renderNoData, + renderAddItem, + renderScrollList, + renderGroup, + } = useListRender(props, c, ns); const { onPageChange } = usePagination(c); - // 是否显示数据伸缩图标 - // 如果未开启分组,并且加载模式为【加载更多】,并且已经加载过一次更多,则为 true - const showCollapseOrExpandIcon = computed(() => { - return !c.model.enableGroup && c.model.pagingMode === 3; - }); - - const renderAddBtn = (group?: IData) => { - if (!c.enableNew) { - return; - } - return ( - c.onClickNew(event, group?.key)} - > - ); - }; - // 绘制项布局面板 const renderPanelItem = (item: IData, modelData: ILayoutPanel): VNode => { const { context, params } = c; @@ -198,35 +106,6 @@ export const DataViewControl = defineComponent({ ); }; - const renderNoData = (): VNode | undefined => { - // 未加载不显示无数据 - const { isLoaded } = c.state; - if (!isLoaded) { - return; - } - const ctrlModel = c.model.controls?.find(item => { - return item.name === `${c.model.name!}_quicktoolbar`; - }); - if (ctrlModel) { - return ( - - ); - } - return ( - isLoaded && ( - - ) - ); - }; - const renderDefaultItem = (item: IData) => { return ( @@ -265,10 +144,9 @@ export const DataViewControl = defineComponent({ Object.assign( cardStyle, ns.cssVarBlock({ - 'item-bg-color': `${item.bgcolor || ''}`, - 'item-font-color': `${item.fontcolor || ''}`, - 'item-hover-color': `${item.hovercolor || ''}`, - 'item-active-color': `${item.activecolor || ''}`, + 'color-bg': `${item.bgcolor || ''}`, + 'color-text': `${item.fontcolor || ''}`, + 'color-item-active': `${item.activecolor || ''}`, }), ); return ( @@ -282,13 +160,10 @@ export const DataViewControl = defineComponent({ ); }; - const renderContent = (items: IData[], group?: IData) => { - if (!items.length) { - return renderNoData(); - } + const renderContent = (items: IData[]) => { const { cardColMD } = c.model; if (cardColMD) { - return [ + return ( {items.map(item => { return ( @@ -297,110 +172,40 @@ export const DataViewControl = defineComponent({ ); })} - , - renderAddBtn(group), - ]; + + ); } return [ ...items.map(item => { return renderCard(item); }), - renderAddBtn(group), ]; }; - const renderGroup = () => { - const showGroupAnchor = c.state.groups.length > 1 && c.showGroupAnchor; - return [ -
-
- {c.state.groups.map((group, index) => { - const _id = `group-${scrollKey}-${index}`; - return [ -
- {group.caption} -
, -
- {renderContent(group.children, group)} -
, - ]; - })} -
- {showGroupAnchor ? ( -
- {c.state.groups.map((group, index) => { - const _id = `group-${scrollKey}-${index}`; - return ( -
handleGroupAnchorClick(_id)} - class={[ - ns.be('group', 'anchor-item'), - ns.is('active', selectScrollKey.value === _id), - ]} - > - {group.caption} -
- ); - })} -
- ) : null} -
, - ]; + const renderDefault = () => { + const result = []; + result.push(renderContent(c.state.items)); + if (c.enableNew) { + result.push(renderAddItem()); + } + return result; }; - // 绘制卡片内容 + // 绘制列表内容 const renderMDContent = () => { - const showGroupAnchor = c.state.groups.length > 1 && c.showGroupAnchor; - return ( - c.loadMore()} - > - {c.enableGroup ? renderGroup() : renderContent(c.state.items)} - - ); - }; - - // 加载更多 - const loadMoreIcon = () => { - return ( -
- c.loadMore()}> - {ibiz.i18n.t('control.common.loadMore')} - -
- ); - }; - - // 分页模式为点击加载时并且当前数量小于总数 - const renderLoadMore = () => { - let icon = null; - const loadMore = - c.state.items.length < c.state.total && c.state.total > c.state.size; - if (showCollapseOrExpandIcon.value && loadMore) { - icon = loadMoreIcon(); - } - return icon; + const slots = c.enableGroup + ? renderGroup({ children: renderContent }) + : renderDefault(); + return renderScrollList(slots); }; return { c, ns, - scrollContainer, - showCollapseOrExpandIcon, + enableLoadMore, onPageChange, renderNoData, renderMDContent, - renderLoadMore, }; }, render() { @@ -409,17 +214,20 @@ export const DataViewControl = defineComponent({ return (
- {this.c.state.isCreated && this.renderMDContent()} - {this.renderLoadMore()} + {this.c.state.isCreated && + (this.c.state.items.length > 0 + ? this.renderMDContent() + : this.renderNoData())}
{enablePagingBar ? ( new MDCtrlController(...args)); const ns = useNamespace(`control-${c.model.controlType!.toLowerCase()}`); - const { renderItem, renderNoData, renderLoadMore, renderAddItem } = - useListRender(props, c, ns); - - const listRef = ref(); - - const isUpdating = ref(false); - - // 不分页 0 分页栏 1 滚动加载 2 加载更多 3 - // 是否可以加载更多 - const isLodeMoreDisabled = computed(() => { - if (c.model.enablePagingBar === true) { - return true; - } - if (c.model.pagingMode !== 2) { - return true; - } - return ( - c.state.items.length >= c.state.total || - c.state.isLoading || - c.state.total <= c.state.size - ); - }); - - // 本地数据模式 - const initSimpleData = (): void => { - if (!props.data) { - return; - } - c.state.items = (props.data as IData[]).map(item => new ControlVO(item)); - c.afterLoad({}, c.state.items as ControlVO[]); - }; - - c.evt.on('onCreated', async () => { - if (props.isSimple) { - initSimpleData(); - c.state.isSimple = true; - c.state.isLoaded = true; - } - }); - - watch( - () => props.data, - () => { - if (props.isSimple) { - initSimpleData(); - } - }, - { - deep: true, - }, - ); + const { + enableLoadMore, + renderItem, + renderNoData, + renderAddItem, + renderScrollList, + renderGroup, + } = useListRender(props, c, ns); const { onPageChange } = usePagination(c); - // 排序值 - const sortVal = computed(() => { - if (c.state.sortQuery) { - const [key, order] = c.state.sortQuery.split(','); - return { key, order }; - } - return null; - }); - - // 处理排序配置回调 - const onSortChange = (sort: { key: string; order: 'asc' | 'desc' }) => { - c.setSort(sort.key, sort.order); - c.load({ isInitialLoad: true }); - }; - - // 加载更多 - const debounceLoadMore = debounce(async () => { - c.loadMore(); - }, 500); - - const scrollKey = createUUID(); - const selectScrollKey = ref(); - - // 处理分组锚点项点击 - const handleGroupAnchorClick = (_id: string) => { - // 获取目标元素和滚动容器 - const targetElement = document.getElementById(_id); - const listDom = listRef.value?.$el; - - if (targetElement && listDom) { - const targetTop = targetElement.offsetTop; - const containerTop = listDom.offsetTop; - const relativePosition = targetTop - containerTop; - - // 基于滚动容器进行滚动 - listDom.scrollTo({ - top: relativePosition, - behavior: 'smooth', - }); - selectScrollKey.value = _id; - } - }; - - const onLoadMore = () => { - debounceLoadMore(); - }; - - // 添加动画帧,反正加载多次 - c.evt.on('onLoadSuccess', () => { - isUpdating.value = true; - window.requestAnimationFrame(() => { - isUpdating.value = false; - }); - selectScrollKey.value = ''; - }); - - // 是否显示数据伸缩图标 - // 如果未开启分组,并且加载模式为【加载更多】,并且已经加载过一次更多,则为 true - const showCollapseOrExpandIcon = computed(() => { - return !c.model.enableGroup && c.model.pagingMode === 3; - }); - // 左滑界面行为组 const leftSlidingActionGroup = c.model.deuiactionGroup; // 右滑界面行为组 @@ -254,7 +145,7 @@ export const MDCtrlControl = defineComponent({ const renderDefault = () => { const result = []; result.push( - c.state.items.map((item: IData) => { + ...c.state.items.map((item: IData) => { return renderDefaultItem(item); }), ); @@ -264,91 +155,44 @@ export const MDCtrlControl = defineComponent({ return result; }; - const renderGroup = () => { - const showGroupAnchor = c.state.groups.length > 1 && c.showGroupAnchor; - return [ -
-
- {c.state.groups.map((group, index) => { - const _id = `group-${scrollKey}-${index}`; - return ( -
-
{group.caption}
- {group.children.map(item => { - return renderDefaultItem(item.data); - })} - {c.enableNew ? renderAddItem(group) : null} -
- ); - })} -
- {showGroupAnchor ? ( -
- {c.state.groups.map((group, index) => { - const _id = `group-${scrollKey}-${index}`; - return ( -
handleGroupAnchorClick(_id)} - class={[ - ns.be('group', 'anchor-item'), - ns.is('active', selectScrollKey.value === _id), - ]} - > - {group.caption} -
- ); - })} -
- ) : null} -
, - ]; + const renderGroupChildren = (children: IData[]) => { + return children.map(item => { + return renderDefaultItem(item.data); + }); }; // 绘制列表内容 const renderMDContent = () => { - return ( - onLoadMore()} - > - {c.enableGroup ? renderGroup() : renderDefault()} - - ); + const slots = c.enableGroup + ? renderGroup({ children: renderGroupChildren }) + : renderDefault(); + return renderScrollList(slots); }; return { c, ns, - listRef, + enableLoadMore, renderMDContent, renderNoData, - showCollapseOrExpandIcon, onPageChange, - renderLoadMore, - sortVal, - onSortChange, }; }, render() { - const enablePagingBar = - this.c.model.enablePagingBar && this.c.model.pagingMode === 1; + const enablePagingBar = this.c.model.enablePagingBar; return ( {this.c.state.isCreated && @@ -366,7 +210,6 @@ export const MDCtrlControl = defineComponent({ onChange={this.onPageChange} >
) : null} - {this.showCollapseOrExpandIcon && this.renderLoadMore()}
); }, -- Gitee