装修,底部导航
This commit is contained in:
parent
aed9b43d5e
commit
116d6d5eff
|
|
@ -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 })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import attr from './attr.vue'
|
||||
import content from './content.vue'
|
||||
import options from './options'
|
||||
export default {
|
||||
attr,
|
||||
content,
|
||||
options
|
||||
}
|
||||
|
|
@ -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像素;图片格式:jpg、png、jpeg</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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import attr from './attr.vue'
|
||||
import content from './content.vue'
|
||||
import options from './options'
|
||||
export default {
|
||||
attr,
|
||||
content,
|
||||
options
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
export default () => ({
|
||||
title: '客服设置',
|
||||
name: 'customer-service',
|
||||
content: {
|
||||
title: '添加客服二维码',
|
||||
time: '',
|
||||
mobile: '',
|
||||
qrcode: ''
|
||||
},
|
||||
styles: {}
|
||||
})
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import attr from './attr.vue'
|
||||
import content from './content.vue'
|
||||
import options from './options'
|
||||
export default {
|
||||
attr,
|
||||
content,
|
||||
options
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
export default () => ({
|
||||
title: '我的服务',
|
||||
name: 'my-service',
|
||||
content: {
|
||||
style: 1,
|
||||
title: '我的服务',
|
||||
data: [
|
||||
{
|
||||
image: '',
|
||||
name: '',
|
||||
link: {}
|
||||
}
|
||||
]
|
||||
},
|
||||
styles: {}
|
||||
})
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import attr from './attr.vue'
|
||||
import content from './content.vue'
|
||||
import options from './options'
|
||||
export default {
|
||||
attr,
|
||||
content,
|
||||
options
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import attr from './attr.vue'
|
||||
import content from './content.vue'
|
||||
import options from './options'
|
||||
export default {
|
||||
attr,
|
||||
content,
|
||||
options
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import attr from './attr.vue'
|
||||
import content from './content.vue'
|
||||
import options from './options'
|
||||
export default {
|
||||
attr,
|
||||
content,
|
||||
options
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
export default () => ({
|
||||
title: '个人中心广告图',
|
||||
name: 'user-banner',
|
||||
content: {
|
||||
enabled: 1,
|
||||
data: [
|
||||
{
|
||||
image: '',
|
||||
name: '',
|
||||
link: {}
|
||||
}
|
||||
]
|
||||
},
|
||||
styles: {}
|
||||
})
|
||||
|
|
@ -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>
|
||||
|
|
@ -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 |
|
|
@ -0,0 +1,8 @@
|
|||
import attr from './attr.vue'
|
||||
import content from './content.vue'
|
||||
import options from './options'
|
||||
export default {
|
||||
attr,
|
||||
content,
|
||||
options
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
export default () => ({
|
||||
title: '用户信息',
|
||||
name: 'user-info',
|
||||
disabled: 1,
|
||||
content: {},
|
||||
styles: {}
|
||||
})
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
Loading…
Reference in New Issue