feat(mall): 支持装修顶部导航栏滚动显示

- 同步 web-antd、web-antdv-next、web-ele 顶部导航栏配置
- 新增 showType 并兼容历史 alwaysShow
- 更新属性面板为显示方式枚举
This commit is contained in:
YunaiV
2026-06-26 22:20:50 -07:00
parent 33a7d46670
commit c24757e5e8
9 changed files with 229 additions and 40 deletions

View File

@@ -1,13 +1,35 @@
import type { Rect } from '../../../../magic-cube-editor/util';
import type { DiyComponent } from '../../../util';
export const NAVIGATION_BAR_SHOW_TYPES = ['always', 'scroll'] as const;
export type NavigationBarShowType = (typeof NAVIGATION_BAR_SHOW_TYPES)[number];
export const isNavigationBarShowType = (
showType: unknown,
): showType is NavigationBarShowType =>
NAVIGATION_BAR_SHOW_TYPES.includes(showType as NavigationBarShowType);
const NAVIGATION_BAR_SCROLL_SHOW_VALUES = new Set<unknown>([
0,
'0',
false,
'false',
]);
export const isNavigationBarAlwaysShow = (
property: Pick<NavigationBarProperty, 'alwaysShow' | 'showType'>,
) => {
if (isNavigationBarShowType(property.showType)) {
return property.showType === 'always';
}
return !NAVIGATION_BAR_SCROLL_SHOW_VALUES.has(property.alwaysShow);
};
/** 顶部导航栏属性 */
export interface NavigationBarProperty {
bgType: 'color' | 'img'; // 背景类型
bgColor: string; // 背景颜色
bgImg: string; // 图片链接
styleType: 'inner' | 'normal'; // 样式类型:默认 | 沉浸式
alwaysShow: boolean; // 常驻显示
showType: NavigationBarShowType; // 显示方式:常驻显示 | 滚动显示
alwaysShow?: boolean | number | string; // 兼容旧版移动端:是否常驻显示
mpCells: NavigationBarCellProperty[]; // 小程序单元格列表
otherCells: NavigationBarCellProperty[]; // 其它平台单元格列表
_local: {
@@ -40,6 +62,7 @@ export const component = {
bgColor: '#fff',
bgImg: '',
styleType: 'normal',
showType: 'always',
alwaysShow: true,
mpCells: [
{

View File

@@ -1,6 +1,8 @@
<script setup lang="ts">
import type { NavigationBarProperty } from './config';
import { watch } from 'vue';
import { useVModel } from '@vueuse/core';
import {
Card,
@@ -16,6 +18,7 @@ import UploadImg from '#/components/upload/image-upload.vue';
import { ColorInput } from '#/views/mall/promotion/components';
import NavigationBarCellProperty from './components/cell-property.vue';
import { isNavigationBarAlwaysShow, isNavigationBarShowType } from './config';
/** 导航栏属性面板 */
defineOptions({ name: 'NavigationBarProperty' });
@@ -29,9 +32,23 @@ const rules: Record<string, any> = {
}; // 表单校验
const formData = useVModel(props, 'modelValue', emit);
if (!isNavigationBarShowType(formData.value.showType)) {
formData.value.showType = isNavigationBarAlwaysShow(formData.value)
? 'always'
: 'scroll';
}
formData.value.alwaysShow = formData.value.showType === 'always';
if (!formData.value._local) {
formData.value._local = { previewMp: true, previewOther: false };
}
watch(
() => formData.value.showType,
(showType) => {
formData.value.alwaysShow = showType === 'always';
},
{ immediate: true },
);
</script>
<template>
@@ -53,17 +70,16 @@ if (!formData.value._local) {
</RadioGroup>
</FormItem>
<FormItem
label="常驻显示"
name="alwaysShow"
label="显示方式"
name="showType"
v-if="formData.styleType === 'inner'"
>
<RadioGroup v-model:value="formData!.alwaysShow">
<Radio :value="false">关闭</Radio>
<Tooltip
title="常驻显示关闭后,头部小组件将在页面滑动时淡入"
placement="top"
>
<Radio :value="true">开启</Radio>
<RadioGroup v-model:value="formData!.showType">
<Tooltip title="头部导航栏固定显示" placement="top">
<Radio value="always">常驻显示</Radio>
</Tooltip>
<Tooltip title="头部导航栏将在页面滑动时淡入" placement="top">
<Radio value="scroll">滚动显示</Radio>
</Tooltip>
</RadioGroup>
</FormItem>

View File

@@ -1,4 +1,5 @@
<script lang="ts" setup>
import type { NavigationBarProperty } from './components/mobile/navigation-bar/config';
import type { DiyComponent, DiyComponentLibrary, PageConfig } from './util';
import { onMounted, ref, unref, watch } from 'vue';
@@ -15,7 +16,11 @@ import statusBarImg from '#/assets/imgs/diy/statusBar.png';
import ComponentContainer from './components/component-container.vue';
import ComponentLibrary from './components/component-library.vue';
import { componentConfigs, components } from './components/mobile';
import { component as NAVIGATION_BAR_COMPONENT } from './components/mobile/navigation-bar/config';
import {
isNavigationBarAlwaysShow,
isNavigationBarShowType,
component as NAVIGATION_BAR_COMPONENT,
} from './components/mobile/navigation-bar/config';
import { component as PAGE_CONFIG_COMPONENT } from './components/mobile/page-config/config';
import { component as TAB_BAR_COMPONENT } from './components/mobile/tab-bar/config';
@@ -56,6 +61,21 @@ const selectedComponent = ref<DiyComponent<any>>(); // 选中的组件,默认
const selectedComponentIndex = ref<number>(-1); // 选中的组件索引
const pageComponents = ref<DiyComponent<any>[]>([]); // 组件列表
// 兼容历史装修数据:旧版只有 alwaysShow新增 showType 后统一转成枚举并继续输出 alwaysShow。
function normalizeNavigationBarProperty(property: NavigationBarProperty) {
const navigationBarProperty = cloneDeep(property);
if (!isNavigationBarShowType(navigationBarProperty.showType)) {
navigationBarProperty.showType = isNavigationBarAlwaysShow(
navigationBarProperty,
)
? 'always'
: 'scroll';
}
navigationBarProperty.alwaysShow =
navigationBarProperty.showType === 'always';
return navigationBarProperty;
}
/**
* 监听传入的页面配置
* 解析出 pageConfigComponent 页面整体的配置navigationBarComponent、pageComponents、tabBarComponent 页面上、中、下的配置
@@ -72,9 +92,10 @@ watch(
(typeof modelValue !== 'string' && modelValue?.page) ||
PAGE_CONFIG_COMPONENT.property;
// noinspection SuspiciousTypeOfGuard
navigationBarComponent.value.property =
navigationBarComponent.value.property = normalizeNavigationBarProperty(
(typeof modelValue !== 'string' && modelValue?.navigationBar) ||
NAVIGATION_BAR_COMPONENT.property;
NAVIGATION_BAR_COMPONENT.property,
);
// noinspection SuspiciousTypeOfGuard
tabBarComponent.value.property =
(typeof modelValue !== 'string' && modelValue?.tabBar) ||

View File

@@ -1,13 +1,35 @@
import type { Rect } from '../../../../magic-cube-editor/util';
import type { DiyComponent } from '../../../util';
export const NAVIGATION_BAR_SHOW_TYPES = ['always', 'scroll'] as const;
export type NavigationBarShowType = (typeof NAVIGATION_BAR_SHOW_TYPES)[number];
export const isNavigationBarShowType = (
showType: unknown,
): showType is NavigationBarShowType =>
NAVIGATION_BAR_SHOW_TYPES.includes(showType as NavigationBarShowType);
const NAVIGATION_BAR_SCROLL_SHOW_VALUES = new Set<unknown>([
0,
'0',
false,
'false',
]);
export const isNavigationBarAlwaysShow = (
property: Pick<NavigationBarProperty, 'alwaysShow' | 'showType'>,
) => {
if (isNavigationBarShowType(property.showType)) {
return property.showType === 'always';
}
return !NAVIGATION_BAR_SCROLL_SHOW_VALUES.has(property.alwaysShow);
};
/** 顶部导航栏属性 */
export interface NavigationBarProperty {
bgType: 'color' | 'img'; // 背景类型
bgColor: string; // 背景颜色
bgImg: string; // 图片链接
styleType: 'inner' | 'normal'; // 样式类型:默认 | 沉浸式
alwaysShow: boolean; // 常驻显示
showType: NavigationBarShowType; // 显示方式:常驻显示 | 滚动显示
alwaysShow?: boolean | number | string; // 兼容旧版移动端:是否常驻显示
mpCells: NavigationBarCellProperty[]; // 小程序单元格列表
otherCells: NavigationBarCellProperty[]; // 其它平台单元格列表
_local: {
@@ -40,6 +62,7 @@ export const component = {
bgColor: '#fff',
bgImg: '',
styleType: 'normal',
showType: 'always',
alwaysShow: true,
mpCells: [
{

View File

@@ -1,6 +1,8 @@
<script setup lang="ts">
import type { NavigationBarProperty } from './config';
import { watch } from 'vue';
import { useVModel } from '@vueuse/core';
import {
Card,
@@ -16,6 +18,7 @@ import UploadImg from '#/components/upload/image-upload.vue';
import { ColorInput } from '#/views/mall/promotion/components';
import NavigationBarCellProperty from './components/cell-property.vue';
import { isNavigationBarAlwaysShow, isNavigationBarShowType } from './config';
/** 导航栏属性面板 */
defineOptions({ name: 'NavigationBarProperty' });
@@ -29,9 +32,23 @@ const rules: Record<string, any> = {
}; // 表单校验
const formData = useVModel(props, 'modelValue', emit);
if (!isNavigationBarShowType(formData.value.showType)) {
formData.value.showType = isNavigationBarAlwaysShow(formData.value)
? 'always'
: 'scroll';
}
formData.value.alwaysShow = formData.value.showType === 'always';
if (!formData.value._local) {
formData.value._local = { previewMp: true, previewOther: false };
}
watch(
() => formData.value.showType,
(showType) => {
formData.value.alwaysShow = showType === 'always';
},
{ immediate: true },
);
</script>
<template>
@@ -53,17 +70,16 @@ if (!formData.value._local) {
</RadioGroup>
</FormItem>
<FormItem
label="常驻显示"
name="alwaysShow"
label="显示方式"
name="showType"
v-if="formData.styleType === 'inner'"
>
<RadioGroup v-model:value="formData!.alwaysShow">
<Radio :value="false">关闭</Radio>
<Tooltip
title="常驻显示关闭后,头部小组件将在页面滑动时淡入"
placement="top"
>
<Radio :value="true">开启</Radio>
<RadioGroup v-model:value="formData!.showType">
<Tooltip title="头部导航栏固定显示" placement="top">
<Radio value="always">常驻显示</Radio>
</Tooltip>
<Tooltip title="头部导航栏将在页面滑动时淡入" placement="top">
<Radio value="scroll">滚动显示</Radio>
</Tooltip>
</RadioGroup>
</FormItem>

View File

@@ -1,4 +1,5 @@
<script lang="ts" setup>
import type { NavigationBarProperty } from './components/mobile/navigation-bar/config';
import type { DiyComponent, DiyComponentLibrary, PageConfig } from './util';
import { onMounted, ref, unref, watch } from 'vue';
@@ -7,7 +8,16 @@ import { Page, useVbenModal } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { cloneDeep, isEmpty, isString } from '@vben/utils';
import { Button, Card, Col, QRCode, Row, Space, Tag, Tooltip } from 'antdv-next';
import {
Button,
Card,
Col,
QRCode,
Row,
Space,
Tag,
Tooltip,
} from 'antdv-next';
import draggable from 'vuedraggable';
import statusBarImg from '#/assets/imgs/diy/statusBar.png';
@@ -15,7 +25,11 @@ import statusBarImg from '#/assets/imgs/diy/statusBar.png';
import ComponentContainer from './components/component-container.vue';
import ComponentLibrary from './components/component-library.vue';
import { componentConfigs, components } from './components/mobile';
import { component as NAVIGATION_BAR_COMPONENT } from './components/mobile/navigation-bar/config';
import {
isNavigationBarAlwaysShow,
isNavigationBarShowType,
component as NAVIGATION_BAR_COMPONENT,
} from './components/mobile/navigation-bar/config';
import { component as PAGE_CONFIG_COMPONENT } from './components/mobile/page-config/config';
import { component as TAB_BAR_COMPONENT } from './components/mobile/tab-bar/config';
@@ -56,6 +70,21 @@ const selectedComponent = ref<DiyComponent<any>>(); // 选中的组件,默认
const selectedComponentIndex = ref<number>(-1); // 选中的组件索引
const pageComponents = ref<DiyComponent<any>[]>([]); // 组件列表
// 兼容历史装修数据:旧版只有 alwaysShow新增 showType 后统一转成枚举并继续输出 alwaysShow。
function normalizeNavigationBarProperty(property: NavigationBarProperty) {
const navigationBarProperty = cloneDeep(property);
if (!isNavigationBarShowType(navigationBarProperty.showType)) {
navigationBarProperty.showType = isNavigationBarAlwaysShow(
navigationBarProperty,
)
? 'always'
: 'scroll';
}
navigationBarProperty.alwaysShow =
navigationBarProperty.showType === 'always';
return navigationBarProperty;
}
/**
* 监听传入的页面配置
* 解析出 pageConfigComponent 页面整体的配置navigationBarComponent、pageComponents、tabBarComponent 页面上、中、下的配置
@@ -72,9 +101,10 @@ watch(
(typeof modelValue !== 'string' && modelValue?.page) ||
PAGE_CONFIG_COMPONENT.property;
// noinspection SuspiciousTypeOfGuard
navigationBarComponent.value.property =
navigationBarComponent.value.property = normalizeNavigationBarProperty(
(typeof modelValue !== 'string' && modelValue?.navigationBar) ||
NAVIGATION_BAR_COMPONENT.property;
NAVIGATION_BAR_COMPONENT.property,
);
// noinspection SuspiciousTypeOfGuard
tabBarComponent.value.property =
(typeof modelValue !== 'string' && modelValue?.tabBar) ||

View File

@@ -1,13 +1,35 @@
import type { Rect } from '../../../../magic-cube-editor/util';
import type { DiyComponent } from '../../../util';
export const NAVIGATION_BAR_SHOW_TYPES = ['always', 'scroll'] as const;
export type NavigationBarShowType = (typeof NAVIGATION_BAR_SHOW_TYPES)[number];
export const isNavigationBarShowType = (
showType: unknown,
): showType is NavigationBarShowType =>
NAVIGATION_BAR_SHOW_TYPES.includes(showType as NavigationBarShowType);
const NAVIGATION_BAR_SCROLL_SHOW_VALUES = new Set<unknown>([
0,
'0',
false,
'false',
]);
export const isNavigationBarAlwaysShow = (
property: Pick<NavigationBarProperty, 'alwaysShow' | 'showType'>,
) => {
if (isNavigationBarShowType(property.showType)) {
return property.showType === 'always';
}
return !NAVIGATION_BAR_SCROLL_SHOW_VALUES.has(property.alwaysShow);
};
/** 顶部导航栏属性 */
export interface NavigationBarProperty {
bgType: 'color' | 'img'; // 背景类型
bgColor: string; // 背景颜色
bgImg: string; // 图片链接
styleType: 'inner' | 'normal'; // 样式类型:默认 | 沉浸式
alwaysShow: boolean; // 常驻显示
showType: NavigationBarShowType; // 显示方式:常驻显示 | 滚动显示
alwaysShow?: boolean | number | string; // 兼容旧版移动端:是否常驻显示
mpCells: NavigationBarCellProperty[]; // 小程序单元格列表
otherCells: NavigationBarCellProperty[]; // 其它平台单元格列表
_local: {
@@ -40,6 +62,7 @@ export const component = {
bgColor: '#fff',
bgImg: '',
styleType: 'normal',
showType: 'always',
alwaysShow: true,
mpCells: [
{

View File

@@ -1,6 +1,8 @@
<script setup lang="ts">
import type { NavigationBarProperty } from './config';
import { watch } from 'vue';
import { useVModel } from '@vueuse/core';
import {
ElCard,
@@ -16,6 +18,7 @@ import UploadImg from '#/components/upload/image-upload.vue';
import { ColorInput } from '#/views/mall/promotion/components';
import NavigationBarCellProperty from './components/cell-property.vue';
import { isNavigationBarAlwaysShow, isNavigationBarShowType } from './config';
/** 导航栏属性面板 */
defineOptions({ name: 'NavigationBarProperty' });
@@ -29,9 +32,23 @@ const rules = {
}; // 表单校验
const formData = useVModel(props, 'modelValue', emit);
if (!isNavigationBarShowType(formData.value.showType)) {
formData.value.showType = isNavigationBarAlwaysShow(formData.value)
? 'always'
: 'scroll';
}
formData.value.alwaysShow = formData.value.showType === 'always';
if (!formData.value._local) {
formData.value._local = { previewMp: true, previewOther: false };
}
watch(
() => formData.value.showType,
(showType) => {
formData.value.alwaysShow = showType === 'always';
},
{ immediate: true },
);
</script>
<template>
@@ -48,17 +65,16 @@ if (!formData.value._local) {
</ElRadioGroup>
</ElFormItem>
<ElFormItem
label="常驻显示"
prop="alwaysShow"
label="显示方式"
prop="showType"
v-if="formData.styleType === 'inner'"
>
<ElRadioGroup v-model="formData!.alwaysShow">
<ElRadio :value="false">关闭</ElRadio>
<ElTooltip
content="常驻显示关闭后,头部小组件将在页面滑动时淡入"
placement="top"
>
<ElRadio :value="true">开启</ElRadio>
<ElRadioGroup v-model="formData!.showType">
<ElTooltip content="头部导航栏固定显示" placement="top">
<ElRadio value="always">常驻显示</ElRadio>
</ElTooltip>
<ElTooltip content="头部导航栏将在页面滑动时淡入" placement="top">
<ElRadio value="scroll">滚动显示</ElRadio>
</ElTooltip>
</ElRadioGroup>
</ElFormItem>

View File

@@ -1,4 +1,5 @@
<script lang="ts" setup>
import type { NavigationBarProperty } from './components/mobile/navigation-bar/config';
import type { DiyComponent, DiyComponentLibrary, PageConfig } from './util';
import { onMounted, ref, unref, watch } from 'vue';
@@ -27,7 +28,11 @@ import statusBarImg from '#/assets/imgs/diy/statusBar.png';
import ComponentContainer from './components/component-container.vue';
import ComponentLibrary from './components/component-library.vue';
import { componentConfigs, components } from './components/mobile';
import { component as NAVIGATION_BAR_COMPONENT } from './components/mobile/navigation-bar/config';
import {
isNavigationBarAlwaysShow,
isNavigationBarShowType,
component as NAVIGATION_BAR_COMPONENT,
} from './components/mobile/navigation-bar/config';
import { component as PAGE_CONFIG_COMPONENT } from './components/mobile/page-config/config';
import { component as TAB_BAR_COMPONENT } from './components/mobile/tab-bar/config';
@@ -67,6 +72,21 @@ const selectedComponent = ref<DiyComponent<any>>(); // 选中的组件,默认
const selectedComponentIndex = ref<number>(-1); // 选中的组件索引
const pageComponents = ref<DiyComponent<any>[]>([]); // 组件列表
// 兼容历史装修数据:旧版只有 alwaysShow新增 showType 后统一转成枚举并继续输出 alwaysShow。
function normalizeNavigationBarProperty(property: NavigationBarProperty) {
const navigationBarProperty = cloneDeep(property);
if (!isNavigationBarShowType(navigationBarProperty.showType)) {
navigationBarProperty.showType = isNavigationBarAlwaysShow(
navigationBarProperty,
)
? 'always'
: 'scroll';
}
navigationBarProperty.alwaysShow =
navigationBarProperty.showType === 'always';
return navigationBarProperty;
}
/**
* 监听传入的页面配置
* 解析出 pageConfigComponent 页面整体的配置navigationBarComponent、pageComponents、tabBarComponent 页面上、中、下的配置
@@ -83,9 +103,10 @@ watch(
(typeof modelValue !== 'string' && modelValue?.page) ||
PAGE_CONFIG_COMPONENT.property;
// noinspection SuspiciousTypeOfGuard
navigationBarComponent.value.property =
navigationBarComponent.value.property = normalizeNavigationBarProperty(
(typeof modelValue !== 'string' && modelValue?.navigationBar) ||
NAVIGATION_BAR_COMPONENT.property;
NAVIGATION_BAR_COMPONENT.property,
);
// noinspection SuspiciousTypeOfGuard
tabBarComponent.value.property =
(typeof modelValue !== 'string' && modelValue?.tabBar) ||