WXL (wul)
2025-11-13 de147dda682f8ac597bbcc8555b57acbdf45dba2
src/components/SortCheckbox/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,289 @@
<template>
  <div class="ordered-checkbox-container">
    <!-- æ¨ªå‘排列的多选框组 -->
    <el-checkbox-group
      v-model="checkedValues"
      class="horizontal-checkbox-group"
    >
      <el-checkbox
        v-for="option in options"
        :key="getValue(option)"
        :label="getValue(option)"
      >
        {{ getLabel(option) }}
      </el-checkbox>
    </el-checkbox-group>
    <!-- é€‰ä¸­é¡ºåºå±•示区域 -->
    <div v-if="selectedOrder.length > 0" class="selection-order-display">
      <span class="order-label">服务执行顺序:</span>
      <span
        v-for="(item, index) in selectedOrder"
        :key="item.value"
        class="order-item"
      >
        {{ getSelectedIndex(index) }}.{{ getLabelByValue(item.value) }}
        <el-tooltip content="设置补偿时间" placement="top">
          <el-input-number
            v-model="item.compensateTime"
            :min="0"
            :max="60"
            size="mini"
            controls-position="right"
            class="compensate-time-input"
            @change="handleCompensateTimeChange(item.value, $event)"
          />
        </el-tooltip>
        <span v-if="index < selectedOrder.length - 1">、</span>
      </span>
    </div>
    <div v-else class="selection-order-display">
      <span class="order-label">暂无选中项</span>
    </div>
  </div>
</template>
<script>
export default {
  name: "OrderedCheckboxGroup",
  props: {
    options: {
      type: Array,
      default: () => [],
    },
    value: {
      type: Array,
      default: () => [],
    },
    initialselectedOrder: {
      type: Array,
      default: () => [],
    },
    valueKey: {
      type: String,
      default: "value",
    },
    labelKey: {
      type: String,
      default: "label",
    },
    // æ–°å¢žï¼šé»˜è®¤è¡¥å¿æ—¶é—´
    defaultCompensateTime: {
      type: Number,
      default: 0,
    },
  },
  data() {
    return {
      checkedValues: [],
      selectedOrder: [], // çŽ°åœ¨æ ¼å¼ä¸º [{value, compensateTime}]
    };
  },
  watch: {
    value: {
      immediate: true,
      handler(newVal) {
        if (
          Array.isArray(newVal) &&
          newVal.length > 0 &&
          typeof newVal[0] === "object"
        ) {
          console.log(this.selectedOrder, "111");
          // 1. ä¼ å…¥çš„æ˜¯å¯¹è±¡æ•°ç»„ [{ sort, preachform, compensateTime }]
          this.checkedValues = newVal.map((item) => item.preachform); // æå– preachform ç»„成选中值数组
          // æž„建 selectedOrder,优先使用传入的 compensateTime,否则用默认值
          this.selectedOrder = newVal.map((item) => ({
            value: item.preachform,
            compensateTime: item.hasOwnProperty("compensateTime")
              ? item.compensateTime
              : this.defaultCompensateTime,
          }));
        } else {
          // 2. ä¼ å…¥çš„æ˜¯å­—符串数组 (如 ["1", "3", "4"],兼容之前的用法)
          if (JSON.stringify(newVal) !== JSON.stringify(this.checkedValues)) {
            this.checkedValues = [...newVal];
            console.log(this.selectedOrder, "222");
            console.log(this.newVal, "22");
            // æž„建或更新 selectedOrder,保留已有的 compensateTime
            const newOrder = [];
            newVal.forEach((value) => {
              const existingItem = this.selectedOrder.find(
                (item) => item.value === value
              );
              if (existingItem) {
                newOrder.push(existingItem);
              } else {
                newOrder.push({
                  value,
                  compensateTime: this.hasOwnProperty(value)
                    ? this.hasOwnProperty(value)
                    : this.defaultCompensateTime,
                });
              }
            });
            this.selectedOrder = newOrder;
          }
        }
      },
      deep: true, // å»ºè®®æ·»åŠ  deep: true ä»¥ç¡®ä¿å¯¹è±¡æ•°ç»„内的变化能被捕获
    },
    checkedValues(newVal, oldVal) {
      console.log(this.selectedOrder, "333");
      // å¤„理选中项的变化
      const added = newVal.filter((item) => !oldVal.includes(item));
      const removed = oldVal.filter((item) => !newVal.includes(item));
      added.forEach((value) => {
        if (!this.selectedOrder.find((item) => item.value === value)) {
          this.selectedOrder.push({
            value,
            compensateTime: this.defaultCompensateTime,
          });
        }
      });
      removed.forEach((value) => {
        const index = this.selectedOrder.findIndex(
          (item) => item.value === value
        );
        if (index > -1) {
          this.selectedOrder.splice(index, 1);
        }
      });
      // æ›´æ–°çˆ¶ç»„ä»¶çš„ v-model ç»‘定值(选中值数组)
      this.$emit("input", [...newVal]);
      // è§¦å‘ change äº‹ä»¶ï¼Œä¼ é€’完整的业务数据
      this.emitChangeEvent();
    },
  },
  methods: {
    getValue(option) {
      return typeof option === "object" ? option[this.valueKey] : option;
    },
    getLabel(option) {
      return typeof option === "object" ? option[this.labelKey] : option;
    },
    getLabelByValue(value) {
      const option = this.options.find((opt) => this.getValue(opt) === value);
      return option ? this.getLabel(option) : value;
    },
    getSelectedIndex(index) {
      if (index < 20) {
        return String.fromCharCode(0x2460 + index);
      } else {
        return `(${index + 1})`;
      }
    },
    // å¤„理补偿时间变化
    handleCompensateTimeChange(value, newTime) {
      const item = this.selectedOrder.find((item) => item.value === value);
      if (item) {
        item.compensateTime = newTime;
        // è¡¥å¿æ—¶é—´å˜åŒ–时,只触发 change äº‹ä»¶ï¼Œä¸è§¦åЍ v-model
        this.emitChangeEvent();
      }
    },
    hasOwnProperty(patfrom) {
      console.log(patfrom);
      console.log(this.initialselectedOrder);
      // ä½¿ç”¨find方法查找匹配的对象
      const foundObject = this.initialselectedOrder.find(
        (item) => item.preachform === patfrom
      );
      // å¦‚果找到对象,返回其compensateTime;否则返回false
      return foundObject ? foundObject.compensateTime : false;
    },
    // å‘射变化事件
    emitChangeEvent() {
      // è½¬æ¢æ•°æ®æ ¼å¼ä¸ºçˆ¶ç»„件需要的格式
      const outputData = this.selectedOrder.map((item, index) => ({
        sort: index + 1,
        preachform: item.value,
        compensateTime: item.compensateTime,
      }));
      this.$emit("change", outputData); // å‘å°„ change äº‹ä»¶ï¼Œä¼ é€’完整数据
    },
    // èŽ·å–å½“å‰é€‰æ‹©é¡ºåºå’Œè¡¥å¿æ—¶é—´
    getSelectionOrder() {
      return this.selectedOrder.map((item, index) => ({
        sort: index + 1,
        preachform: item.value,
        compensateTime: item.compensateTime,
      }));
    },
    // è®¾ç½®é€‰æ‹©é¡ºåºå’Œè¡¥å¿æ—¶é—´
    setSelectionOrder(orderedValues) {
      this.selectedOrder = orderedValues.map((item) => ({
        value: item.preachform,
        compensateTime: item.compensateTime || this.defaultCompensateTime,
      }));
      this.checkedValues = orderedValues.map((item) => item.preachform);
      this.emitChangeEvent();
    },
  },
};
</script>
<style scoped>
.ordered-checkbox-container {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.horizontal-checkbox-group {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}
.selection-order-display {
  padding: 12px;
  background-color: #f5f7fa;
  border-radius: 4px;
  border: 1px solid #ebeef5;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}
.order-label {
  font-weight: bold;
  color: #606266;
  margin-right: 8px;
}
.order-item {
  color: #409eff;
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  margin-right: 8px;
}
.compensate-time-input {
  width: 90px;
  margin-left: 8px;
}
/* å“åº”式设计:小屏幕时换行 */
@media (max-width: 768px) {
  .horizontal-checkbox-group {
    gap: 12px;
  }
  .selection-order-display {
    padding: 8px;
    flex-direction: column;
    align-items: flex-start;
  }
  .order-item {
    margin-bottom: 8px;
  }
}
</style>