Element-UI实现数据库表关联模型

效果预览

父组件

<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>
原文地址:https://www.cnblogs.com/xxr0218/p/14096868.html