<template>
  <el-dialog
    custom-class="component-background"
    :width="dialogWidth"
    :close-on-click-modal="false"
    :visible="visible"
    :before-close="handleClose"
  >
    <div class="background-title">组件背景设置</div>
    <div class="set-background">
      <div
        ref="backgroundImg"
        class="background-img"
        :style="{ width: `${maxWidth}px`, height: `${maxHeight}px` }"
        :class="{ 'is-crop-mode': cropMode }"
        @mousedown="onBackgroundMouseDown"
      >
        <div v-if="file_url" class="img-set" :style="{ top: imgTop, left: imgLeft, position: 'relative' }">
          <div class="dot top-left" @mousedown="dragStart($event, 'nwse-resize', 'top-left')"></div>
          <div class="horizontal-line" @mousedown="dragStart($event, 'ns-resize', 'top')"></div>
          <div class="dot top-right" @mousedown="dragStart($event, 'nesw-resize', 'top-right')"></div>
          <div class="vertical-line" @mousedown="dragStart($event, 'ew-resize', 'left')"></div>
          <img
            ref="img"
            :src="file_url"
            draggable="false"
            :style="{ width: imgWidth, height: imgHeight }"
            @load="handleImageLoad"
            @mousedown="dragStart($event, 'move', 'move')"
          />
          <div class="vertical-line" @mousedown="dragStart($event, 'ew-resize', 'right')"></div>
          <div class="dot bottom-left" @mousedown="dragStart($event, 'nesw-resize', 'bottom-left')"></div>
          <div class="horizontal-line" @mousedown="dragStart($event, 'ns-resize', 'bottom')"></div>
          <div class="dot bottom-right" @mousedown="dragStart($event, 'nwse-resize', 'bottom-right')"></div>
        </div>
        <div v-if="file_url && cropMode" class="crop-tip">在图片上拖拽框选裁切区域</div>
        <div v-if="file_url && cropMode && hasCropSelection" class="crop-selection" :style="cropSelectionStyle"></div>
      </div>

      <div class="setup">
        <div class="setup-item">
          <div class="setup-top">
            <el-radio v-model="background.mode" label="image">图片</el-radio>
            <SvgIcon v-if="file_url" icon-class="delete" style="cursor: pointer" size="14" @click="file_url = ''" />
          </div>
          <div class="setup-content">
            <div class="image-set">
              <el-upload
                ref="upload"
                class="file-uploader"
                action="no"
                accept="image/*"
                :show-file-list="false"
                :limit="1"
                :http-request="uploadFile"
              >
                <span class="select-button">选择图片</span>
              </el-upload>
              <div>
                <span class="opacity-icon"></span>
                <el-input v-model="background.image_opacity" size="mini" :min="0" :max="100" style="width: 55px" />%
              </div>
            </div>
            <div class="mode-list">
              <span
                v-for="mode in imageModeList"
                :key="mode.value"
                :class="{ active: mode.value === background.imageMode }"
                :style="{ fontSize: mode.value === imageModeList[3].value ? '12px' : '' }"
                @click="background.imageMode = mode.value"
              >
                {{ mode.label }}
              </span>
            </div>
            <div v-if="file_url" class="crop-actions">
              <el-button size="mini" plain @click="startRectCrop">裁切</el-button>
              <el-button v-if="cropMode" size="mini" @click="cancelRectCrop">取消</el-button>
              <el-button v-if="cropMode" size="mini" type="primary" @click="applyRectCrop">应用</el-button>
            </div>
            <div v-if="background.imageMode === imageModeList[3].value">
              <el-checkbox v-model="lockScale">锁定比例</el-checkbox>
            </div>
          </div>
        </div>
        <div class="setup-item">
          <div class="setup-top">
            <el-radio v-model="background.mode" label="color">颜色</el-radio>
          </div>
          <div class="setup-content">
            <ColorPicker v-model="background.color" show-alpha />
          </div>
        </div>
        <div class="setup-item">
          <div class="setup-top">
            <el-checkbox v-model="background.enable_border" label="边框" />
          </div>
          <div class="setup-content">
            <el-color-picker v-model="background.border_color" show-alpha />
            <div>
              <el-input v-model="background.border_width" style="width: 70px; margin-right: 10px">
                <i slot="prefix" class="el-icon-s-fold" style="line-height: 32px"></i>
              </el-input>
              <el-select v-model="background.border_style" placeholder="边框样式" style="width: 120px">
                <el-option label="实线" value="solid" />
                <el-option label="虚线" value="dashed" />
                <el-option label="点线" value="dotted" />
              </el-select>
            </div>
          </div>
        </div>
        <div class="setup-item">
          <div class="setup-top">
            <el-checkbox v-model="background.enable_radius" label="圆角" />
          </div>
          <div class="setup-content border-radius">
            <div class="radius-item">
              <span class="span-radius" :style="computedBorderRadius('top', 'left')"></span>
              <el-input v-model="background.top_left_radius" :min="0" type="number" />
            </div>
            <div class="radius-item">
              <span class="span-radius" :style="computedBorderRadius('top', 'right')"></span>
              <el-input v-model="background.top_right_radius" :min="0" type="number" />
            </div>
            <div class="radius-item">
              <span class="span-radius" :style="computedBorderRadius('bottom', 'left')"></span>
              <el-input v-model="background.bottom_left_radius" :min="0" type="number" />
            </div>
            <div class="radius-item">
              <span class="span-radius" :style="computedBorderRadius('bottom', 'right')"></span>
              <el-input v-model="background.bottom_right_radius" :min="0" type="number" />
            </div>
          </div>
        </div>
      </div>
    </div>

    <div slot="footer">
      <el-button @click="handleClose">取 消</el-button>
      <el-button type="primary" @click="confirm">确 定</el-button>
    </div>
  </el-dialog>
</template>

<script>
import ColorPicker from '@/components/ColorPicker.vue';

import { fileUpload } from '@/api/app';

export default {
  name: 'SetComponentBackground',
  components: {
    ColorPicker,
  },
  props: {
    visible: {
      type: Boolean,
      required: true,
    },
    url: {
      type: String,
      default: '',
    },
    position: {
      type: Object,
      default: () => ({ width: 0, height: 0, top: 0, left: 0 }),
    },
    backgroundData: {
      type: Object,
      default: () => ({}),
    },
    componentData: {
      type: Object,
      default: () => ({ courseware_id: '', component_id: '' }),
    },
  },
  data() {
    return {
      maxWidth: 500,
      maxHeight: 450,

      imgData: {
        width: 0,
        height: 0,
        top: 0,
        left: 0,
      },
      drag: {
        dragging: false,
        startX: 0,
        startY: 0,
        type: '',
        aspectRatio: 1, // 锁定比例时的宽高比
      },
      cropMode: false,
      crop: {
        drawing: false,
        startX: 0,
        startY: 0,
        x: 0,
        y: 0,
        width: 0,
        height: 0,
      },
      imageDisplay: {
        top: 0,
        left: 0,
        width: 0,
        height: 0,
      },
      file_url: '', // 背景图片地址
      background: {
        mode: 'image', // 背景模式
        imageMode: 'auto', // 图片背景模式
        image_opacity: 100, // 背景图片透明度
        color: 'rgba(255, 255, 255, 1)', // 背景颜色
        enable_border: false, // 是否启用边框
        border_color: 'rgba(0, 0, 0, 1)', // 边框颜色
        border_width: 1, // 边框宽度
        border_style: 'solid', // 边框样式
        enable_radius: false, // 是否启用圆角
        top_left_radius: 4, // 左上圆角
        top_right_radius: 4, // 右上圆角
        bottom_left_radius: 4, // 左下圆角
        bottom_right_radius: 4, // 右下圆角
      },
      imageModeList: [
        { label: '适应', value: 'adapt' },
        { label: '拉伸', value: 'stretch' },
        { label: '平铺', value: 'fill' },
        { label: '自定义', value: 'auto' },
      ],
      lockScale: false, // 是否锁定比例（仅自定义模式）
    };
  },
  computed: {
    imgTop() {
      return `${this.imgData.top - 9}px`;
    },
    imgLeft() {
      return `${this.imgData.left}px`;
    },
    imgWidth() {
      return `${this.imgData.width}px`;
    },
    imgHeight() {
      return `${this.imgData.height}px`;
    },
    hasCropSelection() {
      return this.crop.width > 1 && this.crop.height > 1;
    },
    cropSelectionStyle() {
      return {
        top: `${this.imageDisplay.top + this.crop.y}px`,
        left: `${this.imageDisplay.left + this.crop.x}px`,
        width: `${this.crop.width}px`,
        height: `${this.crop.height}px`,
      };
    },
    dialogWidth() {
      return `${this.maxWidth + 250}px`;
    },
  },
  watch: {
    visible(newVal) {
      if (!newVal) {
        this.revertData();
        return;
      }

      const component = document.querySelector(`div.${this.componentData.component_id}`);
      if (component) {
        const rect = component.getBoundingClientRect();
        const componentRatio = rect.width / rect.height;
        const imgRatio = this.maxWidth / this.maxHeight;
        if (componentRatio > imgRatio) {
          this.maxHeight = this.maxWidth / componentRatio;
        } else {
          this.maxWidth = this.maxHeight * componentRatio;
        }
      }

      this.file_url = this.url;
      this.imgData = {
        width: (this.position.width * this.maxWidth) / 100,
        height: (this.position.height * this.maxHeight) / 100,
        top: (this.position.top * this.maxHeight) / 100,
        left: (this.position.left * this.maxWidth) / 100,
      };
      if (this.backgroundData && Object.keys(this.backgroundData).length) {
        this.background = { ...this.backgroundData };
      }
      this.cropMode = false;
      this.resetCropRect();

      this.$nextTick(() => {
        document.querySelector('.background-img').addEventListener('mousemove', this.mouseMove);
        this.syncImageDisplayRect();
      });
    },
  },
  mounted() {
    document.body.addEventListener('mouseup', this.mouseUp);
  },
  beforeDestroy() {
    document.querySelector('.background-img')?.removeEventListener('mousemove', this.mouseMove);
    document.body.removeEventListener('mouseup', this.mouseUp);
  },
  methods: {
    handleClose() {
      this.$emit('update:visible', false);
    },
    /**
     * 还原数据
     */
    revertData() {
      this.cropMode = false;
      this.resetCropRect();
      this.maxWidth = 500;
      this.maxHeight = 450;
      this.imgData = {
        width: 0,
        height: 0,
        top: 0,
        left: 0,
      };
      this.crop = {
        drawing: false,
        startX: 0,
        startY: 0,
        x: 0,
        y: 0,
        width: 0,
        height: 0,
      };
      this.imageDisplay = {
        top: 0,
        left: 0,
        width: 0,
        height: 0,
      };
      this.file_url = ''; // 背景图片地址
      this.background = {
        mode: 'image', // 背景模式
        imageMode: 'auto', // 图片背景模式
        image_opacity: 100, // 背景图片透明度
        color: 'rgba(255, 255, 255, 1)', // 背景颜色
        enable_border: false, // 是否启用边框
        border_color: 'rgba(0, 0, 0, 1)', // 边框颜色
        border_width: 1, // 边框宽度
        border_style: 'solid', // 边框样式
        enable_radius: false, // 是否启用圆角
        top_left_radius: 4, // 左上圆角
        top_right_radius: 4, // 右上圆角
        bottom_left_radius: 4, // 左下圆角
        bottom_right_radius: 4, // 右下圆角
      };
    },
    /**
     * 拖拽开始
     * @param {MouseEvent} event
     * @param {string} cursor
     * @param {string} type
     */
    dragStart(event, cursor, type) {
      if (this.cropMode) return;
      const { clientX, clientY } = event;
      const aspectRatio = this.imgData.height ? this.imgData.width / this.imgData.height : 1;
      this.drag = {
        dragging: true,
        startX: clientX,
        startY: clientY,
        type,
        aspectRatio,
      };

      document.querySelector('.el-dialog__wrapper').style.cursor = cursor;
    },
    /**
     * 鼠标移动
     * @param {MouseEvent} event
     */
    mouseMove(event) {
      if (this.cropMode) {
        if (!this.crop.drawing) return;
        const point = this.getPointInImage(event);
        if (!point) return;

        const x = Math.min(this.crop.startX, point.x);
        const y = Math.min(this.crop.startY, point.y);
        const width = Math.abs(point.x - this.crop.startX);
        const height = Math.abs(point.y - this.crop.startY);
        this.crop = {
          ...this.crop,
          x,
          y,
          width,
          height,
        };
        return;
      }

      if (!this.drag.dragging) return;
      const { clientX, clientY } = event;
      const { startX, startY, type } = this.drag;
      const prevImgData = { ...this.imgData };

      const widthDiff = clientX - startX;
      const heightDiff = clientY - startY;

      if (type === 'top-left') {
        this.imgData.width = Math.min(this.maxWidth, Math.max(0, this.imgData.width - widthDiff));
        this.imgData.height = Math.min(this.maxHeight, Math.max(0, this.imgData.height - heightDiff));
        this.imgData.top = Math.min(this.maxHeight - this.imgData.height, Math.max(0, this.imgData.top + heightDiff));
        this.imgData.left = Math.min(this.maxWidth - this.imgData.width, Math.max(0, this.imgData.left + widthDiff));
      } else if (type === 'top-right') {
        this.imgData.width = Math.min(this.maxWidth, this.imgData.width + widthDiff);
        this.imgData.height = Math.min(this.maxHeight, Math.max(0, this.imgData.height - heightDiff));
        this.imgData.top = Math.min(this.maxHeight - this.imgData.height, Math.max(0, this.imgData.top + heightDiff));
        this.imgData.left = Math.min(this.maxWidth - this.imgData.width, Math.max(0, this.imgData.left));
      } else if (type === 'bottom-left') {
        this.imgData.width = Math.min(this.maxWidth, Math.max(0, this.imgData.width - widthDiff));
        this.imgData.height = Math.min(this.maxHeight, this.imgData.height + heightDiff);
        this.imgData.top = Math.min(this.maxHeight - this.imgData.height, Math.max(0, this.imgData.top));
        this.imgData.left = Math.min(this.maxWidth - this.imgData.width, Math.max(0, this.imgData.left + widthDiff));
      } else if (type === 'bottom-right') {
        this.imgData.width = Math.min(this.maxWidth, this.imgData.width + widthDiff);
        this.imgData.height = Math.min(this.maxHeight, this.imgData.height + heightDiff);
        this.imgData.top = Math.min(this.maxHeight - this.imgData.height, Math.max(0, this.imgData.top));
        this.imgData.left = Math.min(this.maxWidth - this.imgData.width, Math.max(0, this.imgData.left));
      }

      if (type === 'top') {
        this.imgData.height = Math.min(this.maxHeight, Math.max(0, this.imgData.height - heightDiff));
        this.imgData.top = Math.min(this.maxHeight - this.imgData.height, Math.max(0, this.imgData.top + heightDiff));
      } else if (type === 'bottom') {
        this.imgData.height = Math.min(this.maxHeight, this.imgData.height + heightDiff);
        this.imgData.top = Math.min(this.maxHeight - this.imgData.height, Math.max(0, this.imgData.top));
      } else if (type === 'left') {
        this.imgData.width = Math.min(this.maxWidth, this.imgData.width - widthDiff);
        this.imgData.left = Math.min(this.maxWidth - this.imgData.width, Math.max(0, this.imgData.left + widthDiff));
      } else if (type === 'right') {
        this.imgData.width = Math.min(this.maxWidth, this.imgData.width + widthDiff);
        this.imgData.left = Math.min(this.maxWidth - this.imgData.width, Math.max(0, this.imgData.left));
      }

      if (type === 'move') {
        this.imgData.top = Math.min(this.maxHeight - this.imgData.height, Math.max(0, this.imgData.top + heightDiff));
        this.imgData.left = Math.min(this.maxWidth - this.imgData.width, Math.max(0, this.imgData.left + widthDiff));
      }

      if (this.shouldLockScale(type)) {
        this.applyLockedScaleResize(type, prevImgData);
      }

      this.drag.startX = clientX;
      this.drag.startY = clientY;
      this.syncImageDisplayRect();
    },
    /**
     * 判断在当前拖动类型下是否应该锁定比例调整尺寸
     * @param {string} type 拖动类型
     * @returns {boolean} 是否应该锁定比例调整尺寸
     */
    shouldLockScale(type) {
      return this.background.imageMode === 'auto' && this.lockScale && type !== 'move';
    },
    /**
     * 在锁定比例的情况下调整尺寸，确保宽高比保持不变，并且不会超出边界
     * @param {string} type 拖动类型
     * @param {object} prevImgData 拖动前的图片数据，用于计算边界限制
     */
    applyLockedScaleResize(type, prevImgData) {
      const ratio = this.drag.aspectRatio || 1;
      if (!ratio) return;

      let width = type === 'top' || type === 'bottom' ? this.imgData.height * ratio : this.imgData.width;
      let height = width / ratio;

      // 根据拖动方向计算允许的最大宽高，确保在锁定比例时不会超出边界
      const widthLimit = type.includes('left')
        ? prevImgData.left + prevImgData.width
        : this.maxWidth - prevImgData.left;
      const heightLimit = type.includes('top')
        ? prevImgData.top + prevImgData.height
        : this.maxHeight - prevImgData.top;
      const maxWidthByHeight = heightLimit * ratio;
      const safeWidth = Math.max(1, Math.min(width, widthLimit, maxWidthByHeight));

      width = safeWidth;
      height = width / ratio;

      // 根据拖动方向计算新的 left 和 top，确保在锁定比例时图片位置调整合理
      const left = type.includes('left') ? prevImgData.left + prevImgData.width - width : prevImgData.left;
      const top = type.includes('top') ? prevImgData.top + prevImgData.height - height : prevImgData.top;

      this.imgData.width = width;
      this.imgData.height = height;
      this.imgData.left = Math.min(this.maxWidth - width, Math.max(0, left));
      this.imgData.top = Math.min(this.maxHeight - height, Math.max(0, top));
    },
    /**
     * 鼠标抬起
     */
    mouseUp() {
      this.crop.drawing = false;
      this.drag.dragging = false;
      document.querySelector('.el-dialog__wrapper').style.cursor = 'auto';
    },
    onBackgroundMouseDown(event) {
      if (!this.cropMode || !this.file_url) return;
      const point = this.getPointInImage(event);
      if (!point) return;

      this.crop = {
        ...this.crop,
        drawing: true,
        startX: point.x,
        startY: point.y,
        x: point.x,
        y: point.y,
        width: 0,
        height: 0,
      };
      event.preventDefault();
    },
    getPointInImage(event) {
      const bg = this.$refs.backgroundImg;
      if (!bg || this.imageDisplay.width <= 0 || this.imageDisplay.height <= 0) return null;

      const bgRect = bg.getBoundingClientRect();
      const rawX = event.clientX - bgRect.left - this.imageDisplay.left;
      const rawY = event.clientY - bgRect.top - this.imageDisplay.top;

      if (rawX < 0 || rawY < 0 || rawX > this.imageDisplay.width || rawY > this.imageDisplay.height) {
        return null;
      }

      return {
        x: Math.min(this.imageDisplay.width, Math.max(0, rawX)),
        y: Math.min(this.imageDisplay.height, Math.max(0, rawY)),
      };
    },
    /**
     * 同步图片显示区域位置和尺寸信息
     */
    syncImageDisplayRect() {
      this.$nextTick(() => {
        const bg = this.$refs.backgroundImg;
        const img = this.$refs.img;
        if (!bg || !img) {
          this.imageDisplay = {
            top: 0,
            left: 0,
            width: 0,
            height: 0,
          };
          return;
        }
        const bgRect = bg.getBoundingClientRect();
        const imgRect = img.getBoundingClientRect();
        this.imageDisplay = {
          top: imgRect.top - bgRect.top,
          left: imgRect.left - bgRect.left,
          width: imgRect.width,
          height: imgRect.height,
        };
      });
    },
    resetCropRect() {
      this.crop = {
        drawing: false,
        startX: 0,
        startY: 0,
        x: 0,
        y: 0,
        width: 0,
        height: 0,
      };
    },
    startRectCrop() {
      if (!this.file_url) return;
      this.cropMode = true;
      this.syncImageDisplayRect();
      this.$nextTick(() => {
        const { width, height } = this.imageDisplay;
        if (!width || !height) return;
        this.crop = {
          drawing: false,
          startX: 0,
          startY: 0,
          x: width * 0.25,
          y: height * 0.25,
          width: width * 0.5,
          height: height * 0.5,
        };
      });
    },
    cancelRectCrop() {
      this.cropMode = false;
      this.resetCropRect();
    },
    async applyRectCrop() {
      if (!this.hasCropSelection) {
        this.$message.warning('请先框选裁切区域');
        return;
      }

      if (!this.file_url || !this.imageDisplay.width || !this.imageDisplay.height) return;

      let sourceImg = null;
      let revokeObjectURL = null;

      try {
        const cropSource = await this.getCropSourceImage();
        sourceImg = cropSource.img;
        revokeObjectURL = cropSource.revokeObjectURL;
      } catch (error) {
        this.$message.error('当前图片不支持裁切，请检查图片服务跨域配置');
        return;
      }

      const naturalWidth = sourceImg.naturalWidth;
      const naturalHeight = sourceImg.naturalHeight;
      const displayWidth = this.imageDisplay.width;
      const displayHeight = this.imageDisplay.height;

      const sx = Math.max(0, Math.round((this.crop.x / displayWidth) * naturalWidth));
      const sy = Math.max(0, Math.round((this.crop.y / displayHeight) * naturalHeight));
      const sw = Math.max(1, Math.round((this.crop.width / displayWidth) * naturalWidth));
      const sh = Math.max(1, Math.round((this.crop.height / displayHeight) * naturalHeight));
      const safeWidth = Math.max(1, Math.min(sw, naturalWidth - sx));
      const safeHeight = Math.max(1, Math.min(sh, naturalHeight - sy));

      try {
        const canvas = document.createElement('canvas');
        canvas.width = safeWidth;
        canvas.height = safeHeight;
        const ctx = canvas.getContext('2d');
        if (!ctx) throw new Error('canvas context unavailable');
        ctx.drawImage(sourceImg, sx, sy, safeWidth, safeHeight, 0, 0, safeWidth, safeHeight);

        const blob = await new Promise((resolve, reject) => {
          canvas.toBlob((result) => {
            if (result) {
              resolve(result);
              return;
            }
            reject(new Error('toBlob failed'));
          }, 'image/png');
        });

        const cropFile = new File([blob], `background-crop-${Date.now()}.png`, { type: 'image/png' });
        const uploadPayload = {
          filename: 'file',
          file: cropFile,
        };

        const { file_info_list } = await fileUpload('Mid', uploadPayload, { isGlobalprogress: true });
        if (!file_info_list || !file_info_list.length) {
          throw new Error('upload failed');
        }

        this.file_url = file_info_list[0].file_url;
        this.normalComputed(safeWidth, safeHeight);
        this.cancelRectCrop();
        this.$message.success('裁切成功');
      } catch (error) {
        this.$message.error('裁切失败');
        console.error('Rect crop error:', error);
      } finally {
        revokeObjectURL?.();
      }
    },
    async getCropSourceImage() {
      try {
        const img = await this.loadImage(this.file_url, 'anonymous');
        return {
          img,
          revokeObjectURL: null,
        };
      } catch (crossOriginError) {
        const blob = await this.fetchImageBlob(this.file_url);
        const objectUrl = URL.createObjectURL(blob);

        try {
          const img = await this.loadImage(objectUrl);
          return {
            img,
            revokeObjectURL: () => URL.revokeObjectURL(objectUrl),
          };
        } catch (loadBlobError) {
          URL.revokeObjectURL(objectUrl);
          throw loadBlobError;
        }
      }
    },
    loadImage(src, crossOrigin = '') {
      return new Promise((resolve, reject) => {
        const img = new Image();
        if (crossOrigin) {
          img.crossOrigin = crossOrigin;
        }
        img.onload = () => resolve(img);
        img.onerror = () => reject(new Error('image load failed'));
        img.src = src;
      });
    },
    async fetchImageBlob(url) {
      const response = await fetch(url, {
        credentials: 'include',
      });

      if (!response.ok) {
        throw new Error('fetch image blob failed');
      }

      return response.blob();
    },
    handleImageLoad() {
      this.syncImageDisplayRect();
    },
    /**
     * 计算圆角样式
     * @param {string} position 位置，top/bottom
     * @param {string} direction 方向，left/right
     * @returns {Object} 圆角样式对象
     */
    computedBorderRadius(position, direction) {
      const radius = this.background[`${position}_${direction}_radius`];
      let borderWidth = {};
      if (position === 'top') {
        borderWidth['border-bottom-width'] = 0;
      } else {
        borderWidth['border-top-width'] = 0;
      }
      if (direction === 'left') {
        borderWidth['border-right-width'] = 0;
      } else {
        borderWidth['border-left-width'] = 0;
      }
      return {
        [`border-${position}-${direction}-radius`]: `${radius}px`,
        ...borderWidth,
      };
    },
    uploadFile(file) {
      fileUpload('Mid', file, { isGlobalprogress: true })
        .then(({ file_info_list }) => {
          this.$refs.upload.clearFiles();
          if (file_info_list.length > 0) {
            const fileUrl = file_info_list[0].file_url_open;
            const img = new Image();
            img.src = fileUrl;
            img.onload = () => {
              const { width, height } = img;
              this.normalComputed(width, height);
              this.file_url = fileUrl;
            };
          }
        })
        .catch(() => {
          this.$message.error('上传失败');
        });
    },
    /**
     * 正常填充
     * @param {number} width 图片宽度
     * @param {number} height 图片高度
     */
    normalComputed(width, height) {
      if (width > this.maxWidth || height > this.maxHeight) {
        const wScale = width / this.maxWidth;
        const hScale = height / this.maxHeight;
        const scale = wScale > hScale ? this.maxWidth / 2 / width : this.maxHeight / 2 / height;

        this.imgData = {
          width: width * scale,
          height: height * scale,
          top: 0,
          left: 0,
        };
      } else {
        this.imgData = {
          width,
          height,
          top: 0,
          left: 0,
        };
      }
    },
    confirm() {
      this.$emit(
        'setComponentBackground',
        this.file_url,
        {
          width: (this.imgData.width / this.maxWidth) * 100,
          height: (this.imgData.height / this.maxHeight) * 100,
          top: (this.imgData.top / this.maxHeight) * 100,
          left: (this.imgData.left / this.maxWidth) * 100,
          imgX: (this.imgData.left / (this.maxWidth - this.imgData.width)) * 100,
          imgY: (this.imgData.top / (this.maxHeight - this.imgData.height)) * 100,
        },
        this.background,
      );
      this.handleClose();
    },
  },
};
</script>

<style lang="scss" scoped>
.background-title {
  margin-bottom: 12px;
  font-size: 16px;
  font-weight: 500;
  color: $font-light-color;
}

.set-background {
  display: flex;
  column-gap: 8px;

  .background-img {
    position: relative;
    border: 1px dashed rgba(0, 0, 0, 8%);

    &.is-crop-mode {
      cursor: crosshair;
    }

    .crop-tip {
      position: absolute;
      top: 8px;
      left: 8px;
      z-index: 3;
      padding: 2px 8px;
      font-size: 12px;
      color: #fff;
      pointer-events: none;
      background: rgba(0, 0, 0, 45%);
      border-radius: 10px;
    }

    .crop-selection {
      position: absolute;
      z-index: 2;
      pointer-events: none;
      border: 1px dashed #fff;
      box-shadow: 0 0 0 9999px rgba(0, 0, 0, 35%);
    }

    .img-set {
      display: inline-grid;
      grid-template:
        ' . . . ' 2px
        ' . img . ' auto
        ' . . . ' 2px
        / 2px auto 2px;

      img {
        object-fit: cover;
      }

      .horizontal-line,
      .vertical-line {
        background-color: $main-color;
      }

      .horizontal-line {
        width: 100%;
        height: 2px;
        cursor: ns-resize;
      }

      .vertical-line {
        width: 2px;
        height: 100%;
        cursor: ew-resize;
      }

      .dot {
        z-index: 1;
        width: 6px;
        height: 6px;
        background-color: $main-color;

        &.top-left {
          top: -2px;
          left: -2px;
        }

        &.top-right {
          top: -2px;
          right: 2px;
        }

        &.bottom-left {
          bottom: 2px;
          left: -2px;
        }

        &.bottom-right {
          right: 2px;
          bottom: 2px;
        }

        &.top-left,
        &.bottom-right {
          position: relative;
          cursor: nwse-resize;
        }

        &.top-right,
        &.bottom-left {
          position: relative;
          cursor: nesw-resize;
        }
      }
    }
  }

  .setup {
    display: flex;
    flex-direction: column;
    row-gap: 12px;
    width: 200px;

    &-item {
      display: flex;
      flex-direction: column;
      column-gap: 8px;

      .setup-top {
        margin-bottom: 6px;
      }

      .setup-content {
        display: flex;
        flex-direction: column;
        row-gap: 8px;

        .image-set {
          display: flex;
          align-items: center;
          justify-content: space-around;
          height: 32px;
          color: $font-light-color;
          background-color: #f2f3f5;
          border: 1px solid #d9d9d9;
          border-radius: 4px;

          .opacity-icon {
            display: inline-block;
            width: 20px;
            height: 20px;
            vertical-align: middle;
            background: url('@/assets/icon/opacity.png');
          }

          .select-button {
            color: #165dff;
            cursor: pointer;
            background-color: #fff;
            box-shadow: 0 0 0 3px #fff;
          }
        }

        .mode-list {
          display: flex;
          padding: 2px 3px;
          background-color: #f2f3f5;
          border: 1px solid #d9d9d9;
          border-radius: 4px;

          span {
            flex: 1;
            padding: 2px 6px;
            line-height: 21px;
            text-align: center;
            cursor: pointer;

            &.active {
              color: $main-color;
              background-color: #fff;
            }
          }
        }

        .crop-actions {
          z-index: 3001;
          display: flex;
        }

        &.border-radius {
          display: grid;
          grid-template-rows: 1fr 1fr;
          grid-template-columns: 1fr 1fr;
          gap: 8px 8px;

          .radius-item {
            display: flex;
            flex: 1;
            column-gap: 4px;
            align-items: center;
            height: 32px;
            padding: 2px 6px;
            background-color: #f2f3f5;

            .span-radius {
              display: inline-block;
              width: 16px;
              height: 16px;
              border-color: #000;
              border-style: solid;
              border-width: 2px;
            }
          }
        }
      }
    }
  }
}
</style>

<style lang="scss">
.el-dialog.component-background {
  .el-dialog__header {
    display: none;
  }

  .el-dialog__body {
    padding: 8px 16px;
  }
}
</style>
