feat:【mall】diy editor 的 navigation-bar 优化

This commit is contained in:
YunaiV
2025-11-01 09:21:26 +08:00
parent 12968a3d66
commit 5e1c7bb54c
4 changed files with 103 additions and 105 deletions

View File

@@ -22,7 +22,7 @@ import {
MagicCubeEditor,
} from '#/views/mall/promotion/components';
// 导航栏属性面板
/** 导航栏单元格属性面板 */
defineOptions({ name: 'NavigationBarCellProperty' });
const props = defineProps({
@@ -35,13 +35,19 @@ const props = defineProps({
default: () => [],
},
});
const emit = defineEmits(['update:modelValue']);
const cellList = useVModel(props, 'modelValue', emit);
// 单元格数量小程序6个右侧胶囊按钮占了2个其它平台8个
/**
* 计算单元格数量
* 1. 小程序6 个(因为右侧有胶囊按钮占据 2 个格子的空间)
* 2. 其它平台8 个(全部空间可用)
*/
const cellCount = computed(() => (props.isMp ? 6 : 8));
// 转换为Rect格式的数据
/** 转换为 Rect 格式的数据MagicCubeEditor 组件需要 Rect 格式的数据来渲染热区 */
const rectList = computed<Rect[]>(() => {
return cellList.value.map((cell) => ({
left: cell.left,
@@ -53,18 +59,19 @@ const rectList = computed<Rect[]>(() => {
}));
});
// 选中的热区
const selectedHotAreaIndex = ref(0);
const handleHotAreaSelected = (
const selectedHotAreaIndex = ref(0); // 选中的热区
/** 处理热区被选中事件 */
function handleHotAreaSelected(
cellValue: NavigationBarCellProperty,
index: number,
) => {
) {
selectedHotAreaIndex.value = index;
if (!cellValue.type) {
cellValue.type = 'text';
cellValue.textColor = '#111111';
}
};
}
</script>
<template>
@@ -141,5 +148,3 @@ const handleHotAreaSelected = (
</template>
</template>
</template>
<style lang="scss" scoped></style>

View File

@@ -2,56 +2,35 @@ import type { DiyComponent } from '../../../util';
/** 顶部导航栏属性 */
export interface NavigationBarProperty {
// 背景类型
bgType: 'color' | 'img';
// 背景颜色
bgColor: string;
// 图片链接
bgImg: string;
// 样式类型:默认 | 沉浸式
styleType: 'inner' | 'normal';
// 常驻显示
alwaysShow: boolean;
// 小程序单元格列表
mpCells: NavigationBarCellProperty[];
// 其它平台单元格列表
otherCells: NavigationBarCellProperty[];
// 本地变量
bgType: 'color' | 'img'; // 背景类型
bgColor: string; // 背景颜色
bgImg: string; // 图片链接
styleType: 'inner' | 'normal'; // 样式类型:默认 | 沉浸式
alwaysShow: boolean; // 常驻显示
mpCells: NavigationBarCellProperty[]; // 小程序单元格列表
otherCells: NavigationBarCellProperty[]; // 其它平台单元格列表
_local: {
// 预览顶部导航(小程序)
previewMp: boolean;
// 预览顶部导航(非小程序)
previewOther: boolean;
};
previewMp: boolean; // 预览顶部导航(小程序)
previewOther: boolean; // 预览顶部导航(非小程序)
}; // 本地变量
}
/** 顶部导航栏 - 单元格 属性 */
export interface NavigationBarCellProperty {
// 类型:文字 | 图片 | 搜索框
type: 'image' | 'search' | 'text';
//
width: number;
// 高度
height: number;
// 顶部位置
top: number;
// 左侧位置
left: number;
// 文字内容
text: string;
// 文字颜色
textColor: string;
// 图片地址
imgUrl: string;
// 图片链接
url: string;
// 搜索框:提示文字
placeholder: string;
// 搜索框:边框圆角半径
borderRadius: number;
type: 'image' | 'search' | 'text'; // 类型:文字 | 图片 | 搜索框
width: number; // 宽度
height: number; //
top: number; // 顶部位置
left: number; // 左侧位置
text: string; // 文字内容
textColor: string; // 文字颜色
imgUrl: string; // 图片地址
url: string; // 图片链接
placeholder: string; // 搜索框:提示文字
borderRadius: number; // 搜索框:边框圆角半径
}
// 定义组件
/** 定义组件 */
export const component = {
id: 'NavigationBar',
name: '顶部导航栏',

View File

@@ -18,7 +18,7 @@ defineOptions({ name: 'NavigationBar' });
const props = defineProps<{ property: NavigationBarProperty }>();
// 背景
/** 计算背景样式 */
const bgStyle = computed(() => {
const background =
props.property.bgType === 'img' && props.property.bgImg
@@ -26,27 +26,31 @@ const bgStyle = computed(() => {
: props.property.bgColor;
return { background };
});
// 单元格列表
/** 获取当前预览的单元格列表 */
const cellList = computed(() =>
props.property._local?.previewMp
? props.property.mpCells
: props.property.otherCells,
);
// 单元格宽度
/** 计算单元格宽度 */
const cellWidth = computed(() => {
return props.property._local?.previewMp
? (375 - 80 - 86) / 6
: (375 - 90) / 8;
});
// 获得单元格样式
const getCellStyle = (cell: NavigationBarCellProperty) => {
/** 获取单元格样式 */
function getCellStyle(cell: NavigationBarCellProperty) {
return {
width: `${cell.width * cellWidth.value + (cell.width - 1) * 10}px`,
left: `${cell.left * cellWidth.value + (cell.left + 1) * 10}px`,
position: 'absolute',
} as StyleValue;
};
// 获得搜索框属性
}
/** 获取搜索框属性配置 */
const getSearchProp = computed(() => (cell: NavigationBarCellProperty) => {
return {
height: 30,

View File

@@ -2,19 +2,31 @@
import type { NavigationBarProperty } from './config';
import { useVModel } from '@vueuse/core';
import {
ElCard,
ElCheckbox,
ElForm,
ElFormItem,
ElRadio,
ElRadioGroup,
ElTooltip,
} from 'element-plus';
import UploadImg from '#/components/upload/image-upload.vue';
import { ColorInput } from '#/views/mall/promotion/components';
import NavigationBarCellProperty from './components/cell-property.vue';
// 导航栏属性面板
/** 导航栏属性面板 */
defineOptions({ name: 'NavigationBarProperty' });
const props = defineProps<{ modelValue: NavigationBarProperty }>();
const emit = defineEmits(['update:modelValue']);
// 表单校验
const rules = {
name: [{ required: true, message: '请输入页面名称', trigger: 'blur' }],
};
}; // 表单校验
const formData = useVModel(props, 'modelValue', emit);
if (!formData.value._local) {
@@ -23,47 +35,47 @@ if (!formData.value._local) {
</script>
<template>
<el-form label-width="80px" :model="formData" :rules="rules">
<el-form-item label="样式" prop="styleType">
<el-radio-group v-model="formData!.styleType">
<el-radio value="normal">标准</el-radio>
<el-tooltip
<ElForm label-width="80px" :model="formData" :rules="rules">
<ElFormItem label="样式" prop="styleType">
<ElRadioGroup v-model="formData!.styleType">
<ElRadio value="normal">标准</ElRadio>
<ElTooltip
content="沉侵式头部仅支持微信小程序、APP建议页面第一个组件为图片展示类组件"
placement="top"
>
<el-radio value="inner">沉浸式</el-radio>
</el-tooltip>
</el-radio-group>
</el-form-item>
<el-form-item
<ElRadio value="inner">沉浸式</ElRadio>
</ElTooltip>
</ElRadioGroup>
</ElFormItem>
<ElFormItem
label="常驻显示"
prop="alwaysShow"
v-if="formData.styleType === 'inner'"
>
<el-radio-group v-model="formData!.alwaysShow">
<el-radio :value="false">关闭</el-radio>
<el-tooltip
<ElRadioGroup v-model="formData!.alwaysShow">
<ElRadio :value="false">关闭</ElRadio>
<ElTooltip
content="常驻显示关闭后,头部小组件将在页面滑动时淡入"
placement="top"
>
<el-radio :value="true">开启</el-radio>
</el-tooltip>
</el-radio-group>
</el-form-item>
<el-form-item label="背景类型" prop="bgType">
<el-radio-group v-model="formData.bgType">
<el-radio value="color">纯色</el-radio>
<el-radio value="img">图片</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
<ElRadio :value="true">开启</ElRadio>
</ElTooltip>
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="背景类型" prop="bgType">
<ElRadioGroup v-model="formData.bgType">
<ElRadio value="color">纯色</ElRadio>
<ElRadio value="img">图片</ElRadio>
</ElRadioGroup>
</ElFormItem>
<ElFormItem
label="背景颜色"
prop="bgColor"
v-if="formData.bgType === 'color'"
>
<ColorInput v-model="formData.bgColor" />
</el-form-item>
<el-form-item label="背景图片" prop="bgImg" v-else>
</ElFormItem>
<ElFormItem label="背景图片" prop="bgImg" v-else>
<div class="flex items-center">
<UploadImg
v-model="formData.bgImg"
@@ -74,44 +86,42 @@ if (!formData.value._local) {
/>
<span class="mb-2 ml-2 text-xs text-gray-400">建议宽度750</span>
</div>
</el-form-item>
<el-card class="property-group" shadow="never">
</ElFormItem>
<ElCard class="property-group" shadow="never">
<template #header>
<div class="flex items-center justify-between">
<span>内容小程序</span>
<el-form-item prop="_local.previewMp" class="mb-0">
<el-checkbox
<ElFormItem prop="_local.previewMp" class="mb-0">
<ElCheckbox
v-model="formData._local.previewMp"
@change="
formData._local.previewOther = !formData._local.previewMp
"
>
预览
</el-checkbox>
</el-form-item>
</ElCheckbox>
</ElFormItem>
</div>
</template>
<NavigationBarCellProperty v-model="formData.mpCells" is-mp />
</el-card>
<el-card class="property-group" shadow="never">
</ElCard>
<ElCard class="property-group" shadow="never">
<template #header>
<div class="flex items-center justify-between">
<span>内容非小程序</span>
<el-form-item prop="_local.previewOther" class="mb-0">
<el-checkbox
<ElFormItem prop="_local.previewOther" class="mb-0">
<ElCheckbox
v-model="formData._local.previewOther"
@change="
formData._local.previewMp = !formData._local.previewOther
"
>
预览
</el-checkbox>
</el-form-item>
</ElCheckbox>
</ElFormItem>
</div>
</template>
<NavigationBarCellProperty v-model="formData.otherCells" :is-mp="false" />
</el-card>
</el-form>
</ElCard>
</ElForm>
</template>
<style scoped lang="scss"></style>