效果预览
父组件
<template>
<div id="assoModel">
<!-- 标题 -->
<div class="addOptions title-container">
<div
class="grid-content title-gap"
v-for="title in titleList"
:key="title.label"
:class="title.class"
>
<span>{{ title.label }}</span>
</div>
</div>
<!-- 内容 -->
<div class="addOptions">
<div
v-for="(itemData, itemIndex) in tableData"
:key="itemData.value"
class="container"
:class="{ 'remove-line': tableData.length == 1 }"
>
<content-table
:tableData="tableData"
:itemData="itemData"
:itemIndex="itemIndex"
></content-table>
</div>
</div>
<div class="footer">
<el-button type="primary" @click="addOptinons">添加关联条件</el-button>
</div>
</div>
</template>
<script>
import ContentTable from "./content";
export default {
components: {
ContentTable,
},
data() {
return {
titleList: [
{
label: "数据集字段",
class: "title-dataSet",
},
{
label: "关联方式",
class: "type",
},
{
label: "关联维表名",
class: "title-tableName",
},
{
label: "关联字段",
class: "fields",
},
{
label: "操作",
class: "handle",
},
],
tableData: [
{
dataSet: {
name: "data-set",
class: "dataSet",
data: [
{
label: "商品ID",
value: "id",
},
{
label: "价格",
value: "price",
},
{
label: "数量",
value: "number",
},
],
},
type: {
name: "type",
class: "mode",
data: ["关联方式"],
},
table: {
name: "tables",
class: "tables",
data: [
{
label: "airbnb_listings",
value: "airbnb_listings",
},
{
label: "aipl_user_online",
value: "aipl_user_online",
},
{
label: "airbnb_calendar",
value: "airbnb_calendar",
},
],
},
field: {
name: "field",
class: "fields",
data: [
{
label: "date",
value: "date",
},
{
label: "listing_id",
value: "listing_id",
},
{
label: "price",
value: "price",
},
],
},
delete: {
name: "delete",
class: "delete",
},
},
],
};
},
methods: {
addOptinons() {
this.tableData.push({
dataSet: {
name: "data-set",
class: "dataSet",
data: [
{
label: "商品ID",
value: "id",
},
{
label: "价格",
value: "price",
},
{
label: "数量",
value: "number",
},
],
},
type: {
name: "type",
class: "mode",
},
table: {
name: "tables",
class: "tables",
},
field: {
name: "field",
class: "fields",
data: [
{
label: "date",
value: "date",
},
{
label: "listing_id",
value: "listing_id",
},
{
label: "price",
value: "price",
},
],
},
delete: {
name: "delete",
class: "delete",
},
});
},
},
};
</script>
<style scoped>
#assoModel {
min-height: 500px;
}
#assoModel .addOptions .container {
padding: 2px 10px;
display: flex;
flex-wrap: nowrap;
border: none;
}
#assoModel .addOptions {
position: relative;
height: auto;
}
#assoModel .grid-content {
display: inline-block;
height: auto;
}
#assoModel .title-container {
padding: 5px 10px;
display: flex;
flex-wrap: nowrap;
/* justify-content: space-around; */
}
#assoModel .grid-content.dataSet {
23%;
text-align: center;
}
#assoModel .grid-content.title-dataSet {
23%;
text-align: center;
}
#assoModel .grid-content.type {
10%;
text-align: center;
}
#assoModel .grid-content.tableName {
24%;
text-align: center;
}
#assoModel .grid-content.title-tableName {
24%;
text-align: center;
}
#assoModel .grid-content.fields {
16%;
text-align: center;
}
#assoModel .grid-content.handle {
10%;
text-align: center;
}
#assoModel .title-gap {
margin-right: 30px;
}
#assoModel .el-select {
100%;
}
#assoModel .grid-content.mode {
10%;
height: calc(100% - 20px);
text-align: center;
}
#assoModel .grid-content.tables {
24%;
height: calc(100% - 20px);
text-align: center;
}
#assoModel .footer {
padding: 10px;
}
#assoModel .footer .el-button--primary {
100%;
background-color: #476dbe;
border-color: #476dbe;
}
</style>
<style >
.remove-line.container:last-child
#add-relation
.el-input.el-input--suffix:after {
0px;
}
.remove-line.container:last-child #field .el-input.el-input--suffix:before {
0px;
}
</style>
子组件
content.vue
<template>
<div style=" 100%">
<div
v-for="item in itemData"
:key="item.name"
class="grid-content title-gap"
:class="item.class"
>
<component
v-if="item.name"
:is="item.name"
:itemData="item"
:itemIndex="itemIndex"
:tableData="tableData"
:ref="item.name"
></component>
</div>
</div>
</template>
</template>
<script>
export default {
props: {
tableData: Array,
itemData: Object,
itemIndex: Number,
},
data() {
return {
name: "data-set",
};
},
};
</script>
<style scoped>
.grid-content {
display: inline-block;
height: auto;
}
.title-gap {
margin-right: 30px;
}
.grid-content.dataSet {
23%;
text-align: center;
}
.grid-content.mode {
10%;
height: calc(100% - 20px);
text-align: center;
}
.grid-content.mode .mode-content {
position: absolute;
margin: auto;
top: 0px;
bottom: 0;
height: 30px;
9.5%;
border: 1px solid #ccc;
}
.grid-content.tables {
24%;
height: calc(100% - 20px);
text-align: center;
}
.grid-content.fields {
16%;
text-align: center;
}
.grid-content.delete {
6%;
/* 10%; */
text-align: center;
}
</style>
dataset.vue
<template>
<!-- 数据集字段 -->
<div id="add-relation">
<el-select v-model="dataSet" placeholder="请选择">
<el-option
v-for="item in itemData.data"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</div>
</template>
<script>
export default {
props: { itemData: Object },
data() {
return {
dataSet: [],
};
},
};
</script>
<style>
#add-relation .el-input.el-input--suffix:after {
content: "";
10px;
height: 49px;
border-right: 1px solid #ccc;
border-top: 1px solid #ccc;
position: absolute;
top: 17px;
}
#add-relation .el-input.el-input--suffix:before {
position: absolute;
content: "";
5px;
height: 100%;
background-color: #42a948;
}
#assoModel
.container:last-child
#add-relation
.el-input.el-input--suffix:after {
height: 0px;
}
</style>
delete.vue
<template>
<!-- 操作 -->
<div class="grid-content">
<span v-html="svgImg.delete" @click="handleDelete" class="iconfont"></span>
</div>
</template>
<script>
export default {
props: {
tableData: Array,
itemIndex: Number,
},
data() {
return {
svgImg: this.$svgImg,
};
},
methods: {
handleDelete() {
if (this.tableData.length <= 1) {
return;
}
this.tableData.splice(this.itemIndex, 1);
},
},
};
</script>
<style scoped>
</style>
field.vue
<template>
<!-- 关联字段 -->
<div id="field">
<el-select v-model="selectdField" placeholder="">
<el-option
v-for="item in itemData.data"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</div>
</template>
<script>
export default {
props: {
itemData: Object,
},
data() {
return {
selectdField: [],
};
},
};
</script>
<style>
#field .el-input.el-input--suffix:before {
content: "";
10px;
height: 49px;
border-left: 1px solid #ccc;
border-top: 1px solid #ccc;
position: absolute;
top: 17px;
left: -10px;
}
#field .el-input.el-input--suffix:after {
position: absolute;
content: "";
5px;
height: 100%;
background-color: #42a948;
left: 0px;
}
#assoModel .container:last-child #field .el-input.el-input--suffix:before {
height: 0px;
}
</style>
tables.vue
<template>
<!-- 关联维表名 -->
<div class="tables-content" v-if="itemData && itemData.data">
<el-select v-model="selectdField" placeholder="请选择">
<el-option
v-for="item in itemData.data"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</div>
</template>
<script>
export default {
props: {
itemData: Object,
},
data() {
return {
selectdField: "",
};
},
};
</script>
<style scoped>
.grid-content.tables .tables-content {
position: absolute;
margin: auto;
top: 0px;
bottom: 0;
height: 30px;
23.5%;
top: -4px;
}
/* 关联维表名 */
.tables-content:after {
position: absolute;
content: "";
15px;
height: 1px;
background-color: #ccc;
right: -20px;
top: 19.5px;
}
</style>
type.vue
<template>
<!-- 关联方式 -->
<div id="type-container" v-if="itemData && itemData.data">
<div class="mode-content">
<el-select
v-model="selectedTypeLabel"
placeholder=""
@change="changeSelection"
ref="select"
>
<el-option
v-for="item in typeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
>
<img :src="item.label" class="imgSize" />
</el-option>
</el-select>
</div>
</div>
</template>
<script>
export default {
props: {
itemData: Object,
tableData: Array,
},
data() {
return {
svgImg: this.$svgImg,
selectedType: "",
selectedTypeLabel: "",
typeOptions: [
{
label: require("../../../assets/imgs/left-join.png"),
value: "left",
},
{
label: require("../../../assets/imgs/inner-join.png"),
value: "inner",
},
],
};
},
mounted() {
this.changeSelection("inner");
},
methods: {
changeSelection(e) {
this.itemData.selectedTypeLabel = this.$lodash.clone(
this.selectedTypeLabel
);
this.selectedTypeLabel = "";
this.itemData;
for (let index in this.typeOptions) {
let obj = this.typeOptions[index];
if (obj.value == e) {
console.log("ffff");
this.$refs["select"].$el.children[0].children[0].setAttribute(
"style",
"background:url(" +
obj.label +
") no-repeat;color:#fff;text-indent: -9999px;background-position: 18% center;background-size:contain;background-size:43%"
);
}
}
},
},
watch: {
tableData: {
handler(e) {
// console.log(e);
console.log("watch");
if (this.itemData.selectedTypeLabel) {
this.changeSelection(this.itemData.selectedTypeLabel);
}
},
deep: true,
},
},
};
</script>
<style scoped>
.mode-content {
position: absolute;
margin: auto;
top: 0px;
bottom: 0;
height: 30px;
9.5%;
top: -4px;
}
.type-switch {
position: absolute;
100%;
height: 55px;
border: 1px solid #ddd;
z-index: 9999;
top: 33px;
background-color: #fff;
}
.type-switch .switch-icon-left {
position: absolute;
100%;
height: 50%;
left: 0px;
}
.type-switch .switch-icon-inner {
position: absolute;
100%;
height: 50%;
left: 0px;
bottom: 0px;
}
.imgSize {
30px;
}
</style>
<style >
.switch-icon-color > svg {
font-size: 15px;
24px;
height: 30px;
fill: #09c;
}
#type-container .el-input.el-input--suffix:before {
position: absolute;
content: "";
15px;
height: 1px;
background-color: #ccc;
left: -20px;
top: 19px;
}
</style>