mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-06 17:09:08 +08:00
feat(manager): 移动端装修编辑器 CRMEB 化迭代
- 轮播图:双 tab 面板(内容/样式)、轮播风格(通栏/卡片)、指示器样式/位置/色调、渐变背景、边距圆角 - 图片魔方:9 种风格收进样式 tab,拖入默认左一右二,内容 tab 固定槽位配置,新增颜色/边距/圆角设置 - 宫格导航:双 tab 面板,菜单管理 + 颜色/边距/圆角设置 - 编辑器整体:灰底卡片模块化、顶栏页面名称编辑、画布操作条/名称标签外置、组件列表三列通高卡片 - 预览模板同步应用各组件样式字段
This commit is contained in:
@@ -0,0 +1,353 @@
|
||||
<template>
|
||||
<div class="panel-ad-cube">
|
||||
<div class="panel-header">
|
||||
<span class="panel-name">图片魔方</span>
|
||||
<div class="panel-tabs">
|
||||
<div
|
||||
v-for="tab in tabs"
|
||||
:key="tab.key"
|
||||
class="panel-tab"
|
||||
:class="{ active: activeTab === tab.key }"
|
||||
@click="activeTab = tab.key"
|
||||
>
|
||||
{{ tab.title }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="activeTab === 'content'">
|
||||
<div class="panel-card">
|
||||
<div class="section-title">图片设置</div>
|
||||
<div class="section-tip">当前风格:{{ res.name }},请按布局区域配置对应图片</div>
|
||||
<div class="img-item" v-for="(item, index) in res.options.list" :key="index">
|
||||
<div class="img-item-head">
|
||||
<span>图片 {{ index + 1 }}</span>
|
||||
<span class="img-size" v-if="item.size">建议尺寸 {{ item.size }}</span>
|
||||
</div>
|
||||
<div class="img-preview" v-if="item.img">
|
||||
<img :src="item.img" alt="" />
|
||||
</div>
|
||||
<div class="img-placeholder" v-else>尺寸 {{ item.size || "不限" }}</div>
|
||||
<div class="field-row" v-if="item.title !== undefined">
|
||||
<div class="field-label">文字</div>
|
||||
<el-input v-model="item.title" size="small" maxlength="10" />
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<el-button plain type="primary" @click="pickImage(item)">选择照片</el-button>
|
||||
<el-button plain type="primary" @click="pickLink(item)">选择链接</el-button>
|
||||
</div>
|
||||
<div class="link-view" v-if="item.url && item.url.___type">
|
||||
已选链接:{{ linkText(item.url) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="panel-card">
|
||||
<div class="section-title">风格选择</div>
|
||||
<div class="section-tip">切换风格将重置当前已配置的图片内容</div>
|
||||
<div class="style-grid">
|
||||
<div
|
||||
v-for="style in styleList"
|
||||
:key="style.type"
|
||||
class="style-item"
|
||||
:class="{ active: res.type === style.type }"
|
||||
@click="changeStyle(style)"
|
||||
>
|
||||
{{ style.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-card">
|
||||
<div class="section-title">颜色设置</div>
|
||||
<color-field
|
||||
v-model="res.options.bgColor1"
|
||||
label="背景颜色"
|
||||
:default-value="defaults.bgColor1"
|
||||
/>
|
||||
<color-field
|
||||
v-model="res.options.bgColor2"
|
||||
label="渐变结束色"
|
||||
:default-value="defaults.bgColor2"
|
||||
/>
|
||||
<div class="section-tip">起始色与结束色一致时为纯色背景</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-card">
|
||||
<div class="section-title">边距设置</div>
|
||||
<slider-field v-model="res.options.paddingTop" label="上边距" :min="0" :max="100" />
|
||||
<slider-field v-model="res.options.paddingBottom" label="下边距" :min="0" :max="100" />
|
||||
<slider-field v-model="res.options.paddingX" label="左右边距" :min="0" :max="30" />
|
||||
<slider-field v-model="res.options.pageGap" label="页面间距" :min="0" :max="100" />
|
||||
<slider-field v-model="res.options.contentGap" label="内容间距" :min="0" :max="50" />
|
||||
</div>
|
||||
|
||||
<div class="panel-card">
|
||||
<div class="section-title">圆角设置</div>
|
||||
<slider-field v-model="res.options.borderRadius" label="背景圆角" :min="0" :max="50" />
|
||||
<slider-field v-model="res.options.imgRadius" label="内容圆角" :min="0" :max="50" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
|
||||
|
||||
<el-dialog v-model="picModelFlag" width="1200px" append-to-body destroy-on-close>
|
||||
<ossManage
|
||||
@callback="callbackSelected"
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
:max-select="1"
|
||||
ref="ossManage"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
import ways from "@/components/lili-dialog/wap.js";
|
||||
import { modelData } from "../config";
|
||||
import ColorField from "../controls/ColorField.vue";
|
||||
import SliderField from "../controls/SliderField.vue";
|
||||
|
||||
const DEFAULTS = {
|
||||
bgColor1: "#FFFFFF",
|
||||
bgColor2: "#FFFFFF",
|
||||
paddingTop: 10,
|
||||
paddingBottom: 10,
|
||||
paddingX: 12,
|
||||
pageGap: 10,
|
||||
contentGap: 0,
|
||||
borderRadius: 0,
|
||||
imgRadius: 0,
|
||||
};
|
||||
|
||||
export default {
|
||||
name: "PanelAdCube",
|
||||
hideDecorateTitle: true,
|
||||
components: { ossManage, ColorField, SliderField },
|
||||
props: {
|
||||
res: { type: Object, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaults: DEFAULTS,
|
||||
activeTab: "content",
|
||||
tabs: [
|
||||
{ key: "content", title: "内容" },
|
||||
{ key: "style", title: "样式" },
|
||||
],
|
||||
picModelFlag: false,
|
||||
currentItem: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 图片魔方的可选风格(config 中 drawer 标记的条目)
|
||||
styleList() {
|
||||
return modelData.filter((m) => m.drawer);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.res.options = Object.assign({}, DEFAULTS, this.res.options);
|
||||
},
|
||||
methods: {
|
||||
linkText(url) {
|
||||
const way = ways.find((e) => e.name === url.___type);
|
||||
const typeTitle = way ? way.title : "发现";
|
||||
const name =
|
||||
url.name || url.goodsName || url.memberName || url.title || "";
|
||||
return name ? `${typeTitle} - ${name}` : typeTitle;
|
||||
},
|
||||
changeStyle(style) {
|
||||
if (style.type === this.res.type) return;
|
||||
this.$emit("handleDrawer", style);
|
||||
},
|
||||
pickImage(item) {
|
||||
this.currentItem = item;
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
const oss = this.$refs.ossManage;
|
||||
if (!oss) return;
|
||||
oss.selectImage = true;
|
||||
oss.fileType = "pic";
|
||||
oss.changeFileType();
|
||||
});
|
||||
},
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
if (val && val.url && this.currentItem) {
|
||||
this.currentItem.img = val.url;
|
||||
}
|
||||
},
|
||||
pickLink(item) {
|
||||
this.currentItem = item;
|
||||
const dialog = this.$refs.liliDialog;
|
||||
dialog.clearGoodsSelected();
|
||||
dialog.goodsFlag = false;
|
||||
dialog.flag = true;
|
||||
},
|
||||
selectedLink(val) {
|
||||
if (!this.currentItem) return;
|
||||
delete val.selected;
|
||||
delete val.intro;
|
||||
delete val.mobileIntro;
|
||||
this.currentItem.url = val;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.panel-ad-cube {
|
||||
background: #f5f7fa;
|
||||
margin: 0 -20px -120px;
|
||||
padding: 0 12px 120px;
|
||||
min-height: calc(100vh - 70px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 50px;
|
||||
margin-bottom: 12px;
|
||||
padding: 0 14px;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||
box-sizing: border-box;
|
||||
.panel-name {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
.panel-tabs {
|
||||
display: inline-flex;
|
||||
background: #f4f4f5;
|
||||
border-radius: 8px;
|
||||
padding: 3px;
|
||||
.panel-tab {
|
||||
padding: 5px 14px;
|
||||
font-size: 13px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
color: #606266;
|
||||
transition: all 0.2s ease;
|
||||
&.active {
|
||||
background: #ffffff;
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
}
|
||||
}
|
||||
.panel-card {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 14px;
|
||||
margin-bottom: 12px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.section-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
.section-tip {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.img-item {
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
.img-item-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 13px;
|
||||
color: #303133;
|
||||
margin-bottom: 8px;
|
||||
.img-size {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
}
|
||||
.img-preview {
|
||||
border: 1px dashed #dcdfe6;
|
||||
border-radius: 6px;
|
||||
padding: 6px;
|
||||
margin-bottom: 10px;
|
||||
img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
.img-placeholder {
|
||||
height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px dashed #dcdfe6;
|
||||
border-radius: 6px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.field-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
.field-label {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
margin-right: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
.btn-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
.link-view {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.style-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 8px;
|
||||
.style-item {
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 6px;
|
||||
padding: 10px 4px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
&:hover {
|
||||
border-color: #409eff;
|
||||
color: #409eff;
|
||||
}
|
||||
&.active {
|
||||
border-color: #409eff;
|
||||
background: #ecf5ff;
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,503 @@
|
||||
<template>
|
||||
<div class="panel-carousel">
|
||||
<div class="panel-header">
|
||||
<span class="panel-name">{{ res.name }}</span>
|
||||
<div class="panel-tabs">
|
||||
<div
|
||||
v-for="tab in tabs"
|
||||
:key="tab.key"
|
||||
class="panel-tab"
|
||||
:class="{ active: activeTab === tab.key }"
|
||||
@click="activeTab = tab.key"
|
||||
>
|
||||
{{ tab.title }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="activeTab === 'content'">
|
||||
<div class="panel-card">
|
||||
<div class="section-title">图片设置</div>
|
||||
<div class="section-tip">最多添加 10 张,建议添加比例一致的图片</div>
|
||||
<div class="img-item" v-for="(item, index) in res.options.list" :key="index">
|
||||
<div class="img-item-head">
|
||||
<span>图片 {{ index + 1 }}</span>
|
||||
<div class="img-item-ops">
|
||||
<el-icon
|
||||
:class="{ disabled: index === 0 }"
|
||||
@click="moveItem(index, -1)"
|
||||
><Top
|
||||
/></el-icon>
|
||||
<el-icon
|
||||
:class="{ disabled: index === res.options.list.length - 1 }"
|
||||
@click="moveItem(index, 1)"
|
||||
><Bottom
|
||||
/></el-icon>
|
||||
<el-icon class="del" @click="removeItem(index)"><Delete /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="img-preview" v-if="item.img">
|
||||
<img :src="item.img" alt="" />
|
||||
</div>
|
||||
<div class="img-placeholder" v-else>建议尺寸 750*350</div>
|
||||
<div class="btn-row">
|
||||
<el-button plain type="primary" @click="pickImage(item)">选择照片</el-button>
|
||||
<el-button plain type="primary" @click="pickLink(item)">选择链接</el-button>
|
||||
</div>
|
||||
<div class="link-view" v-if="item.url && item.url.___type">
|
||||
已选链接:{{ linkText(item.url) }}
|
||||
</div>
|
||||
</div>
|
||||
<el-button
|
||||
v-if="res.options.list.length < 10"
|
||||
class="add-btn"
|
||||
plain
|
||||
type="primary"
|
||||
@click="addItem"
|
||||
>添加图片</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="panel-card">
|
||||
<div class="section-title">轮播风格</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-label">
|
||||
风格
|
||||
<span class="setting-value">{{ swiperStyleText }}</span>
|
||||
</div>
|
||||
<div class="seg">
|
||||
<div
|
||||
v-for="opt in swiperStyleOptions"
|
||||
:key="opt.value"
|
||||
class="seg-item"
|
||||
:class="{ active: res.options.swiperStyle === opt.value }"
|
||||
@click="res.options.swiperStyle = opt.value"
|
||||
>
|
||||
{{ opt.title }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section-tip" v-if="res.options.swiperStyle === 'card'">
|
||||
卡片风格下,两侧会露出前后图片的一部分
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-card">
|
||||
<div class="section-title">指示器设置</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-label">
|
||||
指示器样式
|
||||
<span class="setting-value">{{ indicatorStyleText }}</span>
|
||||
</div>
|
||||
<div class="seg">
|
||||
<div
|
||||
v-for="opt in indicatorStyleOptions"
|
||||
:key="opt.value"
|
||||
class="seg-item"
|
||||
:class="{ active: res.options.indicatorStyle === opt.value }"
|
||||
@click="res.options.indicatorStyle = opt.value"
|
||||
>
|
||||
{{ opt.title }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-row" v-if="res.options.indicatorStyle !== 'none'">
|
||||
<div class="setting-label">
|
||||
指示器位置
|
||||
<span class="setting-value">{{ indicatorPositionText }}</span>
|
||||
</div>
|
||||
<div class="seg">
|
||||
<div
|
||||
v-for="opt in indicatorPositionOptions"
|
||||
:key="opt.value"
|
||||
class="seg-item"
|
||||
:class="{ active: res.options.indicatorPosition === opt.value }"
|
||||
@click="res.options.indicatorPosition = opt.value"
|
||||
>
|
||||
{{ opt.title }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-row" v-if="res.options.indicatorStyle !== 'none'">
|
||||
<div class="setting-label">色调</div>
|
||||
<el-radio-group v-model="res.options.indicatorColorMode">
|
||||
<el-radio value="theme">跟随主题风格</el-radio>
|
||||
<el-radio value="custom">自定义</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<template
|
||||
v-if="
|
||||
res.options.indicatorStyle !== 'none' &&
|
||||
res.options.indicatorColorMode === 'custom'
|
||||
"
|
||||
>
|
||||
<color-field
|
||||
v-model="res.options.indicatorColor"
|
||||
label="指示器颜色"
|
||||
:default-value="defaults.indicatorColor"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="panel-card">
|
||||
<div class="section-title">颜色设置</div>
|
||||
<color-field
|
||||
v-model="res.options.bgColor1"
|
||||
label="背景颜色"
|
||||
:default-value="defaults.bgColor1"
|
||||
/>
|
||||
<color-field
|
||||
v-model="res.options.bgColor2"
|
||||
label="渐变结束色"
|
||||
:default-value="defaults.bgColor2"
|
||||
/>
|
||||
<div class="section-tip">起始色与结束色一致时为纯色背景</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-card">
|
||||
<div class="section-title">边距设置</div>
|
||||
<slider-field v-model="res.options.paddingTop" label="上边距" :min="0" :max="100" />
|
||||
<slider-field v-model="res.options.paddingBottom" label="下边距" :min="0" :max="100" />
|
||||
<slider-field v-model="res.options.paddingX" label="左右边距" :min="0" :max="30" />
|
||||
<slider-field v-model="res.options.pageGap" label="页面间距" :min="0" :max="100" />
|
||||
</div>
|
||||
|
||||
<div class="panel-card">
|
||||
<div class="section-title">圆角设置</div>
|
||||
<slider-field v-model="res.options.borderRadius" label="背景圆角" :min="0" :max="50" />
|
||||
<slider-field v-model="res.options.imgRadius" label="内容圆角" :min="0" :max="50" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
|
||||
|
||||
<el-dialog v-model="picModelFlag" width="1200px" append-to-body destroy-on-close>
|
||||
<ossManage
|
||||
@callback="callbackSelected"
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
:max-select="1"
|
||||
ref="ossManage"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Top, Bottom, Delete } from "@element-plus/icons-vue";
|
||||
import ColorField from "../controls/ColorField.vue";
|
||||
import SliderField from "../controls/SliderField.vue";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
import ways from "@/components/lili-dialog/wap.js";
|
||||
|
||||
const DEFAULTS = {
|
||||
swiperStyle: "full",
|
||||
indicatorStyle: "round",
|
||||
indicatorPosition: "center",
|
||||
indicatorColorMode: "theme",
|
||||
indicatorColor: "#409eff",
|
||||
bgColor1: "#FFFFFF",
|
||||
bgColor2: "#FFFFFF",
|
||||
paddingTop: 10,
|
||||
paddingBottom: 10,
|
||||
paddingX: 12,
|
||||
pageGap: 0,
|
||||
borderRadius: 0,
|
||||
imgRadius: 7,
|
||||
};
|
||||
|
||||
export default {
|
||||
name: "PanelCarousel",
|
||||
hideDecorateTitle: true,
|
||||
components: { ColorField, SliderField, ossManage, Top, Bottom, Delete },
|
||||
props: {
|
||||
res: { type: Object, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaults: DEFAULTS,
|
||||
activeTab: "content",
|
||||
tabs: [
|
||||
{ key: "content", title: "内容" },
|
||||
{ key: "style", title: "样式" },
|
||||
],
|
||||
swiperStyleOptions: [
|
||||
{ value: "full", title: "通栏" },
|
||||
{ value: "card", title: "卡片" },
|
||||
],
|
||||
indicatorStyleOptions: [
|
||||
{ value: "round", title: "圆形" },
|
||||
{ value: "line", title: "长条" },
|
||||
{ value: "none", title: "无" },
|
||||
],
|
||||
indicatorPositionOptions: [
|
||||
{ value: "left", title: "居左" },
|
||||
{ value: "center", title: "居中" },
|
||||
{ value: "right", title: "居右" },
|
||||
],
|
||||
picModelFlag: false,
|
||||
currentItem: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
swiperStyleText() {
|
||||
const hit = this.swiperStyleOptions.find(
|
||||
(o) => o.value === this.res.options.swiperStyle
|
||||
);
|
||||
return hit ? hit.title : "";
|
||||
},
|
||||
indicatorStyleText() {
|
||||
const hit = this.indicatorStyleOptions.find(
|
||||
(o) => o.value === this.res.options.indicatorStyle
|
||||
);
|
||||
return hit ? hit.title : "";
|
||||
},
|
||||
indicatorPositionText() {
|
||||
const hit = this.indicatorPositionOptions.find(
|
||||
(o) => o.value === this.res.options.indicatorPosition
|
||||
);
|
||||
return hit ? hit.title : "";
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.res.options = Object.assign({}, DEFAULTS, this.res.options);
|
||||
if (!Array.isArray(this.res.options.list)) {
|
||||
this.res.options.list = [];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
linkText(url) {
|
||||
const way = ways.find((e) => e.name === url.___type);
|
||||
const typeTitle = way ? way.title : "发现";
|
||||
const name =
|
||||
url.name || url.goodsName || url.memberName || url.title || "";
|
||||
return name ? `${typeTitle} - ${name}` : typeTitle;
|
||||
},
|
||||
pickImage(item) {
|
||||
this.currentItem = item;
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
const oss = this.$refs.ossManage;
|
||||
if (!oss) return;
|
||||
oss.selectImage = true;
|
||||
oss.fileType = "pic";
|
||||
oss.changeFileType();
|
||||
});
|
||||
},
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
if (val && val.url && this.currentItem) {
|
||||
this.currentItem.img = val.url;
|
||||
}
|
||||
},
|
||||
pickLink(item) {
|
||||
this.currentItem = item;
|
||||
const dialog = this.$refs.liliDialog;
|
||||
dialog.clearGoodsSelected();
|
||||
dialog.goodsFlag = false;
|
||||
dialog.flag = true;
|
||||
},
|
||||
selectedLink(val) {
|
||||
if (!this.currentItem) return;
|
||||
delete val.selected;
|
||||
delete val.intro;
|
||||
delete val.mobileIntro;
|
||||
this.currentItem.url = val;
|
||||
},
|
||||
moveItem(index, dir) {
|
||||
const list = this.res.options.list;
|
||||
const target = index + dir;
|
||||
if (target < 0 || target >= list.length) return;
|
||||
list.splice(target, 1, ...list.splice(index, 1, list[target]));
|
||||
},
|
||||
removeItem(index) {
|
||||
this.res.options.list.splice(index, 1);
|
||||
},
|
||||
addItem() {
|
||||
this.res.options.list.push({
|
||||
img: "",
|
||||
url: "",
|
||||
link: "",
|
||||
size: "750*350",
|
||||
model: "link",
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.panel-carousel {
|
||||
background: #f5f7fa;
|
||||
margin: 0 -20px -120px;
|
||||
padding: 0 12px 120px;
|
||||
min-height: calc(100vh - 70px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 50px;
|
||||
margin-bottom: 12px;
|
||||
padding: 0 14px;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||
box-sizing: border-box;
|
||||
.panel-name {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
.panel-tabs {
|
||||
display: inline-flex;
|
||||
background: #f4f4f5;
|
||||
border-radius: 8px;
|
||||
padding: 3px;
|
||||
.panel-tab {
|
||||
padding: 5px 14px;
|
||||
font-size: 13px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
color: #606266;
|
||||
transition: all 0.2s ease;
|
||||
&.active {
|
||||
background: #ffffff;
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
}
|
||||
}
|
||||
.panel-card {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 14px;
|
||||
margin-bottom: 12px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.section-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
.section-tip {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.img-item {
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
.img-item-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 13px;
|
||||
color: #303133;
|
||||
margin-bottom: 8px;
|
||||
.img-item-ops {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
.el-icon {
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
color: #606266;
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
&.del:hover {
|
||||
color: #e1251b;
|
||||
}
|
||||
&.disabled {
|
||||
color: #dcdfe6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.img-preview {
|
||||
border: 1px dashed #dcdfe6;
|
||||
border-radius: 6px;
|
||||
padding: 6px;
|
||||
margin-bottom: 10px;
|
||||
img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
.img-placeholder {
|
||||
height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px dashed #dcdfe6;
|
||||
border-radius: 6px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.btn-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
.link-view {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.add-btn {
|
||||
width: 100%;
|
||||
}
|
||||
.setting-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 14px 0;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.setting-label {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
.setting-value {
|
||||
margin-left: 8px;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
.seg {
|
||||
display: inline-flex;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
.seg-item {
|
||||
padding: 6px 14px;
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
cursor: pointer;
|
||||
border-left: 1px solid #dcdfe6;
|
||||
transition: all 0.2s ease;
|
||||
&:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
&.active {
|
||||
background: #ecf5ff;
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
367
manager/src/views/page-decoration/wap/config-panel/PanelMenu.vue
Normal file
367
manager/src/views/page-decoration/wap/config-panel/PanelMenu.vue
Normal file
@@ -0,0 +1,367 @@
|
||||
<template>
|
||||
<div class="panel-menu">
|
||||
<div class="panel-header">
|
||||
<span class="panel-name">{{ res.name }}</span>
|
||||
<div class="panel-tabs">
|
||||
<div
|
||||
v-for="tab in tabs"
|
||||
:key="tab.key"
|
||||
class="panel-tab"
|
||||
:class="{ active: activeTab === tab.key }"
|
||||
@click="activeTab = tab.key"
|
||||
>
|
||||
{{ tab.title }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="activeTab === 'content'">
|
||||
<div class="panel-card">
|
||||
<div class="section-title">菜单设置</div>
|
||||
<div class="section-tip">每行展示 5 个,建议图标比例一致</div>
|
||||
<div class="menu-item-card" v-for="(item, index) in res.options.list" :key="index">
|
||||
<div class="img-item-head">
|
||||
<span>菜单 {{ index + 1 }}</span>
|
||||
<div class="img-item-ops">
|
||||
<el-icon
|
||||
:class="{ disabled: index === 0 }"
|
||||
@click="moveItem(index, -1)"
|
||||
><Top
|
||||
/></el-icon>
|
||||
<el-icon
|
||||
:class="{ disabled: index === res.options.list.length - 1 }"
|
||||
@click="moveItem(index, 1)"
|
||||
><Bottom
|
||||
/></el-icon>
|
||||
<el-icon class="del" @click="removeItem(index)"><Delete /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-item-body">
|
||||
<div class="icon-preview" v-if="item.img">
|
||||
<img :src="item.img" alt="" />
|
||||
</div>
|
||||
<div class="icon-placeholder" v-else>图标</div>
|
||||
<div class="menu-item-fields">
|
||||
<el-input v-model="item.title" size="small" maxlength="6" placeholder="菜单名称" />
|
||||
<div class="btn-row">
|
||||
<el-button plain type="primary" size="small" @click="pickImage(item)">选择图标</el-button>
|
||||
<el-button plain type="primary" size="small" @click="pickLink(item)">选择链接</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="link-view" v-if="item.url && item.url.___type">
|
||||
已选链接:{{ linkText(item.url) }}
|
||||
</div>
|
||||
</div>
|
||||
<el-button class="add-btn" plain type="primary" @click="addItem">添加菜单</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="panel-card">
|
||||
<div class="section-title">颜色设置</div>
|
||||
<color-field
|
||||
v-model="res.options.bgColor1"
|
||||
label="背景颜色"
|
||||
:default-value="defaults.bgColor1"
|
||||
/>
|
||||
<color-field
|
||||
v-model="res.options.bgColor2"
|
||||
label="渐变结束色"
|
||||
:default-value="defaults.bgColor2"
|
||||
/>
|
||||
<div class="section-tip">起始色与结束色一致时为纯色背景</div>
|
||||
<color-field
|
||||
v-model="res.options.textColor"
|
||||
label="文字颜色"
|
||||
:default-value="defaults.textColor"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="panel-card">
|
||||
<div class="section-title">边距设置</div>
|
||||
<slider-field v-model="res.options.paddingTop" label="上边距" :min="0" :max="100" />
|
||||
<slider-field v-model="res.options.paddingBottom" label="下边距" :min="0" :max="100" />
|
||||
<slider-field v-model="res.options.paddingX" label="左右边距" :min="0" :max="30" />
|
||||
<slider-field v-model="res.options.contentGap" label="内容间距" :min="0" :max="50" />
|
||||
<slider-field v-model="res.options.pageGap" label="页面间距" :min="0" :max="100" />
|
||||
</div>
|
||||
|
||||
<div class="panel-card">
|
||||
<div class="section-title">圆角设置</div>
|
||||
<slider-field v-model="res.options.borderRadius" label="背景圆角" :min="0" :max="50" />
|
||||
<slider-field v-model="res.options.imgRadius" label="内容圆角" :min="0" :max="50" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
|
||||
|
||||
<el-dialog v-model="picModelFlag" width="1200px" append-to-body destroy-on-close>
|
||||
<ossManage
|
||||
@callback="callbackSelected"
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
:max-select="1"
|
||||
ref="ossManage"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Top, Bottom, Delete } from "@element-plus/icons-vue";
|
||||
import ColorField from "../controls/ColorField.vue";
|
||||
import SliderField from "../controls/SliderField.vue";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
import ways from "@/components/lili-dialog/wap.js";
|
||||
|
||||
const DEFAULTS = {
|
||||
bgColor1: "#FFFFFF",
|
||||
bgColor2: "#FFFFFF",
|
||||
textColor: "#282828",
|
||||
paddingTop: 10,
|
||||
paddingBottom: 10,
|
||||
paddingX: 12,
|
||||
contentGap: 10,
|
||||
pageGap: 10,
|
||||
borderRadius: 0,
|
||||
imgRadius: 30,
|
||||
};
|
||||
|
||||
export default {
|
||||
name: "PanelMenu",
|
||||
hideDecorateTitle: true,
|
||||
components: { ColorField, SliderField, ossManage, Top, Bottom, Delete },
|
||||
props: {
|
||||
res: { type: Object, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaults: DEFAULTS,
|
||||
activeTab: "content",
|
||||
tabs: [
|
||||
{ key: "content", title: "内容" },
|
||||
{ key: "style", title: "样式" },
|
||||
],
|
||||
picModelFlag: false,
|
||||
currentItem: null,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.res.options = Object.assign({}, DEFAULTS, this.res.options);
|
||||
if (!Array.isArray(this.res.options.list)) {
|
||||
this.res.options.list = [];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
linkText(url) {
|
||||
const way = ways.find((e) => e.name === url.___type);
|
||||
const typeTitle = way ? way.title : "发现";
|
||||
const name =
|
||||
url.name || url.goodsName || url.memberName || url.title || "";
|
||||
return name ? `${typeTitle} - ${name}` : typeTitle;
|
||||
},
|
||||
pickImage(item) {
|
||||
this.currentItem = item;
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
const oss = this.$refs.ossManage;
|
||||
if (!oss) return;
|
||||
oss.selectImage = true;
|
||||
oss.fileType = "pic";
|
||||
oss.changeFileType();
|
||||
});
|
||||
},
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
if (val && val.url && this.currentItem) {
|
||||
this.currentItem.img = val.url;
|
||||
}
|
||||
},
|
||||
pickLink(item) {
|
||||
this.currentItem = item;
|
||||
const dialog = this.$refs.liliDialog;
|
||||
dialog.clearGoodsSelected();
|
||||
dialog.goodsFlag = false;
|
||||
dialog.flag = true;
|
||||
},
|
||||
selectedLink(val) {
|
||||
if (!this.currentItem) return;
|
||||
delete val.selected;
|
||||
delete val.intro;
|
||||
delete val.mobileIntro;
|
||||
this.currentItem.url = val;
|
||||
},
|
||||
moveItem(index, dir) {
|
||||
const list = this.res.options.list;
|
||||
const target = index + dir;
|
||||
if (target < 0 || target >= list.length) return;
|
||||
list.splice(target, 1, ...list.splice(index, 1, list[target]));
|
||||
},
|
||||
removeItem(index) {
|
||||
this.res.options.list.splice(index, 1);
|
||||
},
|
||||
addItem() {
|
||||
this.res.options.list.push({
|
||||
img: "",
|
||||
url: "",
|
||||
link: "",
|
||||
title: "标题",
|
||||
size: "88*88 (1:1)",
|
||||
model: "link",
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.panel-menu {
|
||||
background: #f5f7fa;
|
||||
margin: 0 -20px -120px;
|
||||
padding: 0 12px 120px;
|
||||
min-height: calc(100vh - 70px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 50px;
|
||||
margin-bottom: 12px;
|
||||
padding: 0 14px;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||
box-sizing: border-box;
|
||||
.panel-name {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
.panel-tabs {
|
||||
display: inline-flex;
|
||||
background: #f4f4f5;
|
||||
border-radius: 8px;
|
||||
padding: 3px;
|
||||
.panel-tab {
|
||||
padding: 5px 14px;
|
||||
font-size: 13px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
color: #606266;
|
||||
transition: all 0.2s ease;
|
||||
&.active {
|
||||
background: #ffffff;
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
}
|
||||
}
|
||||
.panel-card {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 14px;
|
||||
margin-bottom: 12px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.section-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
.section-tip {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.menu-item-card {
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.img-item-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 13px;
|
||||
color: #303133;
|
||||
margin-bottom: 8px;
|
||||
.img-item-ops {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
.el-icon {
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
color: #606266;
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
&.del:hover {
|
||||
color: #e1251b;
|
||||
}
|
||||
&.disabled {
|
||||
color: #dcdfe6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.menu-item-body {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
.icon-preview {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border: 1px dashed #dcdfe6;
|
||||
border-radius: 8px;
|
||||
padding: 3px;
|
||||
box-sizing: border-box;
|
||||
flex-shrink: 0;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
.icon-placeholder {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border: 1px dashed #dcdfe6;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.menu-item-fields {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
.btn-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
.link-view {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.add-btn {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -8,6 +8,23 @@ import PanelCoupon from "./PanelCoupon.vue";
|
||||
import PanelArticle from "./PanelArticle.vue";
|
||||
import PanelShop from "./PanelShop.vue";
|
||||
import PanelFlexOne from "./PanelFlexOne.vue";
|
||||
import PanelCarousel from "./PanelCarousel.vue";
|
||||
import PanelAdCube from "./PanelAdCube.vue";
|
||||
import PanelMenu from "./PanelMenu.vue";
|
||||
|
||||
// 图片魔方及其全部可选风格共用 PanelAdCube
|
||||
const adCubeTypes = [
|
||||
"tpl_ad_list",
|
||||
"leftOneRightTwo",
|
||||
"leftTwoRightOne",
|
||||
"flexThree",
|
||||
"flexTwo",
|
||||
"topOneBottomTwo",
|
||||
"topTwoBottomOne",
|
||||
"flexFive",
|
||||
"flexFour",
|
||||
"textPicture",
|
||||
];
|
||||
|
||||
export default {
|
||||
blank: PanelBlank,
|
||||
@@ -18,4 +35,7 @@ export default {
|
||||
article: PanelArticle,
|
||||
shop: PanelShop,
|
||||
flexOne: PanelFlexOne,
|
||||
carousel: PanelCarousel,
|
||||
menu: PanelMenu,
|
||||
...Object.fromEntries(adCubeTypes.map((t) => [t, PanelAdCube])),
|
||||
};
|
||||
|
||||
@@ -419,7 +419,7 @@ export const modelData = [
|
||||
|
||||
{
|
||||
type: "tpl_ad_list",
|
||||
name: "广告魔方",
|
||||
name: "图片魔方",
|
||||
icon: "Grid",
|
||||
group: "base",
|
||||
img: "md-image",
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
v-if="panelComponent"
|
||||
:is="panelComponent"
|
||||
:res="res"
|
||||
@handleDrawer="$emit('handleDrawer', $event)"
|
||||
/>
|
||||
<!-- 老组件遗留配置分支 -->
|
||||
<template v-else>
|
||||
|
||||
@@ -196,7 +196,8 @@ export default {
|
||||
];
|
||||
return groups.map((g) => ({
|
||||
...g,
|
||||
list: this.modelData.filter((m) => m.group === g.key),
|
||||
// drawer 标记的是图片魔方的可选风格,只在「选择风格」抽屉中出现,不在左侧组件列表展示
|
||||
list: this.modelData.filter((m) => m.group === g.key && !m.drawer),
|
||||
}));
|
||||
},
|
||||
},
|
||||
@@ -213,7 +214,12 @@ export default {
|
||||
},
|
||||
cloneModel(model) {
|
||||
const key = Date.now() + "_" + Math.ceil(Math.random() * 99999);
|
||||
const cloned = JSON.parse(JSON.stringify(model));
|
||||
// 图片魔方拖入画布时默认使用「左一右二」风格
|
||||
const source =
|
||||
model.type === "tpl_ad_list"
|
||||
? this.modelData.find((m) => m.type === "leftOneRightTwo") || model
|
||||
: model;
|
||||
const cloned = JSON.parse(JSON.stringify(source));
|
||||
return {
|
||||
...cloned,
|
||||
key,
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
margin: 20px 0;
|
||||
width: 375px;
|
||||
background: #ebebeb;
|
||||
min-height: 700px;
|
||||
min-height: calc(100vh - 200px);
|
||||
border-radius: 8px;
|
||||
overflow: visible;
|
||||
flex-shrink: 0;
|
||||
|
||||
28
manager/src/views/page-decoration/wap/template/cubeStyle.js
Normal file
28
manager/src/views/page-decoration/wap/template/cubeStyle.js
Normal file
@@ -0,0 +1,28 @@
|
||||
// 图片魔方各布局预览共用的样式字段(面板配置;老数据无字段时返回空对象,保持原样)
|
||||
// 画布 375px ≈ 750rpx,预览按数值一半像素呈现
|
||||
const px = (v) => `${(Number(v) || 0) / 2}px`;
|
||||
|
||||
export function cubeWrapperStyle(res) {
|
||||
if (!res || res.paddingTop === undefined) return {};
|
||||
const bg =
|
||||
!res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`;
|
||||
return {
|
||||
padding: `${px(res.paddingTop)} ${px(res.paddingX)} ${px(res.paddingBottom)}`,
|
||||
background: bg,
|
||||
borderRadius: px(res.borderRadius),
|
||||
marginBottom: px(res.pageGap),
|
||||
overflow: Number(res.borderRadius) > 0 ? "hidden" : "visible",
|
||||
};
|
||||
}
|
||||
|
||||
export function cubeImgStyle(res) {
|
||||
if (!res || res.imgRadius === undefined) return {};
|
||||
return { borderRadius: px(res.imgRadius), overflow: "hidden" };
|
||||
}
|
||||
|
||||
export function cubeGapStyle(res) {
|
||||
if (!res || res.contentGap === undefined) return {};
|
||||
return { gap: px(res.contentGap) };
|
||||
}
|
||||
@@ -1,18 +1,86 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<el-carousel class="carousel" v-if="res" autoplay :interval="5000" height="150px">
|
||||
<el-carousel-item v-for="(item, index) in res.list" :key="index">
|
||||
<div>
|
||||
<img class="image-mode" :src="item.img" />
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
<div class="layout" :style="wrapperStyle">
|
||||
<div class="banner-box" :style="cardMode ? {} : boxStyle">
|
||||
<el-carousel
|
||||
class="carousel"
|
||||
:class="{ 'card-mode': cardMode }"
|
||||
v-if="res"
|
||||
autoplay
|
||||
:interval="5000"
|
||||
height="150px"
|
||||
:type="cardMode ? 'card' : ''"
|
||||
:indicator-position="customIndicator ? 'none' : ''"
|
||||
@change="onChange"
|
||||
>
|
||||
<el-carousel-item v-for="(item, index) in res.list" :key="index">
|
||||
<div class="slide-wrap">
|
||||
<img class="image-mode" :style="boxStyle" :src="item.img" />
|
||||
</div>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
<div
|
||||
v-if="customIndicator && res.indicatorStyle !== 'none' && res.list.length > 1"
|
||||
class="indicator"
|
||||
:class="'pos-' + (res.indicatorPosition || 'center')"
|
||||
>
|
||||
<div
|
||||
v-for="(item, i) in res.list"
|
||||
:key="i"
|
||||
class="dot"
|
||||
:class="[res.indicatorStyle || 'round', { active: i === current }]"
|
||||
:style="i === current ? { background: indicatorColor } : {}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
title: "导航栏",
|
||||
props: ["res"],
|
||||
data() {
|
||||
return { current: 0 };
|
||||
},
|
||||
computed: {
|
||||
// 配置过样式字段才启用自定义样式/指示器,老数据保持原样
|
||||
customIndicator() {
|
||||
return this.res && this.res.indicatorStyle !== undefined;
|
||||
},
|
||||
cardMode() {
|
||||
return this.res && this.res.swiperStyle === "card";
|
||||
},
|
||||
indicatorColor() {
|
||||
return this.res.indicatorColorMode === "custom" && this.res.indicatorColor
|
||||
? this.res.indicatorColor
|
||||
: "#409eff";
|
||||
},
|
||||
wrapperStyle() {
|
||||
const res = this.res || {};
|
||||
if (res.paddingTop === undefined) return {};
|
||||
const bg =
|
||||
!res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`;
|
||||
// 画布 375px ≈ 750rpx,预览按一半像素呈现
|
||||
const px = (v) => `${(Number(v) || 0) / 2}px`;
|
||||
return {
|
||||
padding: `${px(res.paddingTop)} ${px(res.paddingX)} ${px(res.paddingBottom)}`,
|
||||
background: bg,
|
||||
borderRadius: px(res.borderRadius),
|
||||
marginBottom: px(res.pageGap),
|
||||
};
|
||||
},
|
||||
boxStyle() {
|
||||
const res = this.res || {};
|
||||
if (res.imgRadius === undefined) return {};
|
||||
return { borderRadius: `${(Number(res.imgRadius) || 0) / 2}px` };
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onChange(index) {
|
||||
this.current = index;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@@ -21,4 +89,61 @@ export default {
|
||||
.image-mode {
|
||||
height: 150px;
|
||||
}
|
||||
</style>
|
||||
.banner-box {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.slide-wrap {
|
||||
height: 100%;
|
||||
padding: 0 3px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.image-mode {
|
||||
width: 100%;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
// 卡片模式:卡片铺满轮播项,去掉 el-carousel 默认卡片白底阴影
|
||||
.card-mode {
|
||||
:deep(.el-carousel__item--card) {
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
:deep(.el-carousel__mask) {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
.indicator {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 2;
|
||||
&.pos-left {
|
||||
left: 12px;
|
||||
}
|
||||
&.pos-center {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
&.pos-right {
|
||||
right: 12px;
|
||||
}
|
||||
.dot {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
transition: background 0.3s ease;
|
||||
&.round {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
margin: 0 3px;
|
||||
}
|
||||
&.line {
|
||||
width: 12px;
|
||||
height: 3px;
|
||||
border-radius: 2px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,18 +1,31 @@
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<img class="image-mode" :src="res.list[0].img" alt="">
|
||||
<img class="image-mode" :src="res.list[1].img" alt="">
|
||||
<img class="image-mode" :src="res.list[2].img" alt="">
|
||||
<img class="image-mode" :src="res.list[3].img" alt="">
|
||||
<img class="image-mode" :src="res.list[4].img" alt="">
|
||||
<div class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<img class="image-mode" :style="imgStyle" :src="res.list[0].img" alt="">
|
||||
<img class="image-mode" :style="imgStyle" :src="res.list[1].img" alt="">
|
||||
<img class="image-mode" :style="imgStyle" :src="res.list[2].img" alt="">
|
||||
<img class="image-mode" :style="imgStyle" :src="res.list[3].img" alt="">
|
||||
<img class="image-mode" :style="imgStyle" :src="res.list[4].img" alt="">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./cubeStyle";
|
||||
|
||||
export default {
|
||||
title: "五列单行图片模块",
|
||||
props: ["res"],
|
||||
computed: {
|
||||
wrapperStyle() {
|
||||
return cubeWrapperStyle(this.res);
|
||||
},
|
||||
imgStyle() {
|
||||
return cubeImgStyle(this.res);
|
||||
},
|
||||
gapStyle() {
|
||||
return cubeGapStyle(this.res);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@@ -25,6 +38,7 @@ export default {
|
||||
background-size: cover;
|
||||
}
|
||||
img {
|
||||
width: 67px !important;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<img class="image-mode" :src="res.list[0].img" alt="">
|
||||
<img class="image-mode" :src="res.list[1].img" alt="">
|
||||
<img class="image-mode" :src="res.list[2].img" alt="">
|
||||
<img class="image-mode" :src="res.list[3].img" alt="">
|
||||
<div class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<img class="image-mode" :style="imgStyle" :src="res.list[0].img" alt="">
|
||||
<img class="image-mode" :style="imgStyle" :src="res.list[1].img" alt="">
|
||||
<img class="image-mode" :style="imgStyle" :src="res.list[2].img" alt="">
|
||||
<img class="image-mode" :style="imgStyle" :src="res.list[3].img" alt="">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./cubeStyle";
|
||||
|
||||
export default {
|
||||
title: "四列单行图片模块",
|
||||
props: ["res"],
|
||||
|
||||
computed: {
|
||||
wrapperStyle() {
|
||||
return cubeWrapperStyle(this.res);
|
||||
},
|
||||
imgStyle() {
|
||||
return cubeImgStyle(this.res);
|
||||
},
|
||||
gapStyle() {
|
||||
return cubeGapStyle(this.res);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@@ -28,6 +40,7 @@ export default {
|
||||
}
|
||||
|
||||
img{
|
||||
width: 84px !important;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,16 +1,29 @@
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<img class="image-mode" :src="res.list[0].img" alt="">
|
||||
<img class="image-mode" :src="res.list[1].img" alt="">
|
||||
<img class="image-mode" :src="res.list[2].img" alt="">
|
||||
<div class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<img class="image-mode" :style="imgStyle" :src="res.list[0].img" alt="">
|
||||
<img class="image-mode" :style="imgStyle" :src="res.list[1].img" alt="">
|
||||
<img class="image-mode" :style="imgStyle" :src="res.list[2].img" alt="">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./cubeStyle";
|
||||
|
||||
export default {
|
||||
title: "三列单行图片模块",
|
||||
props: ["res"],
|
||||
computed: {
|
||||
wrapperStyle() {
|
||||
return cubeWrapperStyle(this.res);
|
||||
},
|
||||
imgStyle() {
|
||||
return cubeImgStyle(this.res);
|
||||
},
|
||||
gapStyle() {
|
||||
return cubeGapStyle(this.res);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@@ -24,6 +37,7 @@ export default {
|
||||
background-size: cover;
|
||||
}
|
||||
img{
|
||||
width: 111px !important;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,19 +1,32 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="flex-two">
|
||||
<div class="flex-item">
|
||||
<div class="layout" :style="wrapperStyle">
|
||||
<div class="flex-two" :style="gapStyle">
|
||||
<div class="flex-item" :style="imgStyle">
|
||||
<img :src="res.list[0].img" alt />
|
||||
</div>
|
||||
<div class="flex-item">
|
||||
<div class="flex-item" :style="imgStyle">
|
||||
<img :src="res.list[1].img" alt />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./cubeStyle";
|
||||
|
||||
export default {
|
||||
title: "两张横图",
|
||||
props: ["res"],
|
||||
computed: {
|
||||
wrapperStyle() {
|
||||
return cubeWrapperStyle(this.res);
|
||||
},
|
||||
imgStyle() {
|
||||
return cubeImgStyle(this.res);
|
||||
},
|
||||
gapStyle() {
|
||||
return cubeGapStyle(this.res);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@@ -25,10 +38,11 @@ export default {
|
||||
overflow: hidden;
|
||||
}
|
||||
.flex-item {
|
||||
width: 50%;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
> img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="view-height-150">
|
||||
<div class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<div class="view-height-150" :style="imgStyle">
|
||||
<img class="image-mode" :src="res.list[0].img" />
|
||||
</div>
|
||||
<div class="view-height-150">
|
||||
<div class="view-height-75">
|
||||
<div class="view-height-75" :style="imgStyle">
|
||||
<img class="image-mode" :src="res.list[1].img" alt />
|
||||
</div>
|
||||
<div class="view-height-75">
|
||||
<div class="view-height-75" :style="imgStyle">
|
||||
<img class="image-mode" :src="res.list[2].img" alt />
|
||||
</div>
|
||||
</div>
|
||||
@@ -16,9 +16,22 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./cubeStyle";
|
||||
|
||||
export default {
|
||||
title: "左一右二",
|
||||
props: ["res"]
|
||||
props: ["res"],
|
||||
computed: {
|
||||
wrapperStyle() {
|
||||
return cubeWrapperStyle(this.res);
|
||||
},
|
||||
imgStyle() {
|
||||
return cubeImgStyle(this.res);
|
||||
},
|
||||
gapStyle() {
|
||||
return cubeGapStyle(this.res);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@@ -30,5 +43,8 @@ export default {
|
||||
|
||||
background-size: cover;
|
||||
}
|
||||
.view-height-150 {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,24 +1,37 @@
|
||||
|
||||
<template>
|
||||
<div class="layout ">
|
||||
<div class="layout " :style="[wrapperStyle, gapStyle]">
|
||||
<div class="view-height-150">
|
||||
<div class="view-height-75">
|
||||
<div class="view-height-75" :style="imgStyle">
|
||||
<img class="image-mode" :src="res.list[0].img" alt />
|
||||
</div>
|
||||
<div class="view-height-75">
|
||||
<div class="view-height-75" :style="imgStyle">
|
||||
<img class="image-mode" :src="res.list[1].img" alt />
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-height-150">
|
||||
<div class="view-height-150" :style="imgStyle">
|
||||
<img class="image-mode" style="height:150px;" :src="res.list[2].img" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./cubeStyle";
|
||||
|
||||
export default {
|
||||
title: "左二右一",
|
||||
props: ["res"]
|
||||
props: ["res"],
|
||||
computed: {
|
||||
wrapperStyle() {
|
||||
return cubeWrapperStyle(this.res);
|
||||
},
|
||||
imgStyle() {
|
||||
return cubeImgStyle(this.res);
|
||||
},
|
||||
gapStyle() {
|
||||
return cubeGapStyle(this.res);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@@ -31,6 +44,9 @@ export default {
|
||||
|
||||
background-size: cover;
|
||||
}
|
||||
.view-height-150 {
|
||||
min-width: 0;
|
||||
}
|
||||
.view-height-75 {
|
||||
.image-mode {
|
||||
height: 75px;
|
||||
|
||||
@@ -1,26 +1,49 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="menu-list">
|
||||
<div class="layout" :style="wrapperStyle">
|
||||
<div class="menu-list" :class="{ styled: styled }" :style="listStyle">
|
||||
<div class="menu-item" v-for="(item, index) in res.list" :key="index">
|
||||
<div>
|
||||
<div class="menu-img-wrap" :style="imgStyle">
|
||||
<img class="menu-img" :src="item.img" alt="" />
|
||||
</div>
|
||||
<div class="menu-title">{{ item.title }}</div>
|
||||
<div class="menu-title" :style="titleStyle">{{ item.title }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { cubeWrapperStyle, cubeImgStyle } from "./cubeStyle";
|
||||
|
||||
export default {
|
||||
props: ["res"],
|
||||
computed: {
|
||||
// 面板保存过样式字段才启用自定义样式,老数据保持原样
|
||||
styled() {
|
||||
return this.res && this.res.paddingTop !== undefined;
|
||||
},
|
||||
wrapperStyle() {
|
||||
return cubeWrapperStyle(this.res);
|
||||
},
|
||||
imgStyle() {
|
||||
return cubeImgStyle(this.res);
|
||||
},
|
||||
listStyle() {
|
||||
return this.styled
|
||||
? { rowGap: `${(Number(this.res.contentGap) || 0) / 2}px` }
|
||||
: {};
|
||||
},
|
||||
titleStyle() {
|
||||
return this.styled && this.res.textColor
|
||||
? { color: this.res.textColor }
|
||||
: {};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
.menu-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> .menu-item {
|
||||
@@ -28,6 +51,16 @@ export default {
|
||||
width: 20%;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
&.styled > .menu-item {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.menu-img-wrap {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
.menu-img {
|
||||
width: 44px;
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="view-list">
|
||||
<div class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<div class="view-list" :style="gapStyle">
|
||||
<div class="view-item">
|
||||
<div class="-item-tilte">{{res.list[0].title}}</div>
|
||||
<div class="-item-image">
|
||||
<div class="-item-image" :style="imgStyle">
|
||||
<img :src="res.list[0].img" alt />
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-item">
|
||||
<div class="-item-tilte">{{res.list[1].title}}</div>
|
||||
<div class="-item-image">
|
||||
<div class="-item-image" :style="imgStyle">
|
||||
<img :src="res.list[1].img" alt />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-list">
|
||||
<div class="view-list" :style="gapStyle">
|
||||
<div class="view-item">
|
||||
<div class="-item-tilte">{{res.list[2].title}}</div>
|
||||
<div class="-item-image">
|
||||
<div class="-item-image" :style="imgStyle">
|
||||
<img :src="res.list[2].img" alt />
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-item">
|
||||
<div class="-item-tilte">{{res.list[3].title}}</div>
|
||||
<div class="-item-image">
|
||||
<div class="-item-image" :style="imgStyle">
|
||||
<img :src="res.list[3].img" alt />
|
||||
</div>
|
||||
</div>
|
||||
@@ -33,9 +33,22 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./cubeStyle";
|
||||
|
||||
export default {
|
||||
title: "文字图片模板",
|
||||
props: ["res"]
|
||||
props: ["res"],
|
||||
computed: {
|
||||
wrapperStyle() {
|
||||
return cubeWrapperStyle(this.res);
|
||||
},
|
||||
imgStyle() {
|
||||
return cubeImgStyle(this.res);
|
||||
},
|
||||
gapStyle() {
|
||||
return cubeGapStyle(this.res);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@@ -75,7 +88,8 @@ export default {
|
||||
border: 1px solid #ededed;
|
||||
|
||||
> .view-item {
|
||||
width: 50%;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
> .view-item:nth-of-type(1) {
|
||||
> .-item-tilte {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="view-width-100">
|
||||
<div class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<div class="view-width-100" :style="imgStyle">
|
||||
<img class="image-mode" :src="res.list[0].img" />
|
||||
</div>
|
||||
<div class="view-width-100">
|
||||
<div class="view-height-85">
|
||||
<div class="view-width-100" :style="gapStyle">
|
||||
<div class="view-height-85" :style="imgStyle">
|
||||
<img class="image-mode" :src="res.list[1].img" alt />
|
||||
</div>
|
||||
<div class="view-height-85">
|
||||
<div class="view-height-85" :style="imgStyle">
|
||||
<img class="image-mode" :src="res.list[2].img" alt />
|
||||
</div>
|
||||
</div>
|
||||
@@ -16,9 +16,22 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./cubeStyle";
|
||||
|
||||
export default {
|
||||
title: "上一下二",
|
||||
props: ["res"]
|
||||
props: ["res"],
|
||||
computed: {
|
||||
wrapperStyle() {
|
||||
return cubeWrapperStyle(this.res);
|
||||
},
|
||||
imgStyle() {
|
||||
return cubeImgStyle(this.res);
|
||||
},
|
||||
gapStyle() {
|
||||
return cubeGapStyle(this.res);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@@ -28,7 +41,6 @@ export default {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-size: cover;
|
||||
height: 170px;
|
||||
flex-direction: column;
|
||||
}
|
||||
img {
|
||||
@@ -42,6 +54,7 @@ img {
|
||||
}
|
||||
.view-height-85 {
|
||||
padding: 0 1px;
|
||||
width: 50%;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,23 +1,36 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="view-width-100">
|
||||
<div class="view-height-85">
|
||||
<div class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<div class="view-width-100" :style="gapStyle">
|
||||
<div class="view-height-85" :style="imgStyle">
|
||||
<img class="image-mode" :src="res.list[0].img" alt />
|
||||
</div>
|
||||
<div class="view-height-85">
|
||||
<div class="view-height-85" :style="imgStyle">
|
||||
<img class="image-mode" :src="res.list[1].img" alt />
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-width-100">
|
||||
<div class="view-width-100" :style="imgStyle">
|
||||
<img class="image-mode" :src="res.list[2].img" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./cubeStyle";
|
||||
|
||||
export default {
|
||||
title: "上二下一",
|
||||
props: ["res"],
|
||||
computed: {
|
||||
wrapperStyle() {
|
||||
return cubeWrapperStyle(this.res);
|
||||
},
|
||||
imgStyle() {
|
||||
return cubeImgStyle(this.res);
|
||||
},
|
||||
gapStyle() {
|
||||
return cubeGapStyle(this.res);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@@ -27,7 +40,6 @@ export default {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-size: cover;
|
||||
height: 170px;
|
||||
flex-direction: column;
|
||||
}
|
||||
img {
|
||||
@@ -42,6 +54,7 @@ img {
|
||||
}
|
||||
.view-height-85 {
|
||||
padding: 0 1px;
|
||||
width: 50%;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user