适配顶部导航

This commit is contained in:
owen 2024-03-14 00:13:40 +08:00
parent 1a5e7902b8
commit c5b1ad480f
6 changed files with 263 additions and 78 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -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 = []
// 628
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>

View File

@ -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>

View File

@ -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 class="left">
<Icon icon="ep:arrow-left" v-show="property.showGoBack" />
</div> </div>
<!-- 中间 -->
<div
class="center"
:style="{
height: `${property.navBarHeight}px`,
lineHeight: `${property.navBarHeight}px`
}"
>
{{ property.title }}
</div> </div>
<!-- 右侧 --> <img
<div class="right"></div> v-if="property._local?.previewMp"
src="@/assets/imgs/diy/app-nav-bar-mp.png"
alt=""
class="h-30px w-86px"
/>
</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 {

View File

@ -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-tooltip content="常驻显示关闭后,头部小组件将在页面滑动时淡入" placement="top">
<el-radio :label="true">开启</el-radio> <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>
<el-form-item label="背景图片" prop="backgroundImage"> <el-card class="property-group" shadow="never">
<UploadImg v-model="formData!.backgroundImage" :limit="1"> <template #header>
<template #tip>建议宽度 750px</template> <div class="flex items-center justify-between">
</UploadImg> <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> </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>

View File

@ -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)