mirror of
https://gitee.com/zongzhige/shopxo-diy.git
synced 2026-06-05 18:25:52 +08:00
新增公告样式三
This commit is contained in:
@ -24,7 +24,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-else-if="form.notice_style == 'card'">
|
||||
<div class="news-card" :style="container_background_style">
|
||||
<div class="flex-col gap-10" :style="container_background_img_style">
|
||||
<div class="flex-row w jc-sb">
|
||||
@ -48,12 +48,68 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="form.notice_style == 'marquee'">
|
||||
<div class="news-box news-box-marquee" :style="container_background_style">
|
||||
<div class="news-marquee-row flex-row align-c gap-y-8" :style="container_background_img_style">
|
||||
<div class="flex-shrink-0 flex-row align-c">
|
||||
<template v-if="form.title_type == 'img-icon'">
|
||||
<div v-if="!isEmpty(form.img_src)">
|
||||
<image-empty v-model="form.img_src[0]" :style="img_style"></image-empty>
|
||||
</div>
|
||||
<div v-else>
|
||||
<icon :name="form.icon_class" :size="new_style.icon_size + ''" :color="new_style.icon_color"></icon>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div :style="topic_style" class="pl-6 pr-6 radius-sm">{{ form.title }}</div>
|
||||
</template>
|
||||
</div>
|
||||
<template v-if="marquee_scroll_enabled">
|
||||
<!-- 与销售记录横向「平移」一致:free-mode + linear + delay:0 + speed 用间隔时间 -->
|
||||
<div class="news-marquee-viewport flex-1 flex-width swiper-free-mode swiper-horizontal-free-mode">
|
||||
<swiper
|
||||
:key="marquee_swiper_key"
|
||||
class="news-marquee-swiper w flex"
|
||||
direction="horizontal"
|
||||
:modules="swiper_modules"
|
||||
:loop="true"
|
||||
slides-per-view="auto"
|
||||
:space-between="marquee_scroll_gap"
|
||||
:allow-touch-move="false"
|
||||
:autoplay="marquee_swiper_autoplay"
|
||||
:speed="marquee_swiper_speed"
|
||||
>
|
||||
<swiper-slide v-for="i in marquee_swiper_slides" :key="i" class="news-marquee-slide">
|
||||
<div class="news-marquee-slide-inner flex-row align-c">
|
||||
<div class="news-marquee-slide-text news-marquee-slide-text--scroll" :style="`${news_style} color: ${new_style.news_color}`">{{ marquee_display_text }}</div>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
</swiper>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="news-marquee-static flex-1 flex-width">
|
||||
<div class="news-marquee-static-text" :style="`${news_style} color: ${new_style.news_color}`">{{ marquee_display_text }}</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="form.is_right_button == '1'"
|
||||
class="flex-shrink-0 flex-row align-c"
|
||||
:style="`color: ${new_style.right_button_color}; font-size: ${new_style.right_button_size}px`"
|
||||
>
|
||||
{{ form.right_title }}<icon name="arrow-right" :color="new_style.right_button_color || '#999'"></icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { background_computer, common_img_computer, common_styles_computer, get_math, gradient_computer, gradient_handle, padding_computer, radius_computer } from '@/utils';
|
||||
import { isEmpty, cloneDeep, throttle } from 'lodash';
|
||||
import { isEmpty, cloneDeep } from 'lodash';
|
||||
import { Swiper, SwiperSlide } from 'swiper/vue';
|
||||
import { Autoplay } from 'swiper/modules';
|
||||
|
||||
const swiper_modules = [Autoplay];
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
@ -101,7 +157,7 @@ const container_background_img_style = computed(() => {
|
||||
} else {
|
||||
// 为空的时候使用默认数据,兼容老数据没有这个值时的处理
|
||||
let old_padding = { padding: 15, padding_top: 15, padding_right: 15, padding_bottom: 15, padding_left: 15 };
|
||||
if (form.value.notice_style === 'inherit') {
|
||||
if (form.value.notice_style === 'inherit' || form.value.notice_style === 'marquee') {
|
||||
old_padding = { padding: 0, padding_top: 0, padding_right: 10, padding_bottom: 0, padding_left: 10, }
|
||||
}
|
||||
padding = padding_computer(old_padding);
|
||||
@ -139,10 +195,53 @@ const notice_list = computed(() => {
|
||||
return arry_list.filter((item: { is_show: string }) => item.is_show == '1');
|
||||
});
|
||||
|
||||
const marquee_scroll_enabled = computed(() => form.value.marquee_scroll != '0');
|
||||
|
||||
const marquee_display_text = computed(() => {
|
||||
const raw = String(form.value.marquee_content ?? '').trim();
|
||||
if (raw) return raw;
|
||||
const first = notice_list.value[0]?.notice_title;
|
||||
if (first && String(first).trim()) return String(first).trim();
|
||||
return '暂无公告';
|
||||
});
|
||||
|
||||
/** 样式三:每条 slide 一整段文案(宽度随内容),多份 slide + space-between 满足 loop 与间距 */
|
||||
const marquee_repeat = 8;
|
||||
const marquee_swiper_slides = computed(() => Array.from({ length: marquee_repeat }, (_, i) => i));
|
||||
|
||||
const marquee_swiper_key = computed(
|
||||
() =>
|
||||
`notice-marquee-${marquee_display_text.value}-${form.value.interval_time}-${form.value.marquee_scroll}-${form.value.notice_style}`
|
||||
);
|
||||
|
||||
/** 与销售记录横向 translation 一致:delay 0 + waitForTransition,靠 speed 控制平移快慢 */
|
||||
const marquee_swiper_autoplay = computed(() => {
|
||||
if (!marquee_scroll_enabled.value) return false;
|
||||
return {
|
||||
delay: 0,
|
||||
waitForTransition: true,
|
||||
pauseOnMouseEnter: true,
|
||||
disableOnInteraction: false,
|
||||
};
|
||||
});
|
||||
|
||||
/** 销售记录横向:swiper_speed = interval_time * 1000(ms) */
|
||||
const marquee_swiper_speed = computed(() => {
|
||||
const sec = Number(form.value.interval_time) > 0 ? Number(form.value.interval_time) : 3;
|
||||
return sec * 1000;
|
||||
});
|
||||
|
||||
/** 开启滚动时,每条 slide 之间的间距(与销售记录 data_spacing 思路一致) */
|
||||
const marquee_scroll_gap = computed(() => {
|
||||
const n = Number(new_style.value?.data_spacing);
|
||||
if (Number.isFinite(n) && n > 0) return n;
|
||||
return 32;
|
||||
});
|
||||
|
||||
// 内容参数的集合
|
||||
watchEffect(() => {
|
||||
//#region 轮播图设置
|
||||
const time = (new_style.value?.interval_time || 2) * 1000;
|
||||
const time = (Number(form.value.interval_time) > 0 ? Number(form.value.interval_time) : 3) * 1000;
|
||||
const direction = form.value.direction;
|
||||
// 判断长度是否相等
|
||||
const notice_length = notice_list.value.length;
|
||||
@ -169,6 +268,11 @@ watchEffect(() => {
|
||||
height: v-bind(container_height);
|
||||
overflow: hidden;
|
||||
}
|
||||
.news-box-marquee {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.num {
|
||||
padding-right: 0.7rem;
|
||||
color: #999;
|
||||
@ -197,4 +301,73 @@ watchEffect(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.news-marquee-row {
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
.news-marquee-viewport {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
position: relative;
|
||||
/* 供 slide 使用 cqi:每条至少占满中间区域宽度,短文案不再并排「切片」 */
|
||||
container-type: inline-size;
|
||||
container-name: news-marquee-vp;
|
||||
}
|
||||
.news-marquee-viewport.swiper-free-mode :deep(.swiper-wrapper) {
|
||||
transition-timing-function: linear !important;
|
||||
}
|
||||
.news-marquee-swiper {
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
.news-marquee-swiper :deep(.swiper),
|
||||
.news-marquee-swiper :deep(.swiper-wrapper) {
|
||||
height: 100%;
|
||||
}
|
||||
.news-marquee-swiper :deep(.swiper-wrapper) {
|
||||
align-items: center;
|
||||
}
|
||||
.news-marquee-swiper :deep(.swiper-slide) {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
/* 短文案:slide 至少一屏宽,视口内只看到一整条;长文案:随 max-content 变宽 */
|
||||
min-width: 100%;
|
||||
min-width: 100cqi;
|
||||
}
|
||||
.news-marquee-slide-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: max-content;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* 开启滚动:整段文案单行完整展示,不用省略号 */
|
||||
.news-marquee-slide-text--scroll {
|
||||
width: max-content;
|
||||
max-width: none;
|
||||
white-space: nowrap;
|
||||
overflow: visible;
|
||||
}
|
||||
.news-marquee-static {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
}
|
||||
.news-marquee-static-text {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
<el-radio-group v-model="form.notice_style" @change="change_style">
|
||||
<el-radio value="inherit">样式一</el-radio>
|
||||
<el-radio value="card">样式二</el-radio>
|
||||
<el-radio value="marquee">样式三</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</card-container>
|
||||
@ -28,15 +29,25 @@
|
||||
<el-input v-model="form.title" placeholder="请输入标题" maxlength="30" clearable></el-input>
|
||||
</el-form-item>
|
||||
<template v-if="!is_card">
|
||||
<el-form-item label="滚动方式">
|
||||
<el-radio-group v-model="form.direction">
|
||||
<el-radio value="vertical">上下滚动</el-radio>
|
||||
<el-radio value="horizontal">左右滚动</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="间隔时间">
|
||||
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
|
||||
</el-form-item>
|
||||
<template v-if="is_marquee">
|
||||
<el-form-item label="开启滚动">
|
||||
<el-switch v-model="form.marquee_scroll" active-value="1" inactive-value="0"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.marquee_scroll === '1'" label="间隔时间">
|
||||
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-form-item label="滚动方式">
|
||||
<el-radio-group v-model="form.direction">
|
||||
<el-radio value="vertical">上下滚动</el-radio>
|
||||
<el-radio value="horizontal">左右滚动</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="间隔时间">
|
||||
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</template>
|
||||
</card-container>
|
||||
<div class="bg-f5 divider-line" />
|
||||
@ -57,22 +68,37 @@
|
||||
<div class="bg-f5 divider-line" />
|
||||
<card-container>
|
||||
<div class="mb-12">内容设置</div>
|
||||
<drag :data="form.notice_list" type="card" :space-col="25" @remove="remove" @on-sort="on_sort">
|
||||
<template #default="scoped">
|
||||
<div class="flex-col align-c jc-s gap-20 flex-1">
|
||||
<el-form-item label="标题" class="w mb-0" label-width="40">
|
||||
<el-input v-model="scoped.row.notice_title" placeholder="请输入标题" maxlength="100" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="链接" class="w mb-0" label-width="40">
|
||||
<url-value v-model="scoped.row.notice_link"></url-value>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" class="w mb-0" label-width="40">
|
||||
<el-switch v-model="scoped.row.is_show" active-value="1" inactive-value="0"></el-switch>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
</drag>
|
||||
<el-button class="mt-20 mb-20 w" @click="add">+添加</el-button>
|
||||
<template v-if="is_marquee">
|
||||
<el-form-item label="公告内容" class="mb-0">
|
||||
<el-input
|
||||
v-model="form.marquee_content"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入公告文字"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
clearable
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<drag :data="form.notice_list" type="card" :space-col="25" @remove="remove" @on-sort="on_sort">
|
||||
<template #default="scoped">
|
||||
<div class="flex-col align-c jc-s gap-20 flex-1">
|
||||
<el-form-item label="标题" class="w mb-0" label-width="40">
|
||||
<el-input v-model="scoped.row.notice_title" placeholder="请输入标题" maxlength="100" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="链接" class="w mb-0" label-width="40">
|
||||
<url-value v-model="scoped.row.notice_link"></url-value>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" class="w mb-0" label-width="40">
|
||||
<el-switch v-model="scoped.row.is_show" active-value="1" inactive-value="0"></el-switch>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
</drag>
|
||||
<el-button class="mt-20 mb-20 w" @click="add">+添加</el-button>
|
||||
</template>
|
||||
</card-container>
|
||||
</el-form>
|
||||
</div>
|
||||
@ -87,10 +113,11 @@ const props = defineProps({
|
||||
noticeStyle: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const is_card = computed(() => form.value.notice_style == 'card');
|
||||
const is_marquee = computed(() => form.value.notice_style == 'marquee');
|
||||
const is_text = computed(() => form.value.title_type == 'text');
|
||||
|
||||
const state = reactive({
|
||||
@ -99,15 +126,31 @@ const state = reactive({
|
||||
});
|
||||
const { form, new_style } = toRefs(state);
|
||||
|
||||
if (form.value.marquee_content == null || form.value.marquee_content === undefined) {
|
||||
form.value.marquee_content = '';
|
||||
}
|
||||
if (form.value.marquee_scroll == null || form.value.marquee_scroll === undefined) {
|
||||
form.value.marquee_scroll = '1';
|
||||
}
|
||||
|
||||
const sync_marquee_from_list = () => {
|
||||
const t = String(form.value.marquee_content ?? '').trim();
|
||||
if (t) return;
|
||||
const row = form.value.notice_list?.find((i: { is_show?: string; notice_title?: string }) => i.is_show == '1' && String(i.notice_title ?? '').trim());
|
||||
if (row?.notice_title) {
|
||||
form.value.marquee_content = String(row.notice_title).trim();
|
||||
}
|
||||
};
|
||||
|
||||
const change_style = (val: string | number | boolean | undefined) => {
|
||||
if (val === 'inherit') {
|
||||
if (val === 'inherit' || val === 'marquee') {
|
||||
new_style.value.container_padding = {
|
||||
padding: 0,
|
||||
padding_top: 0,
|
||||
padding_right: 10,
|
||||
padding_bottom: 0,
|
||||
padding_left: 10,
|
||||
}
|
||||
};
|
||||
} else {
|
||||
new_style.value.container_padding = {
|
||||
padding: 15,
|
||||
@ -115,7 +158,12 @@ const change_style = (val: string | number | boolean | undefined) => {
|
||||
padding_right: 15,
|
||||
padding_bottom: 15,
|
||||
padding_left: 15,
|
||||
}
|
||||
};
|
||||
}
|
||||
if (val === 'marquee') {
|
||||
sync_marquee_from_list();
|
||||
// 仅切换到样式三时统一左侧图标为喇叭,样式一/二不改动
|
||||
form.value.icon_class = 'speaker';
|
||||
}
|
||||
};
|
||||
const add = () => {
|
||||
@ -130,10 +178,15 @@ const remove = (index: number) => {
|
||||
form.value.notice_list.splice(index, 1);
|
||||
};
|
||||
|
||||
// 拖拽更新之后,更新数据
|
||||
const on_sort = (new_list: nav_group[]) => {
|
||||
form.value.notice_list = new_list;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (is_marquee.value) {
|
||||
sync_marquee_from_list();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
:deep(.size-12.cr-9.mt-10) {
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<flex-gradients-create :color-list="form.title_color_list"></flex-gradients-create>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-else-if="is_img">
|
||||
<template v-if="!isEmpty(substance.icon_class)">
|
||||
<el-form-item label="左侧图标">
|
||||
<div class="flex-col w gap-10">
|
||||
@ -54,7 +54,7 @@
|
||||
<div class="bg-f5 divider-line" />
|
||||
<card-container>
|
||||
<div class="mb-12">容器设置</div>
|
||||
<template v-if="substance.notice_style === 'inherit'">
|
||||
<template v-if="substance.notice_style === 'inherit' || substance.notice_style === 'marquee'">
|
||||
<el-form-item label="高度">
|
||||
<slider v-model="form.container_height" :max="1000"></slider>
|
||||
</el-form-item>
|
||||
@ -91,11 +91,10 @@ const state = reactive({
|
||||
form: props.value,
|
||||
substance: props.content,
|
||||
});
|
||||
// 如果需要解构,确保使用toRefs
|
||||
const { form, substance } = toRefs(state);
|
||||
|
||||
const is_img = computed(() => substance.value.title_type == 'img-icon');
|
||||
// 通用样式处理
|
||||
|
||||
const common_styles_update = (val: Object) => {
|
||||
form.value.common_style = val;
|
||||
};
|
||||
|
||||
@ -16,6 +16,13 @@ interface defaultSearch {
|
||||
title: string;
|
||||
direction: string;
|
||||
img_src: uploadList[];
|
||||
/** 样式三:单条公告文案(替代 notice_list) */
|
||||
marquee_content: string;
|
||||
/** 样式三:是否开启横向滚动(1/0) */
|
||||
marquee_scroll: string;
|
||||
/** 兼容旧数据,样式三已不再使用 */
|
||||
notice_left_img: uploadList[];
|
||||
notice_right_img: uploadList[];
|
||||
icon_class: string;
|
||||
right_title: string;
|
||||
more_link: object;
|
||||
@ -38,6 +45,9 @@ interface defaultSearch {
|
||||
container_height: number;
|
||||
icon_size: number;
|
||||
icon_color: string;
|
||||
/** 兼容旧数据 */
|
||||
marquee_slot_width: number;
|
||||
marquee_slot_height: number;
|
||||
container_color_list: color_list[],
|
||||
container_direction: string,
|
||||
container_background_img_style: string,
|
||||
@ -58,6 +68,10 @@ const defaultSearch: defaultSearch = {
|
||||
title_type: 'img-icon',
|
||||
title: '测试标题',
|
||||
img_src: [],
|
||||
marquee_content: '',
|
||||
marquee_scroll: '1',
|
||||
notice_left_img: [],
|
||||
notice_right_img: [],
|
||||
// 滚动方式
|
||||
direction: 'vertical',
|
||||
interval_time: 3,
|
||||
@ -100,6 +114,8 @@ const defaultSearch: defaultSearch = {
|
||||
title_height: 24,
|
||||
icon_size: 12,
|
||||
icon_color: '#999',
|
||||
marquee_slot_width: 24,
|
||||
marquee_slot_height: 24,
|
||||
right_button_color: '#999',
|
||||
right_button_size: 12,
|
||||
// 容器高度
|
||||
|
||||
Reference in New Issue
Block a user