新增数据魔方显示

v1.0.0
于肖磊 2024-08-13 10:48:05 +08:00
parent 7af9e7ac6a
commit 463874a35a
11 changed files with 528 additions and 3 deletions

View File

@ -39,7 +39,7 @@ interface CubeItem {
x: number;
y: number;
};
img: uploadList[];
img: uploadList[]
}
interface Props {

View File

@ -0,0 +1,158 @@
<template>
<div ref="container" class="img-magic" :style="style_container">
<div class="w h re outer-style">
<!-- 风格3 -->
<template v-if="form.style_actived == 2">
<div class="flex-row align-c jc-c style-size">
<div v-for="(item, index) in form.img_magic_list" :key="index" class="three img-spacing-border">
<image-empty v-model="item.img[0]" :style="content_img_radius"></image-empty>
</div>
</div>
</template>
<!-- 风格9 -->
<template v-else-if="form.style_actived == 8">
<div class="flex-row align-c jc-c style-size flex-wrap">
<div v-for="(item, index) in form.img_magic_list" :key="index" :class="['img-spacing-border', { 'style9-top': [0, 1].includes(index), 'style9-bottom': ![0, 1].includes(index) }]">
<image-empty v-model="item.img[0]" :style="content_img_radius"></image-empty>
</div>
</div>
</template>
<template v-else>
<div v-for="(item, index) in form.img_magic_list" :key="index" class="cube-selected img-spacing-border" :style="selected_style(item)">
<image-empty v-model="item.img[0]" :style="content_img_radius"></image-empty>
</div>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import { common_styles_computer, radius_computer } from '@/utils';
const props = defineProps({
value: {
type: Object,
default: () => {
return {};
},
},
});
//
const state = reactive({
form: props.value.content,
new_style: props.value.style,
});
// 使toRefs
const { form, new_style } = toRefs(state);
const outer_spacing = computed(() => new_style.value.image_spacing + 'px');
const outer_sx = computed(() => -(new_style.value.image_spacing / 2) + 'px');
//
const spacing = computed(() => new_style.value.image_spacing / 2 + 'px');
//
const content_img_radius = computed(() => radius_computer(new_style.value));
//#region
const div_width = ref(0);
const container_size = computed(() => div_width.value + 'px');
const container = ref<HTMLElement | null>(null);
onMounted(() => {
if (container.value) {
div_width.value = container.value.clientWidth;
}
});
//#endregion
//#region
//
interface CubeItem {
start: {
x: number;
y: number;
};
end: {
x: number;
y: number;
};
img: string;
}
const density = ref('4');
//
const cubeCellWidth = computed(() => div_width.value / parseInt(density.value));
//
const cubeCellHeight = computed(() => div_width.value / parseInt(density.value));
const getSelectedWidth = (item: CubeItem) => {
return (item.end.x - item.start.x + 1) * cubeCellWidth.value;
};
//
const getSelectedHeight = (item: CubeItem) => {
return (item.end.y - item.start.y + 1) * cubeCellHeight.value;
};
//
const getSelectedTop = (item: CubeItem) => {
return (item.start.y - 1) * cubeCellHeight.value;
};
//
const getSelectedLeft = (item: CubeItem) => {
return (item.start.x - 1) * cubeCellWidth.value;
};
//
const selected_style = (item: CubeItem) => {
return `width: ${percentage(getSelectedWidth(item))}; height: ${percentage(getSelectedHeight(item))}; top: ${percentage(getSelectedTop(item))}; left: ${percentage(getSelectedLeft(item))};`;
};
//
const percentage = (num: number) => {
const marks = (num / div_width.value) * 100;
return marks.toFixed(4) + '%';
};
//#endregion
//
const style_container = computed(() => common_styles_computer(new_style.value.common_style));
</script>
<style lang="scss" scoped>
//
.img-magic {
height: v-bind(container_size);
width: 100%;
overflow: hidden;
}
.cube-selected {
position: absolute;
text-align: center;
color: $cr-main;
box-sizing: border-box;
}
.outer-style {
width: calc(100% + v-bind(outer_spacing));
height: calc(100% + v-bind(outer_spacing));
margin: v-bind(outer_sx);
}
.img-spacing-border {
padding: v-bind(spacing);
}
.style-size {
height: 100%;
width: 100%;
.three {
width: 33%;
height: 100%;
position: relative;
}
.style9-top {
width: calc(50% - 0.2rem);
height: 50%;
position: relative;
}
.style9-bottom {
width: calc(33% - 0.1rem);
height: 50%;
position: relative;
}
}
:deep(.el-image) {
height: 100%;
width: 100%;
.el-image__inner {
object-fit: cover;
}
.image-slot img {
width: 6rem;
}
}
</style>

View File

@ -0,0 +1,197 @@
<template>
<div class="auxiliary-line common-content-height">
<el-form :model="form" label-width="60">
<card-container class="mb-8">
<div class="mb-12">展示风格</div>
<el-form-item label="选择风格">
<div class="flex align-c flex-wrap gap-10">
<div v-for="(item, index) in style_list" :key="index" :class="['flex-item', {'flex-item-actived': form.style_actived === index }]" @click="style_click(index)">
<icon :name="item" :color="`${ form.style_actived === index ? '#E1EEF9' : '#EDEDED'}`" size="48"></icon>
</div>
</div>
</el-form-item>
</card-container>
<card-container class="mb-8">
<div class="mb-12">展示设置</div>
<el-form-item label-width="0" class="show-config">
<!-- 风格3 -->
<template v-if="form.style_actived == 2">
<div class="flex-row align-c jc-c gap-2 style-size">
<div v-for="(item, index) in form.img_magic_list" :key="index" :class="['three bg-f5', {'cube-selected-active': selected_active == index}]" @click="selected_click(index)">
<template v-if="!isEmpty(item.img[0])">
<image-empty v-model="item.img[0]"></image-empty>
</template>
<template v-else>
<div class="cube-selected-text">250 x 750 像素</div>
</template>
</div>
</div>
</template>
<!-- 风格9 -->
<template v-else-if="form.style_actived == 8">
<div class="flex-row align-c jc-c gap-2 style-size flex-wrap">
<div v-for="(item, index) in form.img_magic_list" :key="index" :class="['bg-f5', {'cube-selected-active': selected_active == index, 'style9-top': [0, 1].includes(index), 'style9-bottom': ![0, 1].includes(index)}]" @click="selected_click(index)">
<template v-if="!isEmpty(item.img[0])">
<image-empty v-model="item.img[0]"></image-empty>
</template>
<template v-else>
<div class="cube-selected-text">
<template v-if="[0, 1].includes(index)">375 x 375 </template>
<template v-else>250 x 375 </template>
</div>
</template>
</div>
</div>
</template>
<template v-else>
<magic-cube :key="form.style_actived" :list="form.img_magic_list" :flag="form.style_actived == 11" :cube-width="cubeWidth" :cube-height="cubeHeight" @selected_click="selected_click"></magic-cube>
</template>
</el-form-item>
</card-container>
<card-container class="mb-8">
<div class="mb-12">内容设置</div>
<template v-if="!isEmpty(form.img_magic_list[selected_active])">
<el-form-item label="上传图片">
<upload v-model="form.img_magic_list[selected_active].img" :limit="1" size="40"></upload>
</el-form-item>
<el-form-item label="链接">
<url-value v-model="form.img_magic_list[selected_active].img_link"></url-value>
</el-form-item>
</template>
</card-container>
</el-form>
</div>
</template>
<script setup lang="ts">
import { isEmpty, cloneDeep } from 'lodash';
const props = defineProps({
value: {
type: Object,
default: () => {},
},
});
const style_list = ['heng2', 'shu2', 'shang2xia1', 'shang1xia2', 'zuo1you2', 'zuo2you1', 'tianzige', 'shang2xia3', 'zuo1youshang1youxia2'];
//
const style_show_list = [
[{ start: {x: 1, y: 1}, end: {x: 4, y: 2}, img: [], img_link: {} }, { start: {x: 1, y: 3},end: {x: 4, y: 4},img: [], img_link: {}}], // 1
[{ start: {x: 1, y: 1}, end: {x: 2, y: 4}, img: [], img_link: {} }, { start: {x: 3, y: 1},end: {x: 4, y: 4},img: [], img_link: {}}], // 2
[{ start: {x: 1, y: 1}, end: {x: 2, y: 2}, img: [], img_link: {} }, { start: {x: 3, y: 1},end: {x: 4, y: 2},img: [], img_link: {}}, { start: {x: 1, y: 3},end: {x: 4, y: 4},img: [], img_link: {}}],// 3
[{ start: {x: 1, y: 1}, end: {x: 4, y: 2}, img: [], img_link: {} }, { start: {x: 1, y: 3},end: {x: 2, y: 4},img: [], img_link: {}}, { start: {x: 3, y: 3},end: {x: 4, y: 4},img: [], img_link: {}}],// 4
[{ start: {x: 1, y: 1}, end: {x: 2, y: 4}, img: [], img_link: {} }, { start: {x: 3, y: 1},end: {x: 4, y: 2},img: [], img_link: {}}, { start: {x: 3, y: 3},end: {x: 4, y: 4},img: [], img_link: {}}],// 5
[{ start: {x: 1, y: 1}, end: {x: 2, y: 2}, img: [], img_link: {} }, { start: {x: 1, y: 3},end: {x: 2, y: 4},img: [], img_link: {}}, { start: {x: 3, y: 1},end: {x: 4, y: 4},img: [], img_link: {}}],// 6
[{ start: {x: 1, y: 1}, end: {x: 2, y: 2}, img: [], img_link: {} }, { start: {x: 3, y: 1},end: {x: 4, y: 2},img: [], img_link: {}}, { start: {x: 1, y: 3},end: {x: 2, y: 4},img: [], img_link: {}}, { start: {x: 3, y: 3},end: {x: 4, y: 4},img: [], img_link: {}}],// 7
[{ start: {x: 1, y: 1}, end: {x: 2, y: 4}, img: [], img_link: {} }, { start: {x: 3, y: 1},end: {x: 4, y: 2},img: [], img_link: {}}, { start: {x: 3, y: 3},end: {x: 3, y: 4},img: [], img_link: {}}, { start: {x: 4, y: 3},end: {x: 4, y: 4},img: [], img_link: {}}, { start: {x: 4, y: 3},end: {x: 4, y: 4},img: [], img_link: {}}],// 8
[{ start: {x: 1, y: 1}, end: {x: 2, y: 4}, img: [], img_link: {} }, { start: {x: 3, y: 1},end: {x: 4, y: 2},img: [], img_link: {}}, { start: {x: 3, y: 3},end: {x: 3, y: 4},img: [], img_link: {}}, { start: {x: 4, y: 3},end: {x: 4, y: 4},img: [], img_link: {}}],// 9
]
//#region
const cubeWidth = ref(400);
const cubeHeight = ref(400);
const style_width = computed(() => cubeWidth.value + 'px');
const style_height = computed(() => cubeHeight.value + 'px');
function handleResize() {
if (window.innerWidth <= 1540) {
cubeWidth.value = 330;
cubeHeight.value = 330;
} else {
cubeWidth.value = 390;
cubeHeight.value = 390;
}
}
onMounted(() => {
handleResize();
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
//#endregion
const selected_active = ref(0);
const state = reactive({
form: props.value
});
const { form } = toRefs(state);
const style_click = (index: number) => {
form.value.img_magic_list = cloneDeep(style_show_list[index]);
form.value.style_actived = index;
selected_active.value = 0;
}
//
const selected_click = (index: number) => {
selected_active.value = index;
}
</script>
<style lang="scss" scoped>
.card.mb-8 {
.el-form-item:last-child {
margin-bottom: 0;
}
}
.flex-item {
width: 7.6rem;
height: 7.6rem;
background: #F7F7F7;
border-radius: 0.4rem;
border: 1px solid #E4E4E4;
padding: 1.4rem;
display: flex;
align-items: center;
justify-content: center;
}
.flex-item-actived {
background: #F5F9FF;
border-radius: 4px;
border: 1px solid #2A94FF;
}
.show-config {
:deep(.el-form-item__content) {
justify-content: center;
}
}
.style-size {
width: v-bind(style_width);
height: v-bind(style_height);
.three {
width: 33%;
height: 100%;
position: relative;
}
.style9-top {
width: calc(50% - 0.2rem);
height: 50%;
position: relative;
}
.style9-bottom {
width: calc(33% - 0.1rem);
height: 50%;
position: relative;
}
}
.cube-selected-active {
border: 1px solid $cr-primary;
}
.cube-selected-text {
font-size: 12px;
width: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
text-align: center;
color: $cr-primary;
}
:deep(.el-image) {
height: 100%;
width: 100%;
background-color: #fff;
.el-image__inner {
object-fit: cover;
}
.image-slot img {
width: 6rem;
}
}
</style>

View File

@ -0,0 +1,27 @@
<template>
<div class="auxiliary-line-setting">
<template v-if="type == '1'">
<model-data-magic-content :value="value.content"></model-data-magic-content>
</template>
<template v-if="type == '2'">
<model-data-magic-styles :value="value.style"></model-data-magic-styles>
</template>
</div>
</template>
<script setup lang="ts">
const props = defineProps({
type: {
type: String,
default: '1',
},
value: {
type: Object,
default: () => ({}),
},
});
</script>
<style lang="scss" scoped>
.auxiliary-line-setting {
width: 100%;
}
</style>

View File

@ -0,0 +1,62 @@
<template>
<div class="w">
<el-form :model="form" label-width="70">
<card-container class="mb-8">
<div class="mb-12">图片魔方</div>
<el-form-item label="图片间距">
<slider v-model="form.image_spacing" :max="100"></slider>
</el-form-item>
<el-form-item label="数据圆角">
<radius :value="form.data" @update:value="radius_change"></radius>
</el-form-item>
<el-form-item label="图片圆角">
<radius :value="form" @update:value="radius_change"></radius>
</el-form-item>
</card-container>
</el-form>
<common-styles :value="form.common_style" @update:value="common_style_update" />
</div>
</template>
<script setup lang="ts">
import { pick } from 'lodash';
const props = defineProps({
value: {
type: Object,
default: () => ({}),
}
});
//
const state = reactive({
form: props.value
});
// 使toRefs
const { form } = toRefs(state);
const common_style_update = (value: any) => {
form.value.common_style = value;
};
//
const radius_change = (radius: any) => {
form.value = Object.assign(form.value, pick(radius, [
'radius',
'radius_top_left',
'radius_top_right',
'radius_bottom_left',
'radius_bottom_right',
]));
}
</script>
<style lang="scss" scoped>
.topic {
:deep(.el-form-item__content) {
align-items: flex-start;
flex-direction: column;
}
}
.card.mb-8 {
.el-form-item:last-child {
margin-bottom: 0;
}
}
</style>

View File

@ -44,7 +44,7 @@
</div>
</template>
<template v-else>
<picture-cube :key="form.style_actived" :list="form.img_magic_list" :flag="form.style_actived == 11" :cube-width="cubeWidth" :cube-height="cubeHeight" @selected_click="selected_click"></picture-cube>
<magic-cube :key="form.style_actived" :list="form.img_magic_list" :flag="form.style_actived == 11" :cube-width="cubeWidth" :cube-height="cubeHeight" @selected_click="selected_click"></magic-cube>
</template>
</el-form-item>
</card-container>

View File

@ -0,0 +1,60 @@
import { get_math } from "@/utils";
import defaultCommon from "./index";
interface img_magic {
start: object;
end: object;
img: uploadList[],
img_link: object;
}
interface defaultSearch {
content: {
style_actived: number;
img_magic_list: img_magic[];
};
style: {
img_radius: object
data_radius: object;
image_spacing: number;
common_style: object;
};
}
const defaultSearch: defaultSearch = {
content: {
style_actived: 0,
img_magic_list: [
{
start: {x: 1, y: 1},
end: {x: 4, y: 2},
img: [],
img_link: {}
},
{
start: {x: 1, y: 3},
end: {x: 4, y: 4},
img: [],
img_link: {}
}
],
},
style: {
img_radius: {
radius: 0,
radius_top_left: 0,
radius_top_right: 0,
radius_bottom_left: 0,
radius_bottom_right: 0,
},
data_radius: {
radius: 0,
radius_top_left: 0,
radius_top_right: 0,
radius_bottom_left: 0,
radius_bottom_right: 0,
},
image_spacing: 2,
common_style: defaultCommon,
},
};
export default defaultSearch;

View File

@ -19,6 +19,8 @@ import defaultShopTabs from './default/shop-tabs';
import defaultImgMagic from './default/img-magic';
import defaultHotZone from './default/hot-zone';
import defaultCustom from './default/custom';
import defaultDataMagic from './default/data-magic';
import { de } from 'element-plus/es/locale';
// 系统设置
interface DefaultSettings {
header_nav: object;
@ -40,6 +42,7 @@ interface DefaultSettings {
shop_list: object;
shop_tabs: object;
img_magic: object;
data_magic: object;
hot_zone: object;
custom: object;
}
@ -64,6 +67,7 @@ const defaultSettings: DefaultSettings = {
shop_list: defaultProductList,
shop_tabs: defaultShopTabs,
img_magic: defaultImgMagic,
data_magic: defaultDataMagic,
hot_zone: defaultHotZone,
custom: defaultCustom,
};

View File

@ -92,6 +92,10 @@
<template v-else-if="item.key == 'img-magic'">
<model-img-magic :key="item.com_data" :value="item.com_data" :show-tabs="item.show_tabs"></model-img-magic>
</template>
<!-- 数据魔方 -->
<template v-else-if="item.key == 'data-magic'">
<model-data-magic :key="item.com_data" :value="item.com_data" :show-tabs="item.show_tabs"></model-data-magic>
</template>
<!-- 热区 -->
<template v-else-if="item.key == 'hot-zone'">
<model-hot-zone :key="item.com_data" :value="item.com_data"></model-hot-zone>
@ -100,7 +104,6 @@
<template v-else-if="item.key == 'custom'">
<model-custom :key="item.com_data" :value="item.com_data" :show-tabs="item.show_tabs"></model-custom>
</template>
<!-- 工具组件 -->
<!-- 辅助线 -->
<template v-else-if="item.key == 'row-line'">
@ -205,6 +208,7 @@ const components = reactive([
{ key: 'article-tabs', name: '文章选项卡' },
{ key: 'hot-zone', name: '热区' },
{ key: 'custom', name: '自定义' },
{ key: 'data-magic', name: '数据魔方' },
],
},
{

View File

@ -63,6 +63,10 @@
<template v-else-if="value.key == 'img-magic'">
<model-img-magic-setting :type="radio" :value="value.com_data"></model-img-magic-setting>
</template>
<!-- 图片魔方 -->
<template v-else-if="value.key == 'data-magic'">
<model-data-magic-setting :type="radio" :value="value.com_data"></model-data-magic-setting>
</template>
<!-- 热区 -->
<template v-else-if="value.key == 'hot-zone'">
<model-hot-zone-setting :type="radio" :value="value.com_data"></model-hot-zone-setting>

View File

@ -13,6 +13,8 @@ declare module 'vue' {
ColorPicker: typeof import('./../components/base/color-picker/index.vue')['default']
CommonStyles: typeof import('./../components/common/common-styles/index.vue')['default']
Components: typeof import('./../components/model-custom/components/index.vue')['default']
copy: typeof import('./../components/model-img-magic copy/index.vue')['default']
Cube: typeof import('./../components/common/cube/index.vue')['default']
Dialog: typeof import('./../components/model-custom/components/dialog.vue')['default']
Drag: typeof import('./../components/base/drag/index.vue')['default']
ElBadge: typeof import('element-plus/es')['ElBadge']
@ -70,8 +72,10 @@ declare module 'vue' {
LinkGoodsSearch: typeof import('./../components/common/url-value/link-goods-search.vue')['default']
LinkList: typeof import('./../components/common/url-value/link-list.vue')['default']
LinkTable: typeof import('./../components/common/url-value/link-table.vue')['default']
MagicCube: typeof import('./../components/common/magic-cube/index.vue')['default']
Main: typeof import('./../layout/components/main/index.vue')['default']
Maps: typeof import('./../components/base/maps/index.vue')['default']
'Model-': typeof import('./../components/model-/index.vue')['default']
ModelArticleList: typeof import('./../components/model-article-list/index.vue')['default']
ModelArticleListContent: typeof import('./../components/model-article-list/model-article-list-content.vue')['default']
ModelArticleListSetting: typeof import('./../components/model-article-list/model-article-list-setting.vue')['default']
@ -96,6 +100,11 @@ declare module 'vue' {
ModelCustomContent: typeof import('./../components/model-custom/model-custom-content.vue')['default']
ModelCustomSetting: typeof import('./../components/model-custom/model-custom-setting.vue')['default']
ModelCustomStyles: typeof import('./../components/model-custom/model-custom-styles.vue')['default']
ModelDataCube: typeof import('./../components/model-data-cube/index.vue')['default']
ModelDataMagic: typeof import('./../components/model-data-magic/index.vue')['default']
ModelDataMagicContent: typeof import('./../components/model-data-magic/model-data-magic-content.vue')['default']
ModelDataMagicSetting: typeof import('./../components/model-data-magic/model-data-magic-setting.vue')['default']
ModelDataMagicStyles: typeof import('./../components/model-data-magic/model-data-magic-styles.vue')['default']
ModelFloatWindow: typeof import('./../components/model-float-window/index.vue')['default']
ModelFloatWindowContent: typeof import('./../components/model-float-window/model-float-window-content.vue')['default']
ModelFloatWindowSetting: typeof import('./../components/model-float-window/model-float-window-setting.vue')['default']