<template>
  <div class="component-upload-image-cropper">
    <!-- 外层容器框 -->
    <div class="upload-container">
      <!-- 已上传的图片展示区域 -->
      <div class="uploaded-images">
        <div 
          v-for="(item, index) in fileList" 
          :key="index" 
          class="image-item"
        >
          <img 
            :src="item.url" 
            :alt="item.name" 
            class="preview-image" 
            @click="previewImage(item.url)"
          />
          <div class="image-actions">
            <el-button 
              type="danger" 
              size="small" 
              circle 
              @click="removeImage(index)"
            >
              <el-icon><Delete /></el-icon>
            </el-button>
          </div>
        </div>
      </div>
      
      <!-- 上传按钮区域 -->
      <el-upload
        ref="imageUploadRef"
        :action="uploadImgUrl"
        list-type="picture-card"
        :on-success="handleUploadSuccess"
        :before-upload="handleBeforeUpload"
        :limit="limit"
        :accept="fileAccept"
        :on-error="handleUploadError"
        :on-exceed="handleExceed"
        :before-remove="handleDelete"
        :show-file-list="false"
        :headers="headers"
        :file-list="[]"
        :class="{ hide: fileList.length >= limit }"
      >
        <el-icon class="avatar-uploader-icon">
          <plus />
        </el-icon>
      </el-upload>
    </div>
    
    <!-- 上传提示 -->
    <div v-if="showTip" class="el-upload__tip">
      请上传
      <template v-if="fileSize">
        大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
      </template>
      <template v-if="fileType">
        格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
      </template>
      的文件
    </div>

    <!-- 图片预览对话框 -->
    <el-dialog v-model="dialogVisible" title="预览" width="800px" append-to-body>
      <img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" />
    </el-dialog>

    <!-- 图片裁切对话框 -->
    <el-dialog v-model="cropperVisible" title="图片裁切" width="1000px" append-to-body @opened="modalOpened" @close="closeCropperDialog">
      <el-row>
        <el-col :xs="24" :md="12" :style="{ height: '400px' }">
          <vue-cropper
            v-if="cropperOptions.visible"
            ref="cropper"
            :img="cropperOptions.img"
            :info="true"
            :auto-crop="cropperOptions.autoCrop"
            :auto-crop-width="cropperOptions.autoCropWidth"
            :auto-crop-height="cropperOptions.autoCropHeight"
            :fixed-box="cropperOptions.fixedBox"
            :output-type="cropperOptions.outputType"
            :fixed="cropperOptions.fixed"
            :fixed-number="cropperOptions.fixedNumber"
            @real-time="realTime"
          />
        </el-col>
        <el-col :xs="24" :md="12" :style="{ height: '400px' }">
          <div class="cropper-preview">
            <h4>预览效果</h4>
            <div class="preview-container">
              <img :src="cropperOptions.previews.url" :style="cropperOptions.previews.img" />
            </div>
          </div>
        </el-col>
      </el-row>
      <br />
      <el-row>
        <el-col :lg="2" :md="2">
          <el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
            <el-button>
              选择图片
              <el-icon class="el-icon--right">
                <Upload />
              </el-icon>
            </el-button>
          </el-upload>
        </el-col>
        <el-col :lg="{ span: 1, offset: 1 }" :md="2">
          <el-button icon="Plus" @click="changeScale(1)" title="放大"></el-button>
        </el-col>
        <el-col :lg="{ span: 1, offset: 1 }" :md="2">
          <el-button icon="Minus" @click="changeScale(-1)" title="缩小"></el-button>
        </el-col>
        <el-col :lg="{ span: 1, offset: 1 }" :md="2">
          <el-button icon="RefreshLeft" @click="rotateLeft()" title="向左旋转"></el-button>
        </el-col>
        <el-col :lg="{ span: 1, offset: 1 }" :md="2">
          <el-button icon="RefreshRight" @click="rotateRight()" title="向右旋转"></el-button>
        </el-col>
        <el-col :lg="{ span: 2, offset: 6 }" :md="2">
          <el-button type="primary" @click="confirmCrop()">确认裁切</el-button>
        </el-col>
        <el-col :lg="{ span: 2, offset: 1 }" :md="2">
          <el-button @click="cancelCrop()">取消</el-button>
        </el-col>
      </el-row>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import 'vue-cropper/dist/index.css';
import { VueCropper } from 'vue-cropper';
import { listByIds, delOss } from '@/api/system/oss';
import { OssVO } from '@/api/system/oss/types';
import { propTypes } from '@/utils/propTypes';
import { globalHeaders } from '@/utils/request';
import { getUploadUrl } from '@/config/api';
import { compressAccurately } from 'image-conversion';
import { UploadRawFile } from 'element-plus';

const props = defineProps({
  modelValue: {
    type: [String, Object, Array],
    default: () => []
  },
  // 图片数量限制
  limit: propTypes.number.def(5),
  // 大小限制(MB)
  fileSize: propTypes.number.def(5),
  // 文件类型, 例如['png', 'jpg', 'jpeg']
  fileType: propTypes.array.def(['png', 'jpg', 'jpeg']),
  // 是否显示提示
  isShowTip: {
    type: Boolean,
    default: true
  },
  // 是否支持压缩，默认否
  compressSupport: {
    type: Boolean,
    default: false
  },
  // 压缩目标大小，单位KB。默认300KB以上文件才压缩，并压缩至300KB以内
  compressTargetSize: propTypes.number.def(300),
  // 是否启用裁切功能
  enableCrop: {
    type: Boolean,
    default: true
  },
  // 裁切框宽度
  cropWidth: {
    type: Number,
    default: 200
  },
  // 裁切框高度
  cropHeight: {
    type: Number,
    default: 200
  },
  // 是否固定裁切比例
  fixedRatio: {
    type: Boolean,
    default: false
  },
  // 裁切比例 [宽, 高]
  cropRatio: {
    type: Array,
    default: () => [1, 1]
  }
});

const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const emit = defineEmits(['update:modelValue']);
const number = ref(0);
const uploadList = ref<any[]>([]);
const dialogImageUrl = ref('');
const dialogVisible = ref(false);
const cropperVisible = ref(false);

const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadImgUrl = ref(getUploadUrl('oss')); // 上传的图片服务器地址
const headers = ref(globalHeaders());

const fileList = ref<any[]>([]);
const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize));

const imageUploadRef = ref<ElUploadInstance>();
const cropper = ref<any>({});

// 监听 fileType 变化，更新 fileAccept
const fileAccept = computed(() => props.fileType.map((type) => `.${type}`).join(','));

// 裁切选项
interface CropperOptions {
  img: string | any;
  autoCrop: boolean;
  autoCropWidth: number;
  autoCropHeight: number;
  fixedBox: boolean;
  fileName: string;
  previews: any;
  outputType: string;
  visible: boolean;
  fixed: boolean;
  fixedNumber: number[];
}

const cropperOptions = reactive<CropperOptions>({
  img: '',
  autoCrop: true,
  autoCropWidth: props.cropWidth,
  autoCropHeight: props.cropHeight,
  fixedBox: false,
  outputType: 'png',
  fileName: '',
  previews: {},
  visible: false,
  fixed: props.fixedRatio,
  fixedNumber: Array.isArray(props.cropRatio) ? props.cropRatio as number[] : [1, 1]
});

// 当前正在裁切的文件
const currentCropFile = ref<any>(null);

watch(
  () => props.modelValue,
  async (val: string) => {
    if (val) {
      // 首先将值转为数组
      let list: OssVO[] = [];
      if (Array.isArray(val)) {
        list = val as OssVO[];
      } else {
        // 判断是否为URL路径（包含http/https协议或相对路径）
        if (typeof val === 'string' && (val.startsWith('http') || val.startsWith('/'))) {
          // 直接使用URL路径作为图片显示
          list = [{ url: val } as OssVO];
        } else {
          // 原有逻辑，调用API获取信息
          const res = await listByIds(val);
          list = res.data;
        }
      }
      // 然后将数组转为对象数组
      fileList.value = list.map((item) => {
        // 字符串回显处理 如果此处存的是url可直接回显 如果存的是id需要调用接口查出来
        let itemData;
        if (typeof item === 'string') {
          itemData = { name: item, url: item };
        } else {
          // 此处name使用ossId 防止删除出现重名
          itemData = { name: item.ossId || item.url, url: item.url, ossId: item.ossId };
        }
        return itemData;
      });
    } else {
      fileList.value = [];
      return [];
    }
  },
  { deep: true, immediate: true }
);

/** 上传前loading加载 */
const handleBeforeUpload = (file: any) => {
  let isImg = false;
  if (props.fileType.length) {
    let fileExtension = '';
    if (file.name.lastIndexOf('.') > -1) {
      fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1);
    }
    isImg = props.fileType.some((type: any) => {
      if (file.type.indexOf(type) > -1) return true;
      if (fileExtension && fileExtension.indexOf(type) > -1) return true;
      return false;
    });
  } else {
    isImg = file.type.indexOf('image') > -1;
  }
  if (!isImg) {
    proxy?.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join('/')}图片格式文件!`);
    return false;
  }
  if (file.name.includes(',')) {
    proxy?.$modal.msgError('文件名不正确，不能包含英文逗号!');
    return false;
  }
  if (props.fileSize) {
    const isLt = file.size / 1024 / 1024 < props.fileSize;
    if (!isLt) {
      proxy?.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`);
      return false;
    }
  }

  // 如果启用裁切功能，先进行裁切
  if (props.enableCrop) {
    currentCropFile.value = file;
    openCropper(file);
    return false; // 阻止默认上传
  }

  //压缩图片，开启压缩并且大于指定的压缩大小时才压缩
  if (props.compressSupport && file.size / 1024 > props.compressTargetSize) {
    proxy?.$modal.loading('正在上传图片，请稍候...');
    number.value++;
    return compressAccurately(file, props.compressTargetSize);
  } else {
    proxy?.$modal.loading('正在上传图片，请稍候...');
    number.value++;
  }
};

// 文件个数超出
const handleExceed = () => {
  proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
};

// 上传成功回调
const handleUploadSuccess = (res: any, file: UploadFile) => {
  if (res.code === 200) {
    uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
    uploadedSuccessfully();
  } else {
    number.value--;
    proxy?.$modal.closeLoading();
    proxy?.$modal.msgError(res.msg);
    imageUploadRef.value?.handleRemove(file);
    uploadedSuccessfully();
  }
};

// 删除图片
const handleDelete = (file: UploadFile): boolean => {
  const findex = fileList.value.map((f) => f.name).indexOf(file.name);
  if (findex > -1 && uploadList.value.length === number.value) {
    const ossId = fileList.value[findex].ossId;
    delOss(ossId);
    fileList.value.splice(findex, 1);
    emit('update:modelValue', listToString(fileList.value));
    return false;
  }
  return true;
};

// 上传结束处理
const uploadedSuccessfully = () => {
  if (number.value > 0 && uploadList.value.length === number.value) {
    fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value);
    uploadList.value = [];
    number.value = 0;
    emit('update:modelValue', listToString(fileList.value));
    proxy?.$modal.closeLoading();
  }
};

// 上传失败
const handleUploadError = () => {
  proxy?.$modal.msgError('上传图片失败');
  proxy?.$modal.closeLoading();
};

// 预览
const handlePictureCardPreview = (file: any) => {
  dialogImageUrl.value = file.url;
  dialogVisible.value = true;
};

// 对象转成指定字符串分隔
const listToString = (list: any[], separator?: string) => {
  let strs = '';
  separator = separator || ',';
  for (const i in list) {
    if (undefined !== list[i].ossId && list[i].url.indexOf('blob:') !== 0) {
      strs += list[i].ossId + separator;
    }
  }
  return strs != '' ? strs.substring(0, strs.length - 1) : '';
};

// 裁切相关方法
/** 打开裁切对话框 */
const openCropper = (file: any) => {
  const reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onload = () => {
    cropperOptions.img = reader.result;
    cropperOptions.fileName = file.name;
    cropperVisible.value = true;
  };
};

/** 打开弹出层结束时的回调 */
const modalOpened = () => {
  cropperOptions.visible = true;
};

/** 覆盖默认上传行为 */
const requestUpload = (): any => {};

/** 向左旋转 */
const rotateLeft = () => {
  cropper.value.rotateLeft();
};

/** 向右旋转 */
const rotateRight = () => {
  cropper.value.rotateRight();
};

/** 图片缩放 */
const changeScale = (num: number) => {
  num = num || 1;
  cropper.value.changeScale(num);
};

/** 上传预处理 */
const beforeUpload = (file: UploadRawFile): any => {
  if (file.type.indexOf('image/') == -1) {
    proxy?.$modal.msgError('文件格式错误，请上传图片类型,如：JPG，PNG后缀的文件。');
  } else {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => {
      cropperOptions.img = reader.result;
      cropperOptions.fileName = file.name;
    };
  }
};

/** 确认裁切 */
const confirmCrop = () => {
  cropper.value.getCropBlob(async (data: any) => {
    // 创建新的文件对象
    const croppedFile = new File([data], cropperOptions.fileName, { type: data.type });
    
    // 关闭裁切对话框
    cropperVisible.value = false;
    cropperOptions.visible = false;
    
    // 手动上传裁切后的图片
    proxy?.$modal.loading('正在上传图片，请稍候...');
    number.value++;
    
    const formData = new FormData();
    formData.append('file', croppedFile);
    
    try {
      const response = await fetch(uploadImgUrl.value, {
        method: 'POST',
        headers: headers.value,
        body: formData
      });
      
      const result = await response.json();
      
      if (result.code === 200) {
        uploadList.value.push({ 
          name: result.data.fileName, 
          url: result.data.url, 
          ossId: result.data.ossId 
        });
        uploadedSuccessfully();
      } else {
        number.value--;
        proxy?.$modal.closeLoading();
        proxy?.$modal.msgError(result.msg || '上传失败');
      }
    } catch (error) {
      number.value--;
      proxy?.$modal.closeLoading();
      proxy?.$modal.msgError('上传失败');
    }
  });
};

/** 取消裁切 */
const cancelCrop = () => {
  cropperVisible.value = false;
  cropperOptions.visible = false;
  currentCropFile.value = null;
};

/** 关闭裁切对话框 */
const closeCropperDialog = () => {
  cropperOptions.visible = false;
  currentCropFile.value = null;
};

/** 实时预览 */
const realTime = (data: any) => {
  cropperOptions.previews = data;
};

// 删除指定索引的图片
const removeImage = (index: number) => {
  const item = fileList.value[index];
  if (item.ossId) {
    delOss(item.ossId);
  }
  fileList.value.splice(index, 1);
  emit('update:modelValue', listToString(fileList.value));
};

// 预览图片
const previewImage = (url: string) => {
  dialogImageUrl.value = url;
  dialogVisible.value = true;
};
</script>

<style lang="scss" scoped>
// .el-upload--picture-card 控制加号部分
:deep(.hide .el-upload--picture-card) {
  display: none;
}

// 外层容器样式
.upload-container {
  border: 2px dashed #d9d9d9;
  border-radius: 8px;
  padding: 20px;
  background-color: #fafafa;
  min-height: 200px;
  transition: border-color 0.3s;
  
  &:hover {
    border-color: #409eff;
  }
}

// 已上传图片展示区域
.uploaded-images {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 15px;
  
  .image-item {
    position: relative;
    width: 120px;
    height: 120px;
    border-radius: 6px;
    overflow: hidden;
    border: 1px solid #e4e7ed;
    background-color: #fff;
    
    .preview-image {
      width: 100%;
      height: 100%;
      object-fit: cover;
      cursor: pointer;
      transition: transform 0.3s;
      
      &:hover {
        transform: scale(1.05);
      }
    }
    
    .image-actions {
      position: absolute;
      top: 5px;
      right: 5px;
      opacity: 0;
      transition: opacity 0.3s;
      
      .el-button {
        background-color: rgba(255, 255, 255, 0.9);
        border: none;
        
        &:hover {
          background-color: #f56c6c;
          color: white;
        }
      }
    }
    
    &:hover {
      .image-actions {
        opacity: 1;
      }
    }
  }
}

// 上传按钮样式调整
:deep(.el-upload--picture-card) {
  width: 120px;
  height: 120px;
  line-height: 120px;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  background-color: #fff;
  
  &:hover {
    border-color: #409eff;
  }
}

.cropper-preview {
  padding: 20px;
  
  h4 {
    margin-bottom: 15px;
    text-align: center;
  }
  
  .preview-container {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 300px;
    border: 1px solid #dcdfe6;
    border-radius: 4px;
    background-color: #f5f7fa;
    
    img {
      max-width: 100%;
      max-height: 100%;
      object-fit: contain;
    }
  }
}

// 响应式设计
@media (max-width: 768px) {
  .uploaded-images {
    .image-item {
      width: 100px;
      height: 100px;
    }
  }
  
  :deep(.el-upload--picture-card) {
    width: 100px;
    height: 100px;
    line-height: 100px;
  }
}
</style> 