装修,底部导航

This commit is contained in:
Jason 2022-09-06 18:42:24 +08:00
parent aed9b43d5e
commit 116d6d5eff
34 changed files with 792 additions and 121 deletions

View File

@ -9,3 +9,13 @@ export function getDecoratePages(params: any) {
export function setDecoratePages(params: any) {
return request.post({ url: '/decorate/pages/save', params })
}
// 底部导航详情
export function getDecorateTabbar(params?: any) {
return request.get({ url: '/decorate/tabbar/detail', params })
}
// 底部导航保存
export function setDecorateTabbar(params: any) {
return request.post({ url: '/decorate/tabbar/save', params })
}

View File

@ -0,0 +1,33 @@
<template>
<div class="color-picker flex flex-1">
<el-color-picker v-model="color" :predefine="predefineColors" />
<el-input v-model="color" class="mx-[10px] flex-1" type="text" readonly />
<el-button type="text" @click="reset">重置</el-button>
</div>
</template>
<script lang="ts" setup>
const props = defineProps({
modelValue: {
type: String
},
defaultColor: {
type: String
}
})
const emit = defineEmits<{
(event: 'update:modelValue', value: any): void
}>()
const color = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
const predefineColors = ['#409EFF', '#28C76F', '#EA5455', '#FF9F43', '#01CFE8', '#4A5DFF']
const reset = () => {
color.value = props.defaultColor
}
</script>

View File

@ -1,21 +1,24 @@
<template>
<div class="footer-btns">
<div class="footer-btns__content">
<div class="footer-btns__content" :style="fixed ? 'position: fixed' : ''">
<slot></slot>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({})
<script lang="ts" setup>
defineProps({
fixed: {
type: Boolean,
default: true
}
})
</script>
<style scoped lang="scss">
.footer-btns {
height: 60px;
&__content {
position: fixed;
bottom: 0;
height: 60px;
right: 0;

View File

@ -1,26 +1,17 @@
<template>
<div class="link-picker">
<popup width="700px" title="链接选择" @confirm="handleConfirm">
<template v-slot:trigger>
<div class="cursor-pointer">
<slot>
<el-input
:model-value="modelValue?.name ?? modelValue?.path"
placeholder="请选择链接"
readonly
>
<template #suffix>
<icon v-if="!modelValue?.path" name="el-icon-ArrowRight" />
<icon
v-else
name="el-icon-Close"
@click.stop="emit('update:modelValue', {})"
/>
</template>
</el-input>
</slot>
</div>
<div class="link-picker flex-1" @click="popupRef?.open()">
<el-input
class="cursor-pointer"
:model-value="modelValue?.name ?? modelValue?.path"
placeholder="请选择链接"
readonly
>
<template #suffix>
<icon v-if="!modelValue?.path" name="el-icon-ArrowRight" />
<icon v-else name="el-icon-Close" @click.stop="emit('update:modelValue', {})" />
</template>
</el-input>
<popup ref="popupRef" width="700px" title="链接选择" @confirm="handleConfirm">
<link-content v-model="activeLink" />
</popup>
</div>
@ -29,6 +20,7 @@
<script lang="ts" setup>
import type { Link } from '.'
import LinkContent from './index.vue'
import Popup from '@/components/popup/index.vue'
const props = defineProps({
modelValue: {
type: Object
@ -37,6 +29,8 @@ const props = defineProps({
const emit = defineEmits<{
(event: 'update:modelValue', value: any): void
}>()
const popupRef = shallowRef<InstanceType<typeof Popup>>()
const activeLink = ref<Link>()
const handleConfirm = () => {
emit('update:modelValue', activeLink.value)

View File

@ -0,0 +1,74 @@
<template>
<div>
<div>
<del-wrap
class="max-w-[400px]"
v-for="(item, index) in modelValue"
:key="index"
@close="handleDelete(index)"
>
<div class="bg-fill-light flex items-center w-full p-4 mb-4">
<material-picker v-model="item.image" upload-class="bg-body" size="60px">
<template #upload>
<div class="upload-btn w-[60px] h-[60px]">
<icon name="el-icon-Plus" :size="20" />
</div>
</template>
</material-picker>
<div class="ml-3 flex-1">
<div class="flex">
<span class="text-tx-regular flex-none mr-3">名称</span>
<el-input v-model="item.name" placeholder="请输入名称" />
</div>
<div class="flex mt-[18px]">
<span class="text-tx-regular flex-none mr-3">链接</span>
<link-picker v-model="item.link" />
</div>
</div>
</div>
</del-wrap>
</div>
<div>
<el-button type="primary" @click="handleAdd">添加</el-button>
</div>
</div>
</template>
<script lang="ts" setup>
import feedback from '@/utils/feedback'
import type { PropType } from 'vue'
const props = defineProps({
modelValue: {
type: Array as PropType<any[]>,
default: () => []
},
max: {
type: Number,
default: 10
},
min: {
type: Number,
default: 1
}
})
const handleAdd = () => {
if (props.modelValue?.length < props.max) {
props.modelValue.push({
image: '',
name: '',
link: {}
})
} else {
feedback.msgError(`最多添加${props.max}`)
}
}
const handleDelete = (index: number) => {
if (props.modelValue?.length <= props.min) {
return feedback.msgError(`最少保留${props.min}`)
}
props.modelValue.splice(index, 1)
}
</script>
<style lang="scss" scoped></style>

View File

@ -5,12 +5,14 @@
>
{{ widget?.title }}
</div>
<component
class="pt-5 pr-4"
:is="widgets[widget?.name]?.attr"
:content="widget?.content"
:styles="widget?.styles"
/>
<keep-alive>
<component
class="pt-5 pr-4"
:is="widgets[widget?.name]?.attr"
:content="widget?.content"
:styles="widget?.styles"
/>
</keep-alive>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,30 +1,32 @@
<template>
<el-scrollbar class="shadow mx-[30px] pages-preview">
<div>
<div class="shadow mx-[30px] pages-preview">
<div
v-for="(widget, index) in pageData"
:key="widget"
class="relative"
:class="{
'cursor-pointer': !widget?.disabled
}"
@click="handleClick(widget, index)"
>
<div
v-for="(widget, index) in pageData"
:key="widget"
class="relative"
class="absolute w-full h-full z-[100] border-dashed"
:class="{
'cursor-pointer': !widget?.disabled
select: index == modelValue,
'border-br border-2': !widget?.disabled
}"
@click="handleClick(widget, index)"
>
<div
class="absolute w-full h-full z-[100] border-dashed"
:class="{
select: index == modelValue,
'border-br border-2': !widget?.disabled
}"
></div>
<component
:is="widgets[widget?.name]?.content"
:content="widget.content"
:styles="widget.styles"
/>
</div>
></div>
<slot>
<keep-alive>
<component
:is="widgets[widget?.name]?.content"
:content="widget.content"
:styles="widget.styles"
/>
</keep-alive>
</slot>
</div>
</el-scrollbar>
</div>
</template>
<script lang="ts" setup>
import widgets from '../widgets'
@ -55,8 +57,8 @@ const handleClick = (widget: any, index: number) => {
.pages-preview {
background-color: #f8f8f8;
width: 360px;
max-height: 734px;
height: 100%;
height: 615px;
color: #333;
.select {
@apply border-primary border-solid;
}

View File

@ -9,14 +9,16 @@
</el-form-item>
<el-form-item label="图片设置">
<div class="flex-1">
<div class="form-tips">最多添加5张建议图片尺寸750px*400px</div>
<del-wrap
v-for="(item, index) in content.data"
:key="index"
@close="handleDelete(index)"
class="max-w-[400px]"
>
<div class="bg-fill-light flex items-center w-full p-4 mb-4">
<div class="bg-fill-light flex items-center w-full p-4 mt-4">
<material-picker v-model="item.image" upload-class="bg-body" />
<div>
<div class="ml-3 flex-1">
<el-form-item label="图片名称">
<el-input v-model="item.name" placeholder="请输入名称" />
</el-form-item>
@ -38,7 +40,7 @@
import feedback from '@/utils/feedback'
import type { PropType } from 'vue'
import type options from './options'
const limit = 10
const limit = 5
type OptionsType = ReturnType<typeof options>
const props = defineProps({
content: {

View File

@ -1,7 +1,7 @@
<template>
<div class="banner">
<div class="banner-image">
<image-contain width="100%" height="170px" :src="getImage" fit="contain" />
<image-contain width="100%" height="200px" :src="getImage" fit="contain" />
</div>
</div>
</template>

View File

@ -0,0 +1,8 @@
import attr from './attr.vue'
import content from './content.vue'
import options from './options'
export default {
attr,
content,
options
}

View File

@ -0,0 +1,38 @@
<template>
<div>
<el-form label-width="90px">
<el-form-item label="客服标题">
<el-input class="w-[400px]" v-model="content.title" />
</el-form-item>
<el-form-item label="服务时间">
<el-input class="w-[400px]" v-model="content.time" />
</el-form-item>
<el-form-item label="联系电话">
<el-input class="w-[400px]" v-model="content.mobile" />
</el-form-item>
<el-form-item label="客服二维码">
<div>
<material-picker v-model="content.qrcode" />
<div class="form-tips">建议图片尺寸200*200像素图片格式jpgpngjpeg</div>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script lang="ts" setup>
import type { PropType } from 'vue'
import type options from './options'
type OptionsType = ReturnType<typeof options>
defineProps({
content: {
type: Object as PropType<OptionsType['content']>,
default: () => ({})
},
styles: {
type: Object as PropType<OptionsType['styles']>,
default: () => ({})
}
})
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,38 @@
<template>
<div class="customer-service">
<image-contain width="140px" height="140px" :src="content.qrcode" alt="" />
<div class="text-[15px] mt-[7px] font-medium">{{ content.title }}</div>
<div class="text-[#666] mt-[20px]">服务时间{{ content.time }}</div>
<div class="text-[#666] mt-[7px]">客服电话{{ content.mobile }}</div>
<div
class="text-white text-[16px] rounded-[42px] bg-[#4173FF] w-full h-[42px] flex justify-center items-center mt-[50px]"
>
保存二维码图片
</div>
</div>
</template>
<script lang="ts" setup>
import type { PropType } from 'vue'
import type options from './options'
type OptionsType = ReturnType<typeof options>
defineProps({
content: {
type: Object as PropType<OptionsType['content']>,
default: () => ({})
},
styles: {
type: Object as PropType<OptionsType['styles']>,
default: () => ({})
}
})
</script>
<style lang="scss" scoped>
.customer-service {
margin: 10px 18px;
border-radius: 10px;
padding: 50px 55px 80px;
background: #fff;
@apply flex flex-col justify-center items-center;
}
</style>

View File

@ -0,0 +1,8 @@
import attr from './attr.vue'
import content from './content.vue'
import options from './options'
export default {
attr,
content,
options
}

View File

@ -0,0 +1,11 @@
export default () => ({
title: '客服设置',
name: 'customer-service',
content: {
title: '添加客服二维码',
time: '',
mobile: '',
qrcode: ''
},
styles: {}
})

View File

@ -1,15 +1,14 @@
const widgets: Record<string, any> = import.meta.glob('./**/*', { eager: true })
const widgets: Record<string, any> = import.meta.glob('./**/index.ts', { eager: true })
interface Widget {
attr: any
content: any
options: any
}
console.log(widgets)
const exportWidgets: Record<string, Widget> = {}
Object.keys(widgets).forEach((key) => {
const widgetName = key.replace(/^\.\/([\w-]+).*/gi, '$1')
const widgetContent = key.replace(/(.*\/)*([^.]+).*/gi, '$2') as keyof Widget
exportWidgets[widgetName] = exportWidgets[widgetName] ?? {}
exportWidgets[widgetName][widgetContent] = widgets[key]?.default
exportWidgets[widgetName] = widgets[key]?.default
})
export default exportWidgets

View File

@ -0,0 +1,38 @@
<template>
<div>
<el-form label-width="70px">
<el-form-item label="排版样式">
<el-radio-group v-model="content.style">
<el-radio :label="1">横排</el-radio>
<el-radio :label="2">竖排</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="标题名称">
<el-input class="w-[400px]" v-model="content.title" />
</el-form-item>
<el-form-item label="菜单设置">
<div class="flex-1">
<AddNav v-model="content.data" />
</div>
</el-form-item>
</el-form>
</div>
</template>
<script lang="ts" setup>
import type { PropType } from 'vue'
import type options from './options'
import AddNav from '../../add-nav.vue'
type OptionsType = ReturnType<typeof options>
defineProps({
content: {
type: Object as PropType<OptionsType['content']>,
default: () => ({})
},
styles: {
type: Object as PropType<OptionsType['styles']>,
default: () => ({})
}
})
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,58 @@
<template>
<div class="my-service">
<div v-if="content.title" class="title px-[15px] py-[10px]">
<div>{{ content.title }}</div>
</div>
<div v-if="content.style == 1" class="flex flex-wrap pt-[20px] pb-[10px]">
<div
v-for="(item, index) in content.data"
:key="index"
class="flex flex-col items-center w-1/4 mb-[15px]"
>
<image-contain width="26px" height="26px" :src="item.image" alt="" />
<div class="mt-[7px]">{{ item.name }}</div>
</div>
</div>
<div v-if="content.style == 2">
<div
v-for="(item, index) in content.data"
:key="index"
class="flex items-center border-b border-[#e5e5e5] h-[50px] px-[12px]"
>
<image-contain width="24px" height="24px" :src="item.image" alt="" />
<div class="ml-[10px] flex-1">{{ item.name }}</div>
<div>
<icon name="el-icon-ArrowRight" />
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import type { PropType } from 'vue'
import type options from './options'
type OptionsType = ReturnType<typeof options>
defineProps({
content: {
type: Object as PropType<OptionsType['content']>,
default: () => ({})
},
styles: {
type: Object as PropType<OptionsType['styles']>,
default: () => ({})
}
})
</script>
<style lang="scss" scoped>
.my-service {
margin: 10px 10px 0;
background-color: #fff;
border-radius: 7px;
.title {
border-bottom: 1px solid #e5e5e5;
font-size: 16px;
font-weight: 500;
}
}
</style>

View File

@ -0,0 +1,8 @@
import attr from './attr.vue'
import content from './content.vue'
import options from './options'
export default {
attr,
content,
options
}

View File

@ -0,0 +1,16 @@
export default () => ({
title: '我的服务',
name: 'my-service',
content: {
style: 1,
title: '我的服务',
data: [
{
image: '',
name: '',
link: {}
}
]
},
styles: {}
})

View File

@ -7,47 +7,21 @@
<el-radio :label="0">停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="菜单图片">
<el-form-item label="菜单设置">
<div class="flex-1">
<div class="form-tips">最多可添加10个建议图片尺寸100px*100px</div>
<del-wrap
v-for="(item, index) in content.data"
:key="index"
@close="handleDelete(index)"
>
<div class="bg-fill-light flex items-center w-full p-4 mt-4">
<material-picker v-model="item.image" upload-class="bg-body">
<template #upload>
<div class="upload-btn w-[60px] h-[60px]">
<icon name="el-icon-Plus" :size="20" />
</div>
</template>
</material-picker>
<div>
<el-form-item label="图片名称">
<el-input v-model="item.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item class="mt-[18px]" label="图片链接">
<link-picker v-model="item.link" />
</el-form-item>
</div>
</div>
</del-wrap>
<div class="form-tips mb-4">最多可添加10个建议图片尺寸100px*100px</div>
<AddNav v-model="content.data" />
</div>
</el-form-item>
<el-form-item v-if="content.data?.length < limit">
<el-button type="primary" @click="handleAdd">添加菜单</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script lang="ts" setup>
import feedback from '@/utils/feedback'
import type { PropType } from 'vue'
import type options from './options'
import AddNav from '../../add-nav.vue'
type OptionsType = ReturnType<typeof options>
const limit = 10
const props = defineProps({
defineProps({
content: {
type: Object as PropType<OptionsType['content']>,
default: () => ({})
@ -57,21 +31,6 @@ const props = defineProps({
default: () => ({})
}
})
const handleAdd = () => {
if (props.content.data?.length < limit) {
props.content.data.push({
image: '',
name: '导航',
link: {}
})
} else {
feedback.msgError(`最多添加${limit}`)
}
}
const handleDelete = (index: number) => {
props.content.data.splice(index, 1)
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,8 @@
import attr from './attr.vue'
import content from './content.vue'
import options from './options'
export default {
attr,
content,
options
}

View File

@ -0,0 +1,8 @@
import attr from './attr.vue'
import content from './content.vue'
import options from './options'
export default {
attr,
content,
options
}

View File

@ -0,0 +1,75 @@
<template>
<div>
<el-form label-width="70px">
<el-form-item label="是否启用">
<el-radio-group v-model="content.enabled">
<el-radio :label="1">开启</el-radio>
<el-radio :label="0">停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="图片设置">
<div class="flex-1">
<div class="form-tips">最多添加5张建议图片尺寸750px*200px</div>
<del-wrap
v-for="(item, index) in content.data"
:key="index"
@close="handleDelete(index)"
class="max-w-[400px]"
>
<div class="bg-fill-light flex items-center w-full p-4 mt-4">
<material-picker v-model="item.image" upload-class="bg-body" />
<div class="ml-3 flex-1">
<el-form-item label="图片名称">
<el-input v-model="item.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item class="mt-[18px]" label="图片链接">
<link-picker v-model="item.link" />
</el-form-item>
</div>
</div>
</del-wrap>
</div>
</el-form-item>
<el-form-item v-if="content.data?.length < limit">
<el-button type="primary" @click="handleAdd">添加图片</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script lang="ts" setup>
import feedback from '@/utils/feedback'
import type { PropType } from 'vue'
import type options from './options'
const limit = 5
type OptionsType = ReturnType<typeof options>
const props = defineProps({
content: {
type: Object as PropType<OptionsType['content']>,
default: () => ({})
},
styles: {
type: Object as PropType<OptionsType['styles']>,
default: () => ({})
}
})
const handleAdd = () => {
if (props.content.data?.length < limit) {
props.content.data.push({
image: '',
name: '',
link: {}
})
} else {
feedback.msgError(`最多添加${limit}张图片`)
}
}
const handleDelete = (index: number) => {
if (props.content.data?.length <= 1) {
return feedback.msgError('最少保留一张图片')
}
props.content.data.splice(index, 1)
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,31 @@
<template>
<div class="banner mx-[10px] mt-[10px]">
<div class="banner-image">
<image-contain width="100%" height="100px" :src="getImage" fit="contain" />
</div>
</div>
</template>
<script lang="ts" setup>
import type { PropType } from 'vue'
import type options from './options'
type OptionsType = ReturnType<typeof options>
const props = defineProps({
content: {
type: Object as PropType<OptionsType['content']>,
default: () => ({})
},
styles: {
type: Object as PropType<OptionsType['styles']>,
default: () => ({})
}
})
const getImage = computed(() => {
const { data } = props.content
if (Array.isArray(data)) {
return data[0] ? data[0].image : ''
}
return ''
})
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,8 @@
import attr from './attr.vue'
import content from './content.vue'
import options from './options'
export default {
attr,
content,
options
}

View File

@ -0,0 +1,15 @@
export default () => ({
title: '个人中心广告图',
name: 'user-banner',
content: {
enabled: 1,
data: [
{
image: '',
name: '',
link: {}
}
]
},
styles: {}
})

View File

@ -0,0 +1,20 @@
<template>
<div></div>
</template>
<script lang="ts" setup>
import type { PropType } from 'vue'
import type options from './options'
type OptionsType = ReturnType<typeof options>
const props = defineProps({
content: {
type: Object as PropType<OptionsType['content']>,
default: () => ({})
},
styles: {
type: Object as PropType<OptionsType['styles']>,
default: () => ({})
}
})
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,23 @@
<template>
<div class="user-info flex items-center px-[25px]">
<img src="./images/default_avatar.png" class="w-[60px] h-[60px]" alt="" />
<div class="text-white text-[18px] ml-[10px]">未登录</div>
</div>
</template>
<script lang="ts" setup></script>
<style lang="scss" scoped>
.user-info {
background: url(./images/my_topbg.png);
height: 115px;
background-position: bottom;
background-size: 100% auto;
.search-con {
height: 100%;
height: 36px;
border-radius: 36px;
background: #f4f4f4;
color: #999999;
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View File

@ -0,0 +1,8 @@
import attr from './attr.vue'
import content from './content.vue'
import options from './options'
export default {
attr,
content,
options
}

View File

@ -0,0 +1,7 @@
export default () => ({
title: '用户信息',
name: 'user-info',
disabled: 1,
content: {},
styles: {}
})

View File

@ -1,17 +1,13 @@
<template>
<div class="decoration-pages">
<div class="decoration-pages min-w-[1100px]">
<el-card shadow="never" class="!border-none flex-1" :body-style="{ height: '100%' }">
<div class="flex h-full items-start">
<el-scrollbar>
<Menu v-model="activeMenu" :menus="menus" />
</el-scrollbar>
<Menu v-model="activeMenu" :menus="menus" />
<preview v-model="selectWidgetIndex" :pageData="getPageData" />
<el-scrollbar class="flex-1">
<attr-setting :widget="getSelectWidget" />
</el-scrollbar>
<attr-setting class="flex-1" :widget="getSelectWidget" />
</div>
</el-card>
<footer-btns>
<footer-btns class="mt-4" :fixed="false">
<el-button type="primary" @click="setData">保存</el-button>
</footer-btns>
</div>
@ -51,13 +47,13 @@ const menus: Record<
id: 2,
pageType: 2,
name: '个人中心',
pageData: generatePageData(['banner'])
pageData: generatePageData(['user-info', 'my-service', 'user-banner'])
},
[pagesTypeEnum.SERVICE]: {
id: 3,
pageType: 3,
name: '客服设置',
pageData: generatePageData(['banner'])
pageData: generatePageData(['customer-service'])
}
})
@ -95,7 +91,7 @@ watch(
</script>
<style lang="scss" scoped>
.decoration-pages {
height: calc(100vh - var(--navbar-height) - 80px);
min-height: calc(100vh - var(--navbar-height) - 80px);
@apply flex flex-col;
}
</style>

View File

@ -0,0 +1,171 @@
<template>
<div class="decoration-tabbar min-w-[800px]">
<el-card shadow="never" class="!border-none flex-1" :body-style="{ height: '100%' }">
<div class="flex h-full items-start">
<div class="pages-preview mx-[30px]">
<div class="tabbar flex">
<div
class="tabbar-item flex flex-col justify-center items-center flex-1"
v-for="(item, index) in tabbar.data"
:key="index"
:style="{ color: tabbar.color }"
>
<img class="w-[22px] h-[22px]" :src="item.unselected" alt="" />
<div class="leading-3 text-[12px] mt-[4px]">{{ item.name }}</div>
</div>
</div>
</div>
<div class="flex-1">
<div
class="title flex items-center before:w-[3px] before:h-[14px] before:block before:bg-primary before:mr-2"
>
底部导航设置
<span class="form-tips ml-[10px] !mt-0">
至少添加2个导航最多添加5个导航
</span>
</div>
<el-form label-width="70px">
<el-tabs model-value="content">
<el-tab-pane label="导航图片" name="content">
<div class="mb-[18px]">
<del-wrap
v-for="(item, index) in tabbar.data"
:key="index"
@close="handleDelete(index)"
class="max-w-[400px]"
>
<div class="bg-fill-light w-full p-4 mt-4">
<el-form-item label="导航图标">
<material-picker
v-model="item.unselected"
upload-class="bg-body"
size="60px"
>
<template #upload>
<div class="upload-btn w-[60px] h-[60px]">
<icon name="el-icon-Plus" :size="16" />
<span class="text-xs leading-5">
未选中
</span>
</div>
</template>
</material-picker>
<material-picker
v-model="item.selected"
upload-class="bg-body"
size="60px"
>
<template #upload>
<div class="upload-btn w-[60px] h-[60px]">
<icon name="el-icon-Plus" :size="16" />
<span class="text-xs leading-5">
选中
</span>
</div>
</template>
</material-picker>
</el-form-item>
<el-form-item label="导航名称">
<el-input
v-model="item.name"
placeholder="请输入名称"
/>
</el-form-item>
<el-form-item label="链接地址">
<link-picker v-model="item.link" />
</el-form-item>
</div>
</del-wrap>
</div>
<el-form-item v-if="tabbar.data?.length < max" label-width="0">
<el-button type="primary" @click="handleAdd">
添加导航
</el-button>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="样式设置" name="styles">
<el-form-item label="默认颜色">
<color-picker class="max-w-[400px]" v-model="tabbar.color" />
</el-form-item>
<el-form-item label="选中颜色">
<color-picker class="max-w-[400px]" v-model="tabbar.color" />
</el-form-item>
</el-tab-pane>
</el-tabs>
</el-form>
</div>
</div>
</el-card>
<footer-btns class="mt-4" :fixed="false">
<el-button type="primary" @click="setData">保存</el-button>
</footer-btns>
</div>
</template>
<script lang="ts" setup>
import { getDecorateTabbar, setDecorateTabbar } from '@/api/decoration'
import feedback from '@/utils/feedback'
const max = 5
const min = 2
const tabbar = reactive({
color: '',
data: [
{
name: '',
selected: '',
unselected: '',
link: {}
}
]
})
const handleAdd = () => {
if (tabbar.data?.length < max) {
tabbar.data.push({
name: '',
selected: '',
unselected: '',
link: {}
})
} else {
feedback.msgError(`最多添加${max}`)
}
}
const handleDelete = (index: number) => {
if (tabbar.data?.length <= min) {
return feedback.msgError(`最少保留${min}`)
}
tabbar.data.splice(index, 1)
}
const getData = async () => {
const data = await getDecorateTabbar()
// tabbar.value = data
}
const setData = async () => {
// await setDecorateTabbar(tabbar.value)
getData()
feedback.msgSuccess('保存成功')
}
</script>
<style lang="scss" scoped>
.decoration-tabbar {
min-height: calc(100vh - var(--navbar-height) - 80px);
@apply flex flex-col;
.pages-preview {
background-color: #f7f7f7;
width: 360px;
height: 615px;
color: #333;
position: relative;
.tabbar {
position: absolute;
height: 50px;
background-color: #fff;
bottom: 0;
width: 100%;
border: 2px solid var(--el-color-primary);
}
}
}
</style>