新增销售记录组件

v1.3.0
于肖磊 2025-03-12 18:22:17 +08:00
parent ebd177f114
commit a250a44961
5 changed files with 639 additions and 0 deletions

View File

@ -0,0 +1,285 @@
<template>
<div class="oh" :style="style_container">
<div class="swiper-free-mode" :style="style_img_container + `height: ${ swiper_outer_height }px;`">
<template v-if="form.rotation_direction == 'vertical'">
<swiper :key="carouselKey" class="w flex" :style="`height: ${ swiper_height }px;`" direction="vertical" :loop="true" :speed="swiper_speed" :autoplay="autoplay" :slides-per-view="slides_per_view" :space-between="space_between" :modules="modules">
<swiper-slide v-for="(item, index) in list" :key="index">
<div class="flex-row align-c gap-5">
<template v-if="!isEmpty(item) && is_show('head')">
<div class="oh re">
<template v-if="!isEmpty(item.new_cover)">
<image-empty v-model="item.new_cover[0]" :style="heading_img_radius"></image-empty>
</template>
<template v-else>
<image-empty v-model="item.head_img" :style="heading_img_radius"></image-empty>
</template>
</div>
</template>
<span v-if="is_show('nick_name')" class="flex-1 text-line-1" :style="trends_config('nick_name')">{{ item.nick_name }}</span>
<template v-if="is_show('goods_image')">
<image-empty v-model="item.goods_images" :style="goods_image_radius"></image-empty>
</template>
<span v-if="is_show('goods_title')" class="flex-1 text-line-1" :style="trends_config('goods_title')">{{ item.goods_title }}</span>
<span v-if="is_show('time')" class="nowarp" :style="trends_config('time')">{{ item.time }}</span>
</div>
</swiper-slide>
</swiper>
</template>
<template v-else>
<div class="swiper-horizontal-free-mode" :style="`height: ${ swiper_height }px;`">
<div v-for="(item, index) in new_list" :key="index" :style="`margin-bottom: ${ index < new_list.length - 1 ? space_between : 0 }px;`">
<swiper :key="carouselKey + index" class="w flex" :style="`height: ${ new_swiper_height }px;`" direction="horizontal" :loop="true" :slides-offset-before="slides_offset_before" :speed="swiper_speed + (1000 * index)" :autoplay="autoplay" slides-per-view="auto" :space-between="space_between" :modules="modules">
<swiper-slide v-for="(item1, index1) in item.split_list" :key="index1">
<div :style="swiper_horizontal_container + 'width: auto;'">
<div class="flex-row align-c gap-5" :style="swiper_horizontal_img_container">
<template v-if="is_show('goods_image')">
<image-empty v-model="item1.goods_images" :style="goods_image_radius"></image-empty>
</template>
<span v-if="is_show('goods_title')" class="flex-1 text-line-1" :style="trends_config('goods_title') + `max-width: ${ max_title_width }px;`">{{ item1.goods_title }}</span>
<span v-if="is_show('time')" class="nowarp" :style="trends_config('time')">{{ item1.time }}</span>
</div>
</div>
</swiper-slide>
</swiper>
</div>
</div>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import { common_styles_computer, common_img_computer, get_math, gradient_handle, radius_computer, background_computer, padding_computer } from '@/utils';
import { isEmpty, cloneDeep } from 'lodash';
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Autoplay } from 'swiper/modules';
const modules = [Autoplay];
/**
* @description: 文章列表渲染
* @param value{Object} 样式数据
* @param isCommonStyle{Object} 是否为通用样式
*/
const props = defineProps({
value: {
type: Object,
default: () => ({}),
},
isCommonStyle: {
type: Boolean,
default: true,
},
});
const form = computed(() => props.value?.content || {});
const new_style = computed(() => props.value?.style || {});
//
const heading_img_radius = computed(() => {
const { heading_img_radius, heading_img_width, heading_img_height, heading_img_border_color, heading_img_border_size } = new_style.value
return `width: ${ heading_img_width }px;height: ${ heading_img_height }px;border: ${ heading_img_border_size }px solid ${ heading_img_border_color };` + radius_computer(heading_img_radius);
});
const goods_image_radius = computed(() => {
const { goods_img_radius, goods_img_width, goods_img_height, goods_img_border_color, goods_img_border_size } = new_style.value
return `width: ${ goods_img_width }px;height: ${ goods_img_height }px;border: ${ goods_img_border_size }px solid ${ goods_img_border_color };` + radius_computer(goods_img_radius);
});
//
const trends_config = (key: string) => {
return style_config(new_style.value[`${key}_typeface`], new_style.value[`${key}_size`], new_style.value[`${key}_color`]);
};
//
const style_config = (typeface: string, size: number, color: string | object) => {
return `font-weight:${typeface}; font-size: ${size}px;line-height: ${ size }px;color: ${color};`;
};
//
const is_show = (index: string) => {
return form.value.is_show.includes(index);
};
//
const swiper_horizontal_container = computed(() => {
const { content_color_list, content_direction, content_radius } = new_style.value
return gradient_handle(content_color_list, content_direction) + radius_computer(content_radius);
});
//
const swiper_horizontal_img_container = computed(() => {
const { content_background_img_style, content_background_img, content_padding } = new_style.value;
const data = {
background_img_style: content_background_img_style,
background_img: content_background_img,
}
return background_computer(data) + padding_computer(content_padding);
})
//
const style_container = computed(() => common_styles_computer(new_style.value.common_style));
const style_img_container = computed(() => common_img_computer(new_style.value.common_style));
//#region
type data_list = {
head_img: string,
new_cover: string[],
nick_name: string,
goods_title: string,
goods_images: string,
time: string
}
const default_list = {
head_img: '',
new_cover: [],
nick_name: '测试昵称测试昵称测试昵称测试昵称',
goods_title: '测试商品标题测试',
goods_images: '',
time: '02-04 23:01:01'
};
type split_list = {
split_list: data_list[]
}
const new_list = ref<split_list[]>([]);
const list = ref<data_list[]>([]);
//
onMounted(() => {
//
if (!isEmpty(form.value.data_list) && form.value.data_type == '0') {
list.value = form.value.data_list.map((item: any) => ({
...item.data,
title: !isEmpty(item.new_title) ? item.new_title : item.data.title,
new_cover: item.new_cover,
}));
} else if (!isEmpty(form.value.data_auto_list) && form.value.data_type == '1') {
//
list.value = form.value.data_auto_list;
} else {
list.value = Array(50).fill(default_list);
}
});
const get_products = () => {
const { category_ids, brand_ids, number, order_by_type, order_by_rule, keywords } = form.value;
const params = {
goods_keywords: keywords,
goods_category_ids: category_ids,
goods_brand_ids: brand_ids,
goods_order_by_type: order_by_type,
goods_order_by_rule: order_by_rule,
goods_number: number,
};
list.value = Array(50).fill(default_list);
//
// ShopAPI.getShopLists(params).then((res: any) => {
// if (!isEmpty(res.data)) {
// list.value = res.data;
// } else {
// list.value = Array(4).fill(default_list);
// }
// });
};
//
const watch_data = computed(() => {
const { category_ids, brand_ids, number, order_by_type, order_by_rule, data_type, data_list, keywords } = form.value;
return { category_ids: category_ids, brand_ids: brand_ids, number: number, order_by_type: order_by_type, order_by_rule: order_by_rule, data_type: data_type, data_list: data_list, keyword: keywords };
})
// ,
watch(() => watch_data.value, (val, oldVal) => {
// 使JSON.stringify()
if ((JSON.stringify(val) !== JSON.stringify(oldVal)) || props.isCommonStyle) {
if (val.data_type == '0') {
if (!isEmpty(val.data_list)) {
list.value = cloneDeep(val.data_list).map((item: any) => ({
...item.data,
title: !isEmpty(item.new_title) ? item.new_title : item.data.title,
new_cover: item.new_cover,
}));
} else {
list.value = Array(4).fill(default_list);
}
} else {
get_products();
}
}
}, { deep: true });
//#endregion
//#region
// key
const carouselKey = ref('0');
//
const autoplay = ref<boolean | object>(false);
//
const slides_per_view = ref(1);
//
const space_between = ref(0);
//
const swiper_height = ref(0);
//
const swiper_outer_height = ref(0);
//
const swiper_speed = ref(0);
//
const max_title_width = ref(0);
//
const new_swiper_height = ref(0);
//
const slides_offset_before = ref(0);
const size_handle = (val: number, type: string) => {
return form.value.is_show.includes(type) ? val : 0;
};
//
watchEffect(() => {
const { rotation_direction, interval_time, show_number = 1, is_roll, is_show } = form.value;
//
if (is_roll == '1' && list.value.length > 0) {
autoplay.value = {
delay: 0,
waitForTransition: true,
pauseOnMouseEnter: true,
};
} else {
autoplay.value = false;
}
//
space_between.value = new_style.value.data_spacing;
//
const show_num = show_number || 1;
slides_per_view.value = show_num;
//
swiper_speed.value = interval_time * 1000;
//
const { heading_img_height, nick_name_size, goods_img_height, goods_title_size, time_size, data_spacing, common_style, content_padding } = new_style.value;
if (rotation_direction == 'vertical') {
swiper_height.value = Math.max(size_handle(heading_img_height, 'head'), size_handle(nick_name_size, 'nick_name'), size_handle(goods_img_height, 'goods_image'), size_handle(goods_title_size, 'goods_title'), size_handle(time_size, 'time')) * show_num + (data_spacing * (show_num - 1));
} else {
//
slides_offset_before.value = 390 - common_style.margin_left + common_style.margin_right - common_style.padding_left - common_style.padding_right;
max_title_width.value = goods_title_size * 9;
new_list.value = [];
//
const split_num = Math.ceil(list.value.length / show_num);
let new_num = show_num;
for (let i = 0; i < show_num; i++) {
if (!isEmpty(list.value[i * split_num])) {
new_list.value.push({ split_list: list.value.slice(i * split_num, (i + 1) * split_num) });
} else {
new_num = i - 1;
break;
}
}
// swiper
new_swiper_height.value = Math.max(size_handle(goods_img_height, 'goods_image'), size_handle(goods_title_size, 'goods_title'), size_handle(time_size, 'time')) + content_padding.padding_top + content_padding.padding_bottom;
//
swiper_height.value = new_swiper_height.value * new_num + (data_spacing * (new_num - 1));
}
swiper_outer_height.value = swiper_height.value + common_style.padding_top + common_style.padding_bottom;
// key
carouselKey.value = get_math();
});
//#endregion
</script>
<style lang="scss" scoped>
:deep(.el-image) {
background-color: #fff;
.image-slot img {
width: 2rem;
height: 2rem;
}
}
.swiper-free-mode :deep(.swiper-wrapper) {
transition-timing-function: linear !important;
}
.swiper-horizontal-free-mode :deep(.swiper-slide) {
width: auto !important;
}
</style>

View File

@ -0,0 +1,74 @@
<template>
<div class="content">
<el-form :model="form" label-width="75" class="m-h">
<common-content-top :value="form.content_top"></common-content-top>
<div class="divider-line"></div>
<card-container>
<div class="mb-12">展示设置</div>
<el-form-item label="自动轮播">
<el-switch v-model="form.is_roll" active-value="1" inactive-value="0" />
</el-form-item>
<el-form-item label="轮播方向">
<el-radio-group v-model="form.rotation_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="50"></slider>
</el-form-item>
</card-container>
<div class="divider-line"></div>
<card-container class="card-container-br">
<div class="mb-12">自动读取</div>
<el-form-item label="读取数量">
<el-input-number v-model="form.show_number" :min="1" :max="50" type="number" placeholder="请输入读取数量" value-on-clear="min" class="w number-show" controls-position="right"></el-input-number>
</el-form-item>
</card-container>
<div class="divider-line"></div>
<card-container>
<div class="mb-12">显示内容</div>
<el-form-item label="商品显示">
<el-checkbox-group v-model="form.is_show">
<el-checkbox v-for="item in base_list.filter(item => item.type.includes(form.rotation_direction))" :key="item.value" :value="item.value">{{ item.name }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
</card-container>
</el-form>
</div>
</template>
<script setup lang="ts">
import { commonStore } from '@/store';
/**
* @description 博客列表内容
* @param value{Object} 传过来的数据用于数据渲染
* @param styles{Object} 样式
*/
const props = defineProps({
value: {
type: Object,
default: () => ({}),
},
styles: {
type: Object,
default: () => ({}),
}
});
//
const state = reactive({
form: props.value,
data: props.styles,
});
// 使toRefs
const { form, data } = toRefs(state);
const base_list = reactive([
{ name: '头像', value: 'head', type: ['vertical'] },
{ name: '昵称', value: 'nick_name', type: ['vertical'] },
{ name: '商品图', value: 'goods_image', type: ['vertical', 'horizontal'] },
{ name: '商品标题', value: 'goods_title', type: ['vertical', 'horizontal'] },
{ name: '日期时间', value: 'time', type: ['vertical', 'horizontal']},
]);
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,27 @@
<template>
<div class="setting-content">
<template v-if="type == '1'">
<model-salerecords-content :value="props.value.content" :styles="props.value.style"></model-salerecords-content>
</template>
<template v-else-if="type == '2'">
<model-salerecords-styles :value="props.value.style" :content="props.value.content"></model-salerecords-styles>
</template>
</div>
</template>
<script setup lang="ts">
/**
* @description: 文章选项卡列表设置
* @param type{String} 类型是进入内容组件还是样式组件
* @param value{Object} 样式数据
*/
const props = defineProps({
type: {
type: String,
default: '1',
},
value: {
type: Object,
default: () => ({}),
},
});
</script>

View File

@ -0,0 +1,111 @@
<template>
<div class="h">
<el-form :model="form" label-width="75">
<card-container>
<div class="mb-12">内容样式</div>
<template v-if="data.rotation_direction == 'vertical'">
<el-form-item v-if="data.is_show.includes('head')" label="头像">
<div class="flex-col gap-10 w h">
<el-form-item label="宽度" label-width="60" class="form-item-child-label">
<slider v-model="form.heading_img_width" :max="500"></slider>
</el-form-item>
<el-form-item label="高度" label-width="60" class="form-item-child-label">
<slider v-model="form.heading_img_height" :max="500"></slider>
</el-form-item>
<el-form-item label="边线颜色" label-width="60" class="form-item-child-label">
<color-picker v-model="form.heading_img_border_color" :default-color="clone_form.end_text_color"></color-picker>
</el-form-item>
<el-form-item label="边线大小" label-width="60" class="form-item-child-label">
<slider v-model="form.heading_img_border_size" :max="50"></slider>
</el-form-item>
<el-form-item label="圆角" label-width="60" class="form-item-child-label">
<radius :value="form.heading_img_radius"></radius>
</el-form-item>
</div>
</el-form-item>
<el-form-item v-if="data.is_show.includes('nick_name')" label="昵称">
<color-text-size-group v-model:color="form.nick_name_color" v-model:typeface="form.nick_name_typeface" v-model:size="form.nick_name_size" default-color="#000000"></color-text-size-group>
</el-form-item>
</template>
<el-form-item v-if="data.is_show.includes('goods_image')" label="商品图片">
<div class="flex-col gap-10 w h">
<el-form-item label="宽度" label-width="60" class="form-item-child-label">
<slider v-model="form.goods_img_width" :max="500"></slider>
</el-form-item>
<el-form-item label="高度" label-width="60" class="form-item-child-label">
<slider v-model="form.goods_img_height" :max="500"></slider>
</el-form-item>
<el-form-item label="边线颜色" label-width="60" class="form-item-child-label">
<color-picker v-model="form.goods_img_border_color" :default-color="clone_form.end_text_color"></color-picker>
</el-form-item>
<el-form-item label="边线大小" label-width="60" class="form-item-child-label">
<slider v-model="form.goods_img_border_size" :max="50"></slider>
</el-form-item>
<el-form-item label="圆角" label-width="60" class="form-item-child-label">
<radius :value="form.goods_img_radius"></radius>
</el-form-item>
</div>
</el-form-item>
<el-form-item v-if="data.is_show.includes('goods_title')" label="商品标题">
<color-text-size-group v-model:color="form.goods_title_color" v-model:typeface="form.goods_title_typeface" v-model:size="form.goods_title_size" default-color="#000000"></color-text-size-group>
</el-form-item>
<el-form-item v-if="data.is_show.includes('time')" label="日期时间">
<color-text-size-group v-model:color="form.time_color" v-model:typeface="form.time_typeface" v-model:size="form.time_size" default-color="#000000"></color-text-size-group>
</el-form-item>
<el-form-item label="数据间距">
<slider v-model="form.data_spacing" :max="100"></slider>
</el-form-item>
<template v-if="data.rotation_direction !== 'vertical'">
<el-form-item label="内容背景">
<background-common v-model:color_list="form.content_color_list" v-model:direction="form.content_direction" v-model:img_style="form.content_background_img_style" v-model:img="form.content_background_img" @mult_color_picker_event="mult_color_picker_event" />
</el-form-item>
<el-form-item label="内间距">
<padding :value="form.content_padding"></padding>
</el-form-item>
<el-form-item label="圆角">
<radius :value="form.content_radius"></radius>
</el-form-item>
</template>
</card-container>
<div class="divider-line"></div>
<common-styles :value="form.common_style" @update:value="common_style_update" />
</el-form>
</div>
</template>
<script setup lang="ts">
import { isEmpty, cloneDeep } from "lodash";
/**
* @description: 组合搭配列表样式
* @param value{Object} 样式数据
* @param content{Object} 内容数据
* @param defaultConfig{Object} 默认配置
*/
const props = defineProps({
value: {
type: Object,
default: () => ({}),
},
content: {
type: Object,
default: () => ({}),
},
});
//
const state = reactive({
form: props.value,
data: props.content,
});
// 使toRefs
const { form, data } = toRefs(state);
let clone_form = cloneDeep(props.value);
//
const mult_color_picker_event = (arry: color_list[], type: number) => {
form.value.content_color_list = arry;
form.value.content_direction = type;
};
const common_style_update = (value: any) => {
form.value.common_style = value;
};
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,142 @@
import defaultCommon from "./index";
import commonTop from './common-top';
import { cloneDeep } from "lodash";
interface defaultSalerecords {
content: {
content_top: object;
is_roll: string;
rotation_direction: string;
interval_time: number;
show_number: number;
is_show: string[];
};
style: {
heading_img_width: number;
heading_img_height: number;
heading_img_border_color: string;
heading_img_border_size: number;
heading_img_radius: {
radius: number;
radius_top_left: number;
radius_top_right: number;
radius_bottom_left: number;
radius_bottom_right: number;
};
nick_name_color: string;
nick_name_typeface: string;
nick_name_size: number;
goods_img_width: number;
goods_img_height: number;
goods_img_border_color: string;
goods_img_border_size: number;
goods_img_radius: {
radius: number;
radius_top_left: number;
radius_top_right: number;
radius_bottom_left: number;
radius_bottom_right: number;
};
goods_title_color: string;
goods_title_typeface: string;
goods_title_size: number;
time_color: string;
time_typeface: string;
time_size: number;
content_color_list: color_list[];
content_direction: number;
content_background_img_style: number;
content_background_img: string[];
data_spacing: number;
content_padding: paddingStyle;
content_radius: {
radius: number;
radius_top_left: number;
radius_top_right: number;
radius_bottom_left: number;
radius_bottom_right: number;
};
common_style: object;
};
}
const defaultSalerecords: defaultSalerecords = {
content: {
content_top: {
...commonTop,
},
is_roll: '1',
rotation_direction: 'vertical',
interval_time: 3,
show_number: 2,
is_show: ['head', 'nick_name', 'goods_image', 'goods_title', 'time'],
},
style: {
heading_img_width: 28,
heading_img_height: 28,
heading_img_border_color: '',
heading_img_border_size: 0,
heading_img_radius: {
radius: 20,
radius_top_left: 20,
radius_top_right: 20,
radius_bottom_left: 20,
radius_bottom_right: 20,
},
nick_name_color: '#666',
nick_name_typeface: '400',
nick_name_size: 14,
goods_img_width: 28,
goods_img_height: 28,
goods_img_border_color: '',
goods_img_border_size: 0,
goods_img_radius: {
radius: 20,
radius_top_left: 20,
radius_top_right: 20,
radius_bottom_left: 20,
radius_bottom_right: 20,
},
goods_title_color: '#666',
goods_title_typeface: '400',
goods_title_size: 14,
time_color: '#999',
time_typeface: '400',
time_size: 14,
content_color_list: [{ color: '#F4F4F4', color_percentage: undefined }],
content_direction: 0,
content_background_img_style: 0,
content_background_img: [],
data_spacing: 10,
content_padding: {
padding: 0,
padding_top: 6,
padding_bottom: 6,
padding_left: 10,
padding_right: 10,
},
content_radius: {
radius: 20,
radius_top_left: 20,
radius_top_right: 20,
radius_bottom_left: 20,
radius_bottom_right: 20,
},
// 公共样式
common_style: {
...defaultCommon,
color_list: [{ color: '#fff', color_percentage: undefined }],
margin: 10,
margin_top: 10,
margin_bottom: 10,
margin_left: 10,
margin_right: 10,
padding: 10,
padding_top: 10,
padding_bottom: 10,
padding_left: 10,
padding_right: 10,
}
},
};
export default defaultSalerecords;