From 8fcb2590482288e975f754212025cff6baa047cc Mon Sep 17 00:00:00 2001
From: ShineKOT <1917095344@qq.com>
Date: Thu, 6 Feb 2025 19:58:39 +0800
Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=20=E6=96=B0=E5=A2=9E=E8=8F=9C?=
=?UTF-8?q?=E5=8D=95=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
CHANGELOG.md | 4 +
ibiz.config.ts | 1 +
package.json | 2 +
pnpm-lock.yaml | 3 +
src/control/app-menu/app-menu.scss | 1 +
src/control/app-menu/app-menu.tsx | 77 ++++---
.../custom-menu-design.scss | 75 +++++++
.../custom-menu-design/custom-menu-design.tsx | 210 ++++++++++++++++++
src/control/app-menu/index.ts | 4 +-
9 files changed, 345 insertions(+), 32 deletions(-)
create mode 100644 src/control/app-menu/custom-menu-design/custom-menu-design.scss
create mode 100644 src/control/app-menu/custom-menu-design/custom-menu-design.tsx
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 00830bb27b..6e5035b1ec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@
## [Unreleased]
+### Added
+
+- 新增菜单自定义功能
+
## [0.0.50] - 2025-01-23
### Added
diff --git a/ibiz.config.ts b/ibiz.config.ts
index 297ca5f1bf..4a59806244 100644
--- a/ibiz.config.ts
+++ b/ibiz.config.ts
@@ -38,6 +38,7 @@ export default defineConfig({
'ramda',
'lodash-es',
'qx-util',
+ 'vuedraggable',
'pinia',
'cherry-markdown',
'@ibiz-template/mob-theme',
diff --git a/package.json b/package.json
index 6d99c16d3e..c7a212dfe5 100644
--- a/package.json
+++ b/package.json
@@ -51,6 +51,7 @@
"vue-i18n": "^9.6.5",
"vue-router": "^4.2.5",
"qr-code-styling": "^1.8.3",
+ "vuedraggable": "^4.1.0",
"vue-qrcode-reader": "5.5.11"
},
"devDependencies": {
@@ -85,6 +86,7 @@
"ramda": "^0.29.0",
"vant": "^4.7.2",
"vue": "^3.3.4",
+ "vuedraggable": "^4.1.0",
"vue3-hash-calendar": "^1.1.3",
"vue-router": "^4.2.4"
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e63b87cee5..3cb6062efe 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -92,6 +92,9 @@ dependencies:
vue3-hash-calendar:
specifier: ^1.1.3
version: 1.1.3(vue@3.3.9)
+ vuedraggable:
+ specifier: ^4.1.0
+ version: 4.1.0(vue@3.3.9)
devDependencies:
'@commitlint/cli':
diff --git a/src/control/app-menu/app-menu.scss b/src/control/app-menu/app-menu.scss
index ebd2271d9d..0307f6dcdc 100644
--- a/src/control/app-menu/app-menu.scss
+++ b/src/control/app-menu/app-menu.scss
@@ -8,6 +8,7 @@
.van-tabbar-item__icon {
width: var(--van-tabbar-item-icon-size);
+ font-size: getCssVar(width, icon, medium);
}
}
diff --git a/src/control/app-menu/app-menu.tsx b/src/control/app-menu/app-menu.tsx
index 87fb389876..172c25a45f 100644
--- a/src/control/app-menu/app-menu.tsx
+++ b/src/control/app-menu/app-menu.tsx
@@ -1,8 +1,9 @@
import { useRoute } from 'vue-router';
-import { defineComponent, onMounted, PropType, ref, watch } from 'vue';
+import { ref, watch, PropType, onMounted, defineComponent } from 'vue';
import { prepareControl, useControlController } from '@ibiz-template/vue3-util';
import { IAppMenu, IAppMenuItem } from '@ibiz/model-core';
import { AppMenuController, IControlProvider } from '@ibiz-template/runtime';
+import { MenuDesign } from './custom-menu-design/custom-menu-design';
import './app-menu.scss';
export const AppMenuControl = defineComponent({
@@ -17,6 +18,7 @@ export const AppMenuControl = defineComponent({
const c = useControlController((...args) => new AppMenuController(...args));
const { controlClass, ns } = prepareControl(c);
const activeName = ref();
+ const showPopup = ref(false);
// 路由对象
const route = useRoute();
// 计算当前路由匹配菜单
@@ -32,11 +34,16 @@ export const AppMenuControl = defineComponent({
});
};
+ const setPopupState = (state: boolean): void => {
+ showPopup.value = state;
+ };
+
const onTabChange = async (
active: string,
event?: MouseEvent,
opts: IData = {},
) => {
+ if (active === 'customized') return setPopupState(true);
activeName.value = active;
await c.onClickMenuItem(active, event, true, opts);
};
@@ -71,40 +78,17 @@ export const AppMenuControl = defineComponent({
);
return {
- controlClass,
- activeName,
c,
- onTabChange,
ns,
+ showPopup,
+ activeName,
+ controlClass,
+ onTabChange,
+ setPopupState,
};
},
render() {
const { model, state } = this.c;
- let appMenu = null;
- if (state.isCreated && model.appMenuItems?.length) {
- appMenu = model.appMenuItems.map(item => {
- if (item.itemType === 'MENUITEM' && item.hidden !== true) {
- const itemState = state.menuItemsState[item.id!];
- if (itemState.visible) {
- return (
-
- {{
- icon: () => (
-
- ),
- default: () => {item.caption},
- }}
-
- );
- }
- }
- return null;
- });
- }
return (
- {appMenu}
+ {state.mobMenuItems.map(item => (
+
+ {{
+ icon: () => (
+
+ ),
+ default: () => {item.caption},
+ }}
+
+ ))}
+ {model.enableCustomized && (
+
+ {{
+ icon: () => ,
+ default: () => 更多,
+ }}
+
+ )}
+
+ this.setPopupState(false)}
+ />
+
);
},
diff --git a/src/control/app-menu/custom-menu-design/custom-menu-design.scss b/src/control/app-menu/custom-menu-design/custom-menu-design.scss
new file mode 100644
index 0000000000..4d5f89a624
--- /dev/null
+++ b/src/control/app-menu/custom-menu-design/custom-menu-design.scss
@@ -0,0 +1,75 @@
+@include b(menu-design) {
+ width: 100%;
+ height: 100%;
+ padding: getCssVar(spacing, base);
+ background-color: getCssVar(color, fill, 2);
+ @include e(header) {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-size: getCssVar(font-size, header, 5);
+ @include m(close-icon) {
+ font-size: getCssVar(font-size, header, 3);
+ }
+ @include m(caption) {
+ font-weight: getCssVar(font-weight, bold);
+ }
+ @include m(save) {
+ color: getCssVar(color, primary);
+ }
+ }
+
+ @include e(group) {
+ @include m(caption) {
+ color: getCssVar(color, text, 2);
+ padding: getCssVar(spacing, base) 0;
+ }
+ }
+
+ @include e(draggable) {
+ padding: getCssVar(spacing, base, tight);
+ border-radius: getCssVar(border-radius, medium);
+ background-color: getCssVar(color, bg, 1);
+
+ @include m(icon) {
+ font-size: getCssVar(font-size, header, 3);
+ &.#{bem(menu-design, draggable, prefix-icon)} {
+ margin-right: getCssVar(spacing, tight);
+ }
+ &.#{bem(menu-design, draggable, remove-icon)} {
+ color: getCssVar(color, danger);
+ }
+ &.#{bem(menu-design, draggable, add-icon)} {
+ color: getCssVar(color, primary);
+ }
+ }
+
+ @include m(item) {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ font-size: getCssVar(width, icon, medium);
+ }
+
+ @include m(item-content) {
+ flex-grow: 1;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: getCssVar(spacing, base, tight) 0;
+ border-bottom: 1px solid getCssVar(color, border);
+ }
+
+ @include m(item-caption) {
+ display: flex;
+ align-items: center;
+ }
+
+ @include m(item-icon) {
+ display: flex;
+ align-items: center;
+ width: var(--van-tabbar-item-icon-size);
+ margin-right: getCssVar(spacing, tight);
+ }
+ }
+}
diff --git a/src/control/app-menu/custom-menu-design/custom-menu-design.tsx b/src/control/app-menu/custom-menu-design/custom-menu-design.tsx
new file mode 100644
index 0000000000..84fec6c8c0
--- /dev/null
+++ b/src/control/app-menu/custom-menu-design/custom-menu-design.tsx
@@ -0,0 +1,210 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
+import { AppMenuController } from '@ibiz-template/runtime';
+import { useNamespace } from '@ibiz-template/vue3-util';
+import { IAppMenuItem } from '@ibiz/model-core';
+import draggable from 'vuedraggable';
+import { defineComponent, PropType, ref, watch } from 'vue';
+import './custom-menu-design.scss';
+
+/**
+ * 处理菜单自定义配置
+ *
+ * @param {AppMenuController} c
+ * @param {IData[]} items
+ * @return {*}
+ */
+export const MenuDesign = defineComponent({
+ name: 'IBizMenuDesign',
+ components: {
+ draggable,
+ },
+ props: {
+ controller: {
+ type: Object as PropType,
+ required: true,
+ },
+ show: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ emits: ['close'],
+ setup(props, { emit }) {
+ const ns = useNamespace('menu-design');
+ const c = props.controller;
+
+ // 底部导航菜单
+ const mobMenuItems = ref([]);
+
+ // 更多导航菜单
+ const moreMenuItems = ref([]);
+
+ const calcMenuItems = () => {
+ const menuItems =
+ c.model.appMenuItems?.filter(
+ item =>
+ item.hidden !== true &&
+ item.itemType === 'MENUITEM' &&
+ c.state.menuItemsState[item.id!].visible,
+ ) || [];
+ mobMenuItems.value = [...c.state.mobMenuItems];
+ moreMenuItems.value = menuItems.filter(
+ menu => !c.state.mobMenuItems.find(_item => menu.id === _item.id),
+ );
+ };
+
+ watch(
+ () => props.show,
+ () => {
+ if (props.show) calcMenuItems();
+ },
+ {
+ immediate: true,
+ },
+ );
+
+ /**
+ * 关闭
+ *
+ */
+ const onClose = () => {
+ emit('close');
+ };
+
+ /**
+ * 保存
+ *
+ */
+ const onSave = async () => {
+ await c.customController!.saveCustomModelData(mobMenuItems.value);
+ c.state.mobMenuItems = mobMenuItems.value;
+ onClose();
+ };
+
+ /**
+ * 处理添加或删除
+ *
+ * @param {('nav' | 'more')} type
+ * @param {number} index
+ */
+ const handleRemoveOrAdd = (type: 'nav' | 'more', index: number) => {
+ if (type === 'nav') {
+ const item = mobMenuItems.value[index];
+ mobMenuItems.value.splice(index, 1);
+ moreMenuItems.value.push(item);
+ } else {
+ const item = moreMenuItems.value[index];
+ moreMenuItems.value.splice(index, 1);
+ mobMenuItems.value.push(item);
+ }
+ };
+
+ /**
+ * 绘制拖拽区
+ *
+ * @param {IAppMenuItem[]} menuItems
+ */
+ const renderDraggable = (
+ type: 'nav' | 'more',
+ menuItems: IAppMenuItem[],
+ ) => {
+ return (
+
+ {{
+ item: ({
+ element,
+ index,
+ }: {
+ element: IAppMenuItem;
+ index: number;
+ }) => {
+ return (
+
+
handleRemoveOrAdd(type, index)}
+ >
+
+
+ );
+ },
+ }}
+
+ );
+ };
+
+ const renderMenuList = (type: 'nav' | 'more') => {
+ return (
+
+
+ {type === 'nav' ? '底部导航' : '更多'}
+
+ {renderDraggable(
+ type,
+ type === 'nav' ? mobMenuItems.value : moreMenuItems.value,
+ )}
+
+ );
+ };
+
+ return {
+ c,
+ ns,
+ onSave,
+ onClose,
+ renderMenuList,
+ };
+ },
+
+ render() {
+ return (
+
+
+
+ {this.renderMenuList('nav')}
+ {this.renderMenuList('more')}
+
+
+ );
+ },
+});
diff --git a/src/control/app-menu/index.ts b/src/control/app-menu/index.ts
index bf9acc2f80..5a9670e7c4 100644
--- a/src/control/app-menu/index.ts
+++ b/src/control/app-menu/index.ts
@@ -1,5 +1,5 @@
-import { ControlType, registerControlProvider } from '@ibiz-template/runtime';
import { App } from 'vue';
+import { ControlType, registerControlProvider } from '@ibiz-template/runtime';
import { withInstall } from '@ibiz-template/vue3-util';
import { AppMenuControl } from './app-menu';
import { AppMenuProvider } from './app-menu.provider';
@@ -9,7 +9,7 @@ export * from './app-menu.provider';
export const IBizAppMenuControl = withInstall(
AppMenuControl,
function (v: App) {
- v.component(AppMenuControl.name, AppMenuControl);
+ v.component(AppMenuControl.name!, AppMenuControl);
registerControlProvider(ControlType.APP_MENU, () => new AppMenuProvider());
},
);
--
Gitee
From d8d22501dd2ba0cf799d398a59e7ab060e4c9871 Mon Sep 17 00:00:00 2001
From: ShineKOT <1917095344@qq.com>
Date: Thu, 6 Feb 2025 20:17:29 +0800
Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=20=E6=9B=B4=E6=96=B0=E5=9B=BD?=
=?UTF-8?q?=E9=99=85=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/control/app-menu/app-menu.tsx | 4 +++-
.../app-menu/custom-menu-design/custom-menu-design.tsx | 10 +++++++---
src/locale/en/index.ts | 6 ++++++
src/locale/zh-CN/index.ts | 6 ++++++
4 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/src/control/app-menu/app-menu.tsx b/src/control/app-menu/app-menu.tsx
index 172c25a45f..88c83bfb40 100644
--- a/src/control/app-menu/app-menu.tsx
+++ b/src/control/app-menu/app-menu.tsx
@@ -116,7 +116,9 @@ export const AppMenuControl = defineComponent({
{{
icon: () => ,
- default: () => 更多,
+ default: () => (
+ {ibiz.i18n.t('control.appmenu.more')}
+ ),
}}
)}
diff --git a/src/control/app-menu/custom-menu-design/custom-menu-design.tsx b/src/control/app-menu/custom-menu-design/custom-menu-design.tsx
index 84fec6c8c0..ae03a3b780 100644
--- a/src/control/app-menu/custom-menu-design/custom-menu-design.tsx
+++ b/src/control/app-menu/custom-menu-design/custom-menu-design.tsx
@@ -167,7 +167,9 @@ export const MenuDesign = defineComponent({
return (
- {type === 'nav' ? '底部导航' : '更多'}
+ {type === 'nav'
+ ? ibiz.i18n.t('control.appmenu.bottomNav')
+ : ibiz.i18n.t('control.appmenu.more')}
{renderDraggable(
type,
@@ -195,9 +197,11 @@ export const MenuDesign = defineComponent({
onClick={this.onClose}
class={this.ns.em('header', 'close-icon')}
>
-
+
diff --git a/src/locale/en/index.ts b/src/locale/en/index.ts
index c2540e7a2f..43301a1b8f 100644
--- a/src/locale/en/index.ts
+++ b/src/locale/en/index.ts
@@ -66,6 +66,12 @@ export default {
common: {
loadMore: 'Load more',
},
+ appmenu: {
+ more: 'More',
+ bottomNav: 'Bottom Navigation',
+ customNav: 'Customize Navigation',
+ save: 'Save',
+ },
dataView: { end: 'The end~' },
form: {
noSupportDetailType:
diff --git a/src/locale/zh-CN/index.ts b/src/locale/zh-CN/index.ts
index ac826e54b4..d3c4b48ca6 100644
--- a/src/locale/zh-CN/index.ts
+++ b/src/locale/zh-CN/index.ts
@@ -62,6 +62,12 @@ export default {
loadMore: '加载更多',
},
dataView: { end: '我已经到底啦~' },
+ appmenu: {
+ more: '更多',
+ bottomNav: '底部导航',
+ customNav: '自定义导航',
+ save: '保存',
+ },
form: {
noSupportDetailType:
'暂未支持的表单项类型: {detailType} 或找不到对应适配器',
--
Gitee