<template>
  <component
    :is="!!topLabel ? 'BorderLabel' : 'div'"
    :label="topLabel"
    :icon="icon"
    :model-value="isCollapse"
  >
    <div class="array-editor">
      <div v-for="(item, index) in items" :key="index" class="row items-center">
        <template v-if="optionsKeys.length">
          <div
            v-for="key in optionsKeys"
            :key="key.value"
            :class="[
              key.width ? `col-${key.width}` : 'col',
              optionsKeys.length > 1 ? 'q-pr-sm' : '',
            ]"
          >
            <VariableInput
              :model-value="item[key.value] || key.defaultValue"
              :label="key.label"
              :no-icon="true"
              :placeholder="key.placeholder"
              :options="key.options"
              @update:model-value="
                (val) => updateItemKeyValue(index, key.value, val)
              "
            />
          </div>
        </template>
        <template v-else>
          <div class="col">
            <VariableInput
              :model-value="item"
              :label="`${label || '项目'} ${index + 1}`"
              :icon="icon || 'code'"
              :placeholder="placeholder"
              :options="{
                items: options.items,
              }"
              @update:model-value="(val) => updateItemValue(index, val)"
            />
          </div>
        </template>
        <div class="col-auto">
          <div class="btn-container">
            <template v-if="items.length === 1">
              <q-btn
                flat
                dense
                size="sm"
                icon="add"
                class="center-btn"
                @click="addItem"
              />
            </template>
            <template v-else-if="index === items.length - 1">
              <q-btn
                flat
                dense
                size="sm"
                icon="remove"
                class="top-btn"
                @click="removeItem(index)"
              />
              <q-btn
                flat
                dense
                size="sm"
                icon="add"
                class="bottom-btn"
                @click="addItem"
              />
            </template>
            <template v-else>
              <q-btn
                flat
                dense
                size="sm"
                icon="remove"
                class="center-btn"
                @click="removeItem(index)"
              />
            </template>
          </div>
        </div>
      </div>
    </div>
  </component>
</template>

<script>
/**
 * 数组编辑器组件
 * @description 支持单值数组和多键对象数组的编辑
 *
 * @property {Array} modelValue - 绑定的数组值
 * @property {String} label - 输入框标签
 * @property {String} icon - 输入框图标
 * @property {Object} options - 配置选项
 *
 * @property {String[]} [options.keys] - 多键对象模式的键名列表
 * @property {String[]} [options.keys.value] - 元素为对象时，对象的键名
 * @property {String[]} [options.keys.label] - 对应varInput的label
 * @property {String[]} [options.keys.placeholder] - 对应varInput的placeholder
 * @property {String[]} [options.keys.defaultValue] - 对应varInput的defaultValue
 * @property {Object} [options.keys.options] - 对应varInput的options
 *
 * @property {String[]} [options.items] - 下拉选择模式的选项列表
 * @property {Object} [options.defaultValue] - 初始化时，默认的值，决定显示几个元素，对应元素内容
 * @example
 * // 基础数组
 * [
 *   newVarInputVal("str", "张三")
 * ]
 *
 * // 多键对象数组
 * options.keys = ['name', 'age', 'email']
 *
 * options.keys= [
 *   {
 *     label: "姓名",
 *     value: "name",
 *     placeholder: "姓名",
 *     options: {
 *       items: ["张三", "李四", "王五"],
 *       multiSelect: true,
 *     },
 *   }
 * ]
 *
 * [
 *   {
 *     name: newVarInputVal("str", "张三"),
 *     age: newVarInputVal("str", "18"),
 *     email: newVarInputVal("str", "zhangsan@example.com")
 *   }
 * ]
 *
 * // 下拉选择模式
 * options.items = ['选项1', '选项2', '选项3']
 * [
 *   newVarInputVal("str", "选项1"),
 *   newVarInputVal("str", "选项2"),
 *   newVarInputVal("str", "选项3")
 * ]
 */
import { defineComponent } from "vue";
import VariableInput from "components/composer/common/VariableInput.vue";
import { newVarInputVal } from "js/composer/varInputValManager";
import BorderLabel from "components/composer/common/BorderLabel.vue";

export default defineComponent({
  name: "ArrayEditor",
  components: {
    VariableInput,
    BorderLabel,
  },
  props: {
    modelValue: {
      type: Array,
      required: true,
      default: () => [],
    },
    label: {
      type: String,
      default: "",
    },
    topLabel: {
      type: String,
      default: "",
    },
    icon: {
      type: String,
      default: "",
    },
    isCollapse: {
      type: Boolean,
      default: true,
    },
    options: {
      type: Object,
      default: () => ({}),
    },
    placeholder: {
      type: String,
      default: "",
    },
  },
  emits: ["update:modelValue"],
  computed: {
    items() {
      return this.modelValue.length ? this.modelValue : this.initializeItems();
    },
    optionsKeys() {
      return (
        this.options?.keys?.map((key) => {
          return {
            ...key,
            value: key.value || key,
            label: key.label || key,
          };
        }) || []
      );
    },
  },
  methods: {
    initializeItems() {
      if (this.optionsKeys?.length) {
        const item = {};
        this.optionsKeys.forEach((key) => {
          item[key] = newVarInputVal("str");
        });
        return [item];
      }

      return [newVarInputVal("str")];
    },
    /**
     * 添加新的数组项
     * 根据配置创建相应的数据结构
     */
    addItem() {
      let newItems = [];
      if (this.options.keys) {
        const newItem = {};
        this.options.keys.forEach((key) => {
          newItem[key] = newVarInputVal("str");
        });
        newItems = [...this.items, newItem];
      } else {
        newItems = [...this.items, newVarInputVal("str")];
      }
      this.$emit("update:modelValue", newItems);
    },
    /**
     * 移除指定索引的数组项
     * 如果移除后数组为空，则创建一个新的空项
     */
    removeItem(index) {
      const newItems = [...this.items];
      newItems.splice(index, 1);
      if (newItems.length === 0) {
        if (this.options.keys) {
          const newItem = {};
          this.options.keys.forEach((key) => {
            newItem[key] = newVarInputVal("str");
          });
          newItems.push(newItem);
        } else {
          newItems.push(newVarInputVal("str"));
        }
      }
      this.$emit("update:modelValue", newItems);
    },
    /**
     * 更新单值模式下的值
     */
    updateItemValue(index, value) {
      const newItems = [...this.items];
      newItems[index] = value;
      this.$emit("update:modelValue", newItems);
    },
    /**
     * 更新多键模式下指定键的值
     */
    updateItemKeyValue(index, key, value) {
      const newItems = [...this.items];
      newItems[index] = {
        ...newItems[index],
        [key]: value,
      };
      this.$emit("update:modelValue", newItems);
    },
  },
});
</script>

<style scoped>
.array-editor {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* 防止输入框换行 */
.array-editor :deep(.q-field__native) {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.btn-container {
  position: relative;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.btn-container .q-btn {
  position: absolute;
  width: 16px;
  height: 16px;
  min-height: 16px;
  padding: 0;
}

.btn-container .center-btn {
  position: relative;
}

.btn-container .top-btn {
  top: 0;
}

.btn-container .bottom-btn {
  bottom: 0;
}

.array-editor :deep(.q-btn .q-icon) {
  font-size: 14px;
}

.array-editor :deep(.q-btn.q-btn--dense) {
  padding: 0;
  min-height: 16px;
}
</style>
