适配顶部导航
This commit is contained in:
parent
1a5e7902b8
commit
c5b1ad480f
BIN
src/assets/imgs/diy/app-nav-bar-mp.png
Normal file
BIN
src/assets/imgs/diy/app-nav-bar-mp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
@ -0,0 +1,90 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-40px flex items-center justify-center">
|
||||||
|
<MagicCubeEditor
|
||||||
|
v-model="cellList"
|
||||||
|
class="m-b-16px"
|
||||||
|
:rows="1"
|
||||||
|
:cols="cellCount"
|
||||||
|
:cube-size="38"
|
||||||
|
@hot-area-selected="handleHotAreaSelected"
|
||||||
|
/>
|
||||||
|
<img src="@/assets/imgs/diy/app-nav-bar-mp.png" alt="" class="h-30px w-76px" v-if="isMp" />
|
||||||
|
</div>
|
||||||
|
<template v-for="(cell, cellIndex) in cellList" :key="cellIndex">
|
||||||
|
<template v-if="selectedHotAreaIndex === cellIndex">
|
||||||
|
<el-form-item label="类型" :prop="`cell[${cellIndex}].type`">
|
||||||
|
<el-radio-group v-model="cell.type">
|
||||||
|
<el-radio label="text">文字</el-radio>
|
||||||
|
<el-radio label="image">图片</el-radio>
|
||||||
|
<el-radio label="search">搜索框</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<!-- 1. 文字 -->
|
||||||
|
<template v-if="cell.type === 'text'">
|
||||||
|
<el-form-item label="内容" :prop="`cell[${cellIndex}].text`">
|
||||||
|
<el-input v-model="cell!.text" maxlength="10" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="颜色" :prop="`cell[${cellIndex}].text`">
|
||||||
|
<ColorInput v-model="cell!.textColor" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<!-- 2. 图片 -->
|
||||||
|
<template v-else-if="cell.type === 'image'">
|
||||||
|
<el-form-item label="图片" :prop="`cell[${cellIndex}].imgUrl`">
|
||||||
|
<UploadImg v-model="cell.imgUrl" :limit="1" height="56px" width="56px">
|
||||||
|
<template #tip>建议尺寸 56*56</template>
|
||||||
|
</UploadImg>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="链接" :prop="`cell[${cellIndex}].url`">
|
||||||
|
<AppLinkInput v-model="cell.url" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<!-- 3. 搜索框 -->
|
||||||
|
<template v-else>
|
||||||
|
<el-form-item label="提示文字" :prop="`cell[${cellIndex}].placeholder`">
|
||||||
|
<el-input v-model="cell.placeholder" maxlength="10" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="圆角" :prop="`cell[${cellIndex}].borderRadius`">
|
||||||
|
<el-slider
|
||||||
|
v-model="cell.borderRadius"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
show-input
|
||||||
|
input-size="small"
|
||||||
|
:show-input-controls="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { NavigationBarCellProperty } from '../config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
// 导航栏属性面板
|
||||||
|
defineOptions({ name: 'NavigationBarCellProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: NavigationBarCellProperty[]
|
||||||
|
isMp: boolean
|
||||||
|
}>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData: cellList } = usePropertyForm(props.modelValue, emit)
|
||||||
|
if (!cellList.value) cellList.value = []
|
||||||
|
|
||||||
|
// 单元格数量:小程序6个(右侧胶囊按钮占了2个),其它平台8个
|
||||||
|
const cellCount = computed(() => (props.isMp ? 6 : 8))
|
||||||
|
|
||||||
|
// 选中的热区
|
||||||
|
const selectedHotAreaIndex = ref(0)
|
||||||
|
const handleHotAreaSelected = (cellValue: NavigationBarCellProperty, index: number) => {
|
||||||
|
selectedHotAreaIndex.value = index
|
||||||
|
if (!cellValue.type) {
|
||||||
|
cellValue.type = 'text'
|
||||||
|
cellValue.textColor = '#111111'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -2,22 +2,53 @@ import { DiyComponent } from '@/components/DiyEditor/util'
|
|||||||
|
|
||||||
/** 顶部导航栏属性 */
|
/** 顶部导航栏属性 */
|
||||||
export interface NavigationBarProperty {
|
export interface NavigationBarProperty {
|
||||||
// 页面标题
|
// 背景类型
|
||||||
title: string
|
bgType: 'color' | 'img'
|
||||||
// 页面描述
|
// 背景颜色
|
||||||
description: string
|
bgColor: string
|
||||||
// 顶部导航高度
|
// 图片链接
|
||||||
navBarHeight: number
|
bgImg: string
|
||||||
// 页面背景颜色
|
|
||||||
backgroundColor: string
|
|
||||||
// 页面背景图片
|
|
||||||
backgroundImage: string
|
|
||||||
// 样式类型:默认 | 沉浸式
|
// 样式类型:默认 | 沉浸式
|
||||||
styleType: 'default' | 'immersion'
|
styleType: 'normal' | 'inner'
|
||||||
// 常驻显示
|
// 常驻显示
|
||||||
alwaysShow: boolean
|
alwaysShow: boolean
|
||||||
// 是否显示返回按钮
|
// 小程序单元格列表
|
||||||
showGoBack: boolean
|
mpCells: NavigationBarCellProperty[]
|
||||||
|
// 其它平台单元格列表
|
||||||
|
otherCells: NavigationBarCellProperty[]
|
||||||
|
// 本地变量
|
||||||
|
_local: {
|
||||||
|
// 预览顶部导航(小程序)
|
||||||
|
previewMp: boolean
|
||||||
|
// 预览顶部导航(非小程序)
|
||||||
|
previewOther: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 顶部导航栏 - 单元格 属性 */
|
||||||
|
export interface NavigationBarCellProperty {
|
||||||
|
// 类型:文字 | 图片 | 搜索框
|
||||||
|
type: 'text' | 'image' | 'search'
|
||||||
|
// 宽度
|
||||||
|
width: number
|
||||||
|
// 高度
|
||||||
|
height: number
|
||||||
|
// 顶部位置
|
||||||
|
top: number
|
||||||
|
// 左侧位置
|
||||||
|
left: number
|
||||||
|
// 文字内容
|
||||||
|
text: string
|
||||||
|
// 文字颜色
|
||||||
|
textColor: string
|
||||||
|
// 图片地址
|
||||||
|
imgUrl: string
|
||||||
|
// 图片链接
|
||||||
|
url: string
|
||||||
|
// 搜索框:提示文字
|
||||||
|
placeholder: string
|
||||||
|
// 搜索框:边框圆角半径
|
||||||
|
borderRadius: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义组件
|
// 定义组件
|
||||||
@ -26,13 +57,26 @@ export const component = {
|
|||||||
name: '顶部导航栏',
|
name: '顶部导航栏',
|
||||||
icon: 'tabler:layout-navbar',
|
icon: 'tabler:layout-navbar',
|
||||||
property: {
|
property: {
|
||||||
title: '页面标题',
|
bgType: 'color',
|
||||||
description: '',
|
bgColor: '#fff',
|
||||||
navBarHeight: 35,
|
bgImg: '',
|
||||||
backgroundColor: '#fff',
|
styleType: 'normal',
|
||||||
backgroundImage: '',
|
|
||||||
styleType: 'default',
|
|
||||||
alwaysShow: true,
|
alwaysShow: true,
|
||||||
showGoBack: true
|
mpCells: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
textColor: '#111111'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
otherCells: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
textColor: '#111111'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
_local: {
|
||||||
|
previewMp: true,
|
||||||
|
previewOther: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} as DiyComponent<NavigationBarProperty>
|
} as DiyComponent<NavigationBarProperty>
|
||||||
|
@ -1,45 +1,73 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div class="navigation-bar" :style="bgStyle">
|
||||||
class="navigation-bar"
|
<div class="h-full w-full flex items-center">
|
||||||
:style="{
|
<div v-for="(cell, cellIndex) in cellList" :key="cellIndex" :style="getCellStyle(cell)">
|
||||||
height: `${property.navBarHeight}px`,
|
<span v-if="cell.type === 'text'">{{ cell.text }}</span>
|
||||||
backgroundColor: property.backgroundColor,
|
<img v-else-if="cell.type === 'image'" :src="cell.imgUrl" alt="" class="h-full w-full" />
|
||||||
backgroundImage: `url(${property.backgroundImage})`
|
<SearchBar v-else :property="getSearchProp" />
|
||||||
}"
|
</div>
|
||||||
>
|
|
||||||
<!-- 左侧 -->
|
|
||||||
<div class="left">
|
|
||||||
<Icon icon="ep:arrow-left" v-show="property.showGoBack" />
|
|
||||||
</div>
|
</div>
|
||||||
<!-- 中间 -->
|
<img
|
||||||
<div
|
v-if="property._local?.previewMp"
|
||||||
class="center"
|
src="@/assets/imgs/diy/app-nav-bar-mp.png"
|
||||||
:style="{
|
alt=""
|
||||||
height: `${property.navBarHeight}px`,
|
class="h-30px w-86px"
|
||||||
lineHeight: `${property.navBarHeight}px`
|
/>
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ property.title }}
|
|
||||||
</div>
|
|
||||||
<!-- 右侧 -->
|
|
||||||
<div class="right"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { NavigationBarProperty } from './config'
|
import { NavigationBarCellProperty, NavigationBarProperty } from './config'
|
||||||
|
import SearchBar from '@/components/DiyEditor/components/mobile/SearchBar/index.vue'
|
||||||
|
import { StyleValue } from 'vue'
|
||||||
|
import { SearchProperty } from '@/components/DiyEditor/components/mobile/SearchBar/config'
|
||||||
|
|
||||||
/** 页面顶部导航栏 */
|
/** 页面顶部导航栏 */
|
||||||
defineOptions({ name: 'NavigationBar' })
|
defineOptions({ name: 'NavigationBar' })
|
||||||
|
|
||||||
defineProps<{ property: NavigationBarProperty }>()
|
const props = defineProps<{ property: NavigationBarProperty }>()
|
||||||
|
|
||||||
|
// 背景
|
||||||
|
const bgStyle = computed(() => {
|
||||||
|
const background =
|
||||||
|
props.property.bgType === 'img' && props.property.bgImg
|
||||||
|
? `url(${props.property.bgImg}) no-repeat top center / 100% 100%`
|
||||||
|
: 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) => {
|
||||||
|
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 = (cell: NavigationBarCellProperty) => {
|
||||||
|
return {
|
||||||
|
height: 30,
|
||||||
|
showScan: false,
|
||||||
|
placeholder: cell.placeholder,
|
||||||
|
borderRadius: cell.borderRadius
|
||||||
|
} as SearchProperty
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.navigation-bar {
|
.navigation-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 35px;
|
height: 50px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 0 6px;
|
||||||
|
|
||||||
/* 左边 */
|
/* 左边 */
|
||||||
.left {
|
.left {
|
||||||
|
@ -1,53 +1,73 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-form label-width="80px" :model="formData" :rules="rules">
|
<el-form label-width="80px" :model="formData" :rules="rules">
|
||||||
<el-form-item label="页面标题" prop="title">
|
|
||||||
<el-input v-model="formData!.title" placeholder="页面标题" maxlength="25" show-word-limit />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="页面描述" prop="description">
|
|
||||||
<el-input
|
|
||||||
type="textarea"
|
|
||||||
v-model="formData!.description"
|
|
||||||
placeholder="用户通过微信分享给朋友时,会自动显示页面描述"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="样式" prop="styleType">
|
<el-form-item label="样式" prop="styleType">
|
||||||
<el-radio-group v-model="formData!.styleType">
|
<el-radio-group v-model="formData!.styleType">
|
||||||
<el-radio label="default">默认</el-radio>
|
<el-radio label="normal">标准</el-radio>
|
||||||
<el-radio label="immersion">沉浸式</el-radio>
|
<el-tooltip
|
||||||
|
content="沉侵式头部仅支持微信小程序、APP,建议页面第一个组件为图片展示类组件"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<el-radio label="inner">沉浸式</el-radio>
|
||||||
|
</el-tooltip>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="常驻显示" prop="alwaysShow" v-if="formData.styleType === 'immersion'">
|
<el-form-item label="常驻显示" prop="alwaysShow" v-if="formData.styleType === 'inner'">
|
||||||
<el-radio-group v-model="formData!.alwaysShow">
|
<el-radio-group v-model="formData!.alwaysShow">
|
||||||
<el-radio :label="false">关闭</el-radio>
|
<el-radio :label="false">关闭</el-radio>
|
||||||
<el-radio :label="true">开启</el-radio>
|
<el-tooltip content="常驻显示关闭后,头部小组件将在页面滑动时淡入" placement="top">
|
||||||
|
<el-radio :label="true">开启</el-radio>
|
||||||
|
</el-tooltip>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="高度" prop="navBarHeight">
|
<el-form-item label="背景类型" prop="bgType">
|
||||||
<el-slider
|
<el-radio-group v-model="formData.bgType">
|
||||||
v-model="formData!.navBarHeight"
|
<el-radio label="color">纯色</el-radio>
|
||||||
:max="100"
|
<el-radio label="img">图片</el-radio>
|
||||||
:min="35"
|
</el-radio-group>
|
||||||
show-input
|
|
||||||
input-size="small"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="返回按钮" prop="showGoBack">
|
<el-form-item label="背景颜色" prop="bgColor" v-if="formData.bgType === 'color'">
|
||||||
<el-switch v-model="formData!.showGoBack" />
|
<ColorInput v-model="formData.bgColor" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="背景颜色" prop="backgroundColor">
|
<el-form-item label="背景图片" prop="bgImg" v-else>
|
||||||
<ColorInput v-model="formData!.backgroundColor" />
|
<UploadImg v-model="formData.bgImg" :limit="1" width="56px" height="56px" />
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="背景图片" prop="backgroundImage">
|
|
||||||
<UploadImg v-model="formData!.backgroundImage" :limit="1">
|
|
||||||
<template #tip>建议宽度 750px</template>
|
|
||||||
</UploadImg>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-card class="property-group" shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span>内容(小程序)</span>
|
||||||
|
<el-form-item prop="_local.previewMp" class="m-b-0!">
|
||||||
|
<el-checkbox
|
||||||
|
v-model="formData._local.previewMp"
|
||||||
|
@change="formData._local.previewOther = !formData._local.previewMp"
|
||||||
|
>预览</el-checkbox
|
||||||
|
>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<NavigationBarCellProperty v-model="formData.mpCells" is-mp />
|
||||||
|
</el-card>
|
||||||
|
<el-card class="property-group" shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span>内容(非小程序)</span>
|
||||||
|
<el-form-item prop="_local.previewOther" class="m-b-0!">
|
||||||
|
<el-checkbox
|
||||||
|
v-model="formData._local.previewOther"
|
||||||
|
@change="formData._local.previewMp = !formData._local.previewOther"
|
||||||
|
>预览</el-checkbox
|
||||||
|
>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<NavigationBarCellProperty v-model="formData.otherCells" :is-mp="false" />
|
||||||
|
</el-card>
|
||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { NavigationBarProperty } from './config'
|
import { NavigationBarProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
import NavigationBarCellProperty from '@/components/DiyEditor/components/mobile/NavigationBar/components/CellProperty.vue'
|
||||||
// 导航栏属性面板
|
// 导航栏属性面板
|
||||||
defineOptions({ name: 'NavigationBarProperty' })
|
defineOptions({ name: 'NavigationBarProperty' })
|
||||||
// 表单校验
|
// 表单校验
|
||||||
@ -58,6 +78,9 @@ const rules = {
|
|||||||
const props = defineProps<{ modelValue: NavigationBarProperty }>()
|
const props = defineProps<{ modelValue: NavigationBarProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
if (!formData.value._local) {
|
||||||
|
formData.value._local = { previewMp: true, previewOther: false }
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -189,7 +189,7 @@ const emit = defineEmits(['update:modelValue', 'hotAreaSelected'])
|
|||||||
const emitUpdateModelValue = () => emit('update:modelValue', hotAreas)
|
const emitUpdateModelValue = () => emit('update:modelValue', hotAreas)
|
||||||
|
|
||||||
// 热区选中
|
// 热区选中
|
||||||
const selectedHotAreaIndex = ref(-1)
|
const selectedHotAreaIndex = ref(0)
|
||||||
const handleHotAreaSelected = (hotArea: Rect, index: number) => {
|
const handleHotAreaSelected = (hotArea: Rect, index: number) => {
|
||||||
selectedHotAreaIndex.value = index
|
selectedHotAreaIndex.value = index
|
||||||
emit('hotAreaSelected', hotArea, index)
|
emit('hotAreaSelected', hotArea, index)
|
||||||
|
Loading…
Reference in New Issue
Block a user