修改导航组选项卡的效果

v1.1.0
于肖磊 2024-11-25 16:58:35 +08:00
parent 10a0f58ed6
commit eb2a52c672
11 changed files with 372 additions and 60 deletions

View File

@ -1,10 +1,10 @@
<template>
<VueDraggable v-model="from" :animation="500" target=".sort-target" handle=".icon-drag" :scroll="true" :on-sort="on_sort">
<TransitionGroup type="transition" tag="ul" name="fade" class="sort-target flex-col gap-x-20">
<li v-for="(item, index) in from" :key="index" :class="className" class="flex gap-y-16 re" @click="on_click(item, index)">
<li v-for="(item, index) in from" :key="index" :class="[`flex gap-y-16 re ${ className }`, props.modelType == 'nav-group' && modelIndex === index ? 'nav-index-select' : '']" @click="on_click(item, index)">
<icon name="drag" size="16" class="cursor-move" />
<slot :row="item" :index="index" />
<div class="abs c-pointer top-de-5 right-de-5" @click.stop="remove(index)">
<div class="abs c-pointer top-de-6 right-de-6 remove-icon" @click.stop="remove(index)">
<icon v-if="type == 'card'" name="close-fillup" size="18" color="c" />
</div>
<div class="c-pointer do-not-trigger" @click.stop="remove(index)">
@ -34,12 +34,16 @@ interface Props {
spaceCol?: number; //
iconPosition?: string; // top/bottom/center
isShowEdit?: boolean;
modelType?: string;
modelIndex?: number;
}
const props = withDefaults(defineProps<Props>(), {
type: () => 'line',
isShowEdit: false,
spaceCol: () => 5,
iconPosition: 'center',
modelType: 'outer',
modelIndex: 0,
});
const className = ref('');
if (props.type == 'card') {
@ -84,15 +88,26 @@ const on_sort = () => {
emits('onSort', from.value);
};
</script>
<style scoped>
<style lang="scss" scoped>
.card-background {
background: #fff;
padding-left: 1.6rem;
padding-right: 2rem;
border-radius: 4px;
}
.cursor-move {
color: #ddd;
cursor: move;
}
.remove-icon {
display: flex;
background: #fff;
border-radius: 100%;
line-height: 1.8rem;
}
.nav-index-select {
box-shadow: 0rem 0 0rem 0.1rem #409eff;
/* border: 1px solid #409eff; */
}
</style>

View File

@ -27,9 +27,15 @@ const props = defineProps({
return {};
},
},
type: {
type: String,
default: 'outer',
},
});
const form = computed(() => props.value?.content || {});
const new_style = computed(() => props.value?.style || {});
const new_style = computed(() => props.type == 'nav-group' ? { subscript_style: props.value?.style || {} } : props.value?.style || {});
//#region
//
const corner_marker = computed(() => {

View File

@ -1,6 +1,6 @@
<template>
<card-container>
<div class="mb-12">角标设</div>
<div v-if="props.type != 'nav-group'" class="mb-12"></div>
<el-form-item label="角标位置">
<div class="flex-col gap-10">
<el-radio-group v-model="form.seckill_subscript_location">
@ -26,14 +26,14 @@
</template>
<template v-else>
<el-form-item label="内容设置">
<color-text-size-group v-model:color="form.text_or_icon_color" v-model:size="form.text_or_icon_size" :default-color="clone_form.text_or_icon_color" :type-list="['color', 'size']"></color-text-size-group>
<color-text-size-group v-model:color="form.text_or_icon_color" v-model:size="form.text_or_icon_size" :default-color="clone_form.text_or_icon_color" :slider-name="data.subscript_type == 'text' ? '' : ''" :type-list="['color', 'size']"></color-text-size-group>
</el-form-item>
</template>
<el-form-item label="背景">
<div class="flex-col gap-10 w">
<div class="size-12">背景色</div>
<mult-color-picker :value="form.color_list" :type="form.direction" @update:value="mult_color_picker_event"></mult-color-picker>
<div class="flex-row jc-sb align-c">
<div class="flex-col gap-10 jc-sb">
<div class="size-12">背景图</div>
<bg-btn-style v-model="form.background_img_style"></bg-btn-style>
</div>

View File

@ -233,6 +233,7 @@ const tabs_add = () => {
is_cover: '1',
data_list: [],
});
form.tabs_active_index = form.tabs_list.length - 1;
// emit('update:value', form);
};

View File

@ -9,7 +9,7 @@
</div>
</template>
<div v-else-if="data_source_content_list.length > 0 && ['1', '2'].includes(form.data_source_direction)" class="oh" :style="form.data_source_direction == '2' ? `height:100%;` : `height: ${ swiper_height }px;`">
<swiper :key="carouselKey" class="w flex" :direction="form.data_source_direction == '2' ? 'horizontal': 'vertical'" :height="swiper_height" :loop="true" :autoplay="autoplay" :slides-per-view="form.data_source_carousel_col" :slides-per-group="slides_per_group" :allow-touch-move="false" :pause-on-mouse-enter="true" :modules="modules" @slide-change="slideChange">
<swiper :key="carouselKey" class="w flex" :direction="form.data_source_direction == '2' ? 'horizontal': 'vertical'" :height="swiper_height" :loop="true" :autoplay="autoplay" :slides-per-view="slides_per_view" :slides-per-group="slides_per_group" :allow-touch-move="false" :pause-on-mouse-enter="true" :modules="modules" @slide-change="slideChange">
<swiper-slide v-for="(item1, index1) in data_source_content_list" :key="index1">
<div :style="style_chunk_container">
<div class="w h" :style="style_chunk_img_container">
@ -129,6 +129,7 @@ const autoplay = ref<boolean | object>(false);
const slides_per_group = ref(1);
const dot_list = ref<unknown[]>([]);
const swiper_height = ref(390);
const slides_per_view = ref(1);
//
watchEffect(() => {
//
@ -155,6 +156,7 @@ watchEffect(() => {
swiper_height.value = (form.value.height * scale.value + padding_top + padding_bottom + margin_bottom + margin_top) * form.value.data_source_carousel_col;
}
dot_list.value = Array(num);
slides_per_view.value = form.value.data_source_carousel_col;
// key
carouselKey.value = get_math();
});

View File

@ -194,6 +194,7 @@ const tabs_add = () => {
order_by_rule: '0',
data_list: [],
});
form.value.tabs_active_index = form.value.tabs_list.length - 1;
};
//

View File

@ -1,8 +1,21 @@
interface subscript_type {
content: {
seckill_subscript_show: string,
subscript_type: string,
subscript_img_src: uploadList[],
subscript_icon_class: string,
subscript_text: string,
},
style: object;
}
interface nav_group {
id: string;
img: uploadList[];
title: string;
link: object;
tabs_name: string;
subscript: subscript_type;
}
interface nav_group_content {
@ -11,11 +24,6 @@ interface nav_group_content {
display_style: string;
row: number;
nav_content_list: nav_group[];
seckill_subscript_show: string;
subscript_type: string;
subscript_img_src: uploadList[];
subscript_icon_class: string;
subscript_text: string;
}
interface nav_group_styles {

View File

@ -8,7 +8,7 @@
<div v-if="['image_with_text', 'image'].includes(nav_style)" class="top-img flex align-c jc-c re">
<image-empty v-model="item1.img[0]" :style="img_style"></image-empty>
<!-- 角标 -->
<subscript-index :value="props.value"></subscript-index>
<subscript-index :value="item1.subscript" type="nav-group"></subscript-index>
</div>
<p v-if="['image_with_text', 'text'].includes(nav_style)" class="w size-12 ma-0 nowrap oh tc" :style="text_style">{{ item1.title }}</p>
</div>
@ -174,16 +174,25 @@ const nav_content_list = computed(() => {
return [{ split_list: list }];
}
});
//
watch(() => props.value, (val) => {
nextTick(() => {
if (!isEmpty(bannerImg.value)) {
newHeight.value = (bannerImg.value[0]?.clientHeight || 100) + 'px';
}
});
}, {immediate: true, deep: true});
//
const autoplay = ref<boolean | object>(false);
const slides_per_view = ref(1);
//
const group_width = ref('100%');
//
watch(() => props.value, (val) => {
//
watchEffect(() => {
const display_is_roll = form.value.display_style == 'slide' ? new_style.value.is_roll : '0';
const list = form.value?.nav_content_list || [];
//
if (display_is_roll == '1') {
if (display_is_roll == '1' && list.length > 1) {
autoplay.value = {
delay: (new_style.value.interval_time || 2) * 1000,
pauseOnMouseEnter: true,
@ -205,12 +214,7 @@ watch(() => props.value, (val) => {
}
// key
carouselKey.value = get_math();
nextTick(() => {
if (!isEmpty(bannerImg.value)) {
newHeight.value = (bannerImg.value[0]?.clientHeight || 100) + 'px';
}
});
}, {immediate: true, deep: true});
});
const slideChange = (swiper: { realIndex: number }) => {
actived_index.value = swiper.realIndex;
};

View File

@ -36,36 +36,67 @@
<card-container>
<div class="mb-12 flex-row align-c jc-sb">内容设置<span class="classify-style" @click="classify_add"></span></div>
<div class="tips mt-10 mb-20 size-12">最多添加{{ form.nav_content_list.length }}张图片建议尺寸90*90px</div>
<drag :data="form.nav_content_list" type="card" :space-col="27" @remove="remove" @on-sort="on_sort">
<drag :data="form.nav_content_list" type="card" :space-col="27" model-type="nav-group" :model-index="tabs_active_index" @remove="remove" @on-sort="on_sort" @click="tabs_list_click">
<template #default="scoped">
<div class="flex-row align-c jc-c w h">
<upload v-model="scoped.row.img" :limit="1" size="72"></upload>
<div class="flex-col flex-1 jc-c gap-20">
<el-form-item label="标题" class="mb-0" label-width="50">
<el-input v-model="scoped.row.title" placeholder="请输入标题" maxlength="10" show-word-limit clearable></el-input>
</el-form-item>
<el-form-item label="链接" class="w mb-0" label-width="50">
<url-value v-model="scoped.row.link"></url-value>
<div class="flex-col gap-10">
<div class="flex-row align-c jc-c w h">
<upload v-model="scoped.row.img" :limit="1" size="72"></upload>
<div class="flex-col flex-1 jc-c gap-20">
<el-form-item label="标题" class="mb-0" label-width="50">
<el-input v-model="scoped.row.title" placeholder="请输入标题" maxlength="10" show-word-limit clearable></el-input>
</el-form-item>
<el-form-item label="链接" class="w mb-0" label-width="50">
<url-value v-model="scoped.row.link"></url-value>
</el-form-item>
</div>
</div>
<div class="not-label-width flex-col gap-10 w h">
<!-- // -->
<el-form-item label="角标" label-width="40" class="mb-0">
<el-switch v-model="scoped.row.subscript.content.seckill_subscript_show" active-value="1" inactive-value="0"></el-switch>
</el-form-item>
<!-- 内容设置 -->
<template v-if="tabs_active_index == scoped.index">
<el-form v-if="scoped.row.subscript.content.seckill_subscript_show == '1'" :model="scoped.row.subscript.style" label-width="60">
<el-tabs v-model="scoped.row.tabs_name" class="content-tabs">
<el-tab-pane label="内容设置" name="content">
<el-form-item label-width="0">
<div class="flex-col gap-10 w h">
<el-form-item label="类型" label-width="40">
<el-radio-group v-model="scoped.row.subscript.content.subscript_type">
<el-radio value="text">文本</el-radio>
<el-radio value="img-icon">图片或图标</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="scoped.row.subscript.content.subscript_type != 'text'" label-width="40">
<upload v-model="scoped.row.subscript.content.subscript_img_src" v-model:icon-value="scoped.row.subscript.content.subscript_icon_class" is-icon :limit="1" size="50"></upload>
</el-form-item>
<el-form-item v-if="scoped.row.subscript.content.subscript_type == 'text'" label-width="40">
<el-input v-model="scoped.row.subscript.content.subscript_text" placeholder="请输入秒杀文字" clearable></el-input>
</el-form-item>
</div>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="样式设置" name="styles">
<subscript-styles :value="scoped.row.subscript.style" :data="scoped.row.subscript.content" type="nav-group"></subscript-styles>
</el-tab-pane>
</el-tabs>
</el-form>
</template>
</div>
</div>
</template>
</drag>
<el-button class="mt-20 mb-20 w" @click="add">+</el-button>
</card-container>
<div class="divider-line"></div>
<card-container>
<div class="mb-12">角标设置</div>
<!-- 角标设置 -->
<subscript-content :value="form"></subscript-content>
</card-container>
</el-form>
<category-dialog v-model:dialog-visible="dialogVisible" @confirm_event="confirm_event"></category-dialog>
</div>
</template>
<script setup lang="ts">
import { get_math } from "@/utils";
import { isEmpty } from "lodash";
import { clone, isEmpty } from "lodash";
import subscriptStyle from '@/config/const/subscript-style';
interface Props {
value: nav_group_content;
@ -82,32 +113,98 @@ const props = withDefaults(defineProps<Props>(),{
img: [],
title: '',
link: {},
tabs_name: 'content',
//
subscript: {
content: {
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: {
...subscriptStyle,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
}
}
},
{
id: get_math(), // 使使index
img: [],
title: '',
link: {},
tabs_name: 'content',
//
subscript: {
content: {
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: {
...subscriptStyle,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
}
}
},
{
id: get_math(), // 使使index
img: [],
title: '',
link: {},
tabs_name: 'content',
//
subscript: {
content: {
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: {
...subscriptStyle,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
}
}
},
{
id: get_math(), // 使使index
img: [],
title: '',
link: {},
tabs_name: 'content',
//
subscript: {
content: {
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: {
...subscriptStyle,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
}
}
}
],
//
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
})
});
@ -115,16 +212,74 @@ const state = reactive({
form: props.value
});
const { form } = toRefs(state);
const tabs_active_index = ref(0);
onBeforeMount(() => {
tabs_active_index.value = 0;
let nav_content_list = form.value.nav_content_list;
//
nav_content_list = nav_content_list.map(item => ({
...item,
tabs_name: 'content',
//
subscript: isEmpty(item.subscript) ?
{
content: {
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: {
...subscriptStyle,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
}
} : item.subscript,
}));
});
const add = () => {
form.value.nav_content_list.push({
id: get_math(),
img: [],
title: '',
link: {},
tabs_name: 'content',
//
subscript: {
content: {
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: {
...subscriptStyle,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
}
}
});
tabs_active_index.value = form.value.nav_content_list.length - 1;
}
const remove = (index: number) => {
form.value.nav_content_list.splice(index, 1);
if (form.value.nav_content_list.length > 1) {
form.value.nav_content_list.splice(index, 1);
if (form.value.nav_content_list.length > index) {
tabs_active_index.value = index;
} else {
tabs_active_index.value = index - 1;
}
} else {
tabs_active_index.value = 0;
ElMessage.warning('至少保留一个选项卡');
}
}
//
const on_sort = (new_list: nav_group[]) => {
@ -151,10 +306,33 @@ const confirm_event = (list: categoryList[]) => {
name: item.name,
page: `/pages/goods-search/goods-search?category_id=${ item.id }`
},
tabs_name: 'content',
//
subscript: {
content: {
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: {
...subscriptStyle,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
}
}
});
});
}
//#endregion
//#region
const tabs_list_click = (item: any, index: number) => {
tabs_active_index.value = index;
}
//#endregion
</script>
<style lang="scss" scoped>
:deep(.size-12.cr-9.mt-10) {
@ -167,4 +345,28 @@ const confirm_event = (list: categoryList[]) => {
cursor: pointer;
color: $cr-main;
}
.not-label-width {
:deep(.el-form-item__label) {
width: 60px;
}
.card {
padding: 0 !important;
}
}
:deep(.el-tabs.content-tabs) {
.el-tabs__header.is-top {
background: #fff;
margin: 0;
padding-bottom: 1rem;
}
.el-tabs__item.is-top {
padding: 0;
align-items: center;
width: 10rem;
font-size: 1.4rem;
}
.el-tabs__active-bar{
width: 100%;
}
}
</style>

View File

@ -62,10 +62,10 @@
</el-form-item>
</card-container>
<!-- 角标 -->
<template v-if="data_content.seckill_subscript_show == '1'">
<!-- <template v-if="is_show_subscript">
<div class="divider-line"></div>
<subscript-styles :value="form.subscript_style" :data="data_content" type="nav-group"></subscript-styles>
</template>
</template> -->
</el-form>
<div class="divider-line"></div>
<common-styles :value="form.common_style" @update:value="common_styles_update" />

View File

@ -1,11 +1,23 @@
import { get_math } from '@/utils';
import defaultCommon from './index';
import subscriptStyle from './subscript-style';
interface subscript_type {
content: {
seckill_subscript_show: string,
subscript_type: string,
subscript_img_src: uploadList[],
subscript_icon_class: string,
subscript_text: string,
},
style: object;
}
interface nav_group {
id: string;
img: uploadList[];
title: string;
link: object;
tabs_name: string;
subscript: subscript_type;
}
interface defaultSearch {
content: {
@ -14,11 +26,6 @@ interface defaultSearch {
display_style: string;
row: number;
nav_content_list: nav_group[];
seckill_subscript_show: string;
subscript_type: string;
subscript_img_src: uploadList[];
subscript_icon_class: string;
subscript_text: string;
};
style: {
space: number;
@ -66,32 +73,98 @@ const defaultSearch: defaultSearch = {
img: [],
title: '测试标题',
link: {},
tabs_name: 'content',
// 角标配置
subscript: {
content: {
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: {
...subscriptStyle,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
}
}
},
{
id: get_math(), // 唯一标识使用避免使用index作为唯一标识导致渲染节点出现问题
img: [],
title: '测试标题',
link: {},
tabs_name: 'content',
// 角标配置
subscript: {
content: {
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: {
...subscriptStyle,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
}
}
},
{
id: get_math(), // 唯一标识使用避免使用index作为唯一标识导致渲染节点出现问题
img: [],
title: '测试标题',
link: {},
tabs_name: 'content',
// 角标配置
subscript: {
content: {
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: {
...subscriptStyle,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
}
}
},
{
id: get_math(), // 唯一标识使用避免使用index作为唯一标识导致渲染节点出现问题
img: [],
title: '测试标题',
link: {},
tabs_name: 'content',
// 角标配置
subscript: {
content: {
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: {
...subscriptStyle,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
}
}
},
],
// 角标配置
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: {
// 数据间距