<template>
  <el-form-item :label="label" :rules="requireRule( required, label)" :prop="prop">
    <el-select ref="select" v-if="(!disabled && !parent.disabled) || readOnly" class="common-select" placeholder=""
               v-loadmore="loadmore" @clear="handleClear" :value="values" :multiple="multiple" :clearable="clearable"
               :collapse-tags="collapseTags" filterable :filter-method="lazy? handleFilter: null"
               @visible-change="initData" @change="handleChange" :disabled="readOnly">
      <el-option v-if="lazy" v-for="item in selectedList.filter(({value}) => !exclude || !exclude.includes(value))"
                 :key="`hidden:${item.value}`" :value="item[valueProp]" :label="item.label" style="display:none" />
      <el-option v-for="item in options.filter(({value}) => !exclude || !exclude.includes(value))"
                 :key="item.value" :value="item[valueProp]" :label="item.label" />
      <p v-if="loading" class="common-select-loadmore">{{$t('oqcMessage.loading')}}</p>

      <template #empty v-if="loading">
        <p class="common-select-loadmore">{{$t('oqcMessage.loading')}}</p>
      </template>
    </el-select>
    <TextBox v-else :text="text" />
  </el-form-item>
</template>

<script>
import mixin from "./mixin";
import { mapGetters } from "vuex";

export default {
  mixins: [mixin],
  props: {
    // 查询选项的方法，必传
    optionMethod: {
      type: Function,
      required: true,
    },
    // 类型参数（目前只有字典会用）
    type: {
      type: String,
    },
    // 额外参数（其他的查询条件通过此对象传入）
    extra: {
      type: Object,
      default: () => {},
    },
    extraData: {
      type: Function,
      default: () => {},
    },
    multiple: {
      type: Boolean,
      default: false,
    },
    collapseTags: {
      type: Boolean,
      default: false,
    },
    clearable: {
      type: Boolean,
      default: true,
    },
    exclude: {},
    lazy: {
      type: Boolean,
      default: true,
    },
    readOnly: {
      type: Boolean,
      default: false,
    },
    showOther: {
      type: Boolean,
      default: false,
    },
    labelProp: {
      type: String,
    },
    valueProp: {
      type: String,
      default: "value",
    },
  },
  data() {
    return {
      pageSize: 10,
      pageNum: 1,
      options: [],
      keyword: undefined,
      loading: false,
      nomore: false,
    };
  },
  created() {
    if (!this.lazy) {
      this.options =
        this.dict[this.type].filter((item) => item.status !== "1") || [];
    }
  },
  computed: {
    ...mapGetters(["dict"]),
    _labelProp() {
      return this.labelProp || `${this.prop}Name`;
    },
    text() {
      const value = this.parent.model[this.prop];
      const name = this.parent.model[this._labelProp];
      if (!this.lazy) {
        const label = value
          ? value
              .split(",")
              .map((val) => this.optionNameMap[val])
              .join(",")
          : undefined;
        return name || label || value;
      }
      return name || value;
    },
    values() {
      const values = this.parent.model[this.prop];
      if (!this.multiple) {
        return values;
      }
      if (!values || values === "") return [];
      return values.split(",");
    },
    selectedList() {
      if (!this.lazy) {
        return [];
      }
      if (!this.multiple) {
        return [
          {
            value: this.parent.model[this.prop],
            label: this.parent.model[this._labelProp],
          },
        ];
      }
      const names = this.parent.model[this._labelProp];
      const namesArr = names ? names.split(",") : [];
      return this.values.map((val, index) => ({
        value: val,
        label: namesArr[index],
      }));
    },
    optionNameMap() {
      const result = {};
      const options = [...this.options, ...this.selectedList];
      options.forEach(({ label, value }) => {
        result[value] = label;
      });
      return result;
    },
  },
  methods: {
    handleChange(val) {
      const value = this.multiple ? val.join(",") : val;
      const label = value
        ? value
            .split(",")
            .map((val) => this.optionNameMap[val])
            .join(",")
        : undefined;

      this.$set(this.parent.model, this.prop, value);
      // 如果是查询表单则不设置name值
      if (this.parent.size !== "small") {
        this.$set(this.parent.model, this._labelProp, label);
      }
      this.$emit("change", value);
    },
    handleClear() {
      this.$set(this.parent.model, this.prop, "");
      this.$set(this.parent.model, this._labelProp, "");
      this.$emit("clear")
    },
    async initData(isOpen) {
      if (!this.lazy || this.loading) {
        return;
      }
      if (isOpen) {
        this.pageNum = 1;
        this.options = [];
        this.nomore = false;
        this.loading = true;
        await this.getOption();
      } else {
        this.keyword = undefined;
      }
    },
    handleFilter(query) {
      this.keyword = query;
      this.initData(true);
    },
    loadmore() {
      if (this.nomore || this.loading || !this.lazy) {
        return;
      }
      this.loading = true;
      this.pageNum = this.pageNum + 1;
      this.getOption();
    },
    async getOption() {
      let res;
      try {
        res = await this.optionMethod({
          type: this.type,
          keyword: this.keyword,
          pageNum: this.pageNum,
          pageSize: this.pageSize,
          lang: localStorage.getItem("lang"),
          ...this.extra,
          ...this.extraData(),
        });
      } catch (e) {
        this.loading = false;
        return;
      }

      if (res.code !== 200) {
        this.$refs.select.blur();
        return;
      }

      if (this.showOther) {
        this.options.push(...res.data);
      } else {
        this.options.push(...res.rows);
      }
      this.nomore = res.total <= this.options.length;
      this.loading = false;
    },
  },
};
</script>

<style>
.common-select,
.common-select > .el-input,
.common-select > .el-input > .el-input__inner {
  width: 220px !important;
}
.common-select .el-input__icon {
  line-height: 36px;
}
.common-select-loadmore {
  text-align: center;
  font-size: 12px;
  color: #5e6d82;
  line-height: 1.2em;
  margin: 7px;
}
</style>
