jeecgBoot-vue3图片上传

场景说明

jeecgboot-vue3图片上传组件只吃显示的是网络地址,例如http://.....jpg格式,但如果既想支持显示网络地址,又想支持显示base64格式地址,那么需要对封装的JImageUpload组件进行修改,如下:

<template>
  <div class="clearfix">
    <a-upload :listType="listType" accept="image/*" :multiple="multiple" :action="uploadUrl"                       :headers="headers"
      :data="{ biz: bizPath }" v-model:fileList="uploadFileList" :beforeUpload="beforeUpload"                     :disabled="disabled"
      @change="handleChange" @preview="handlePreview" name="file" :show-upload-list="showUploadList">
      <div v-if="uploadVisible">
        <div v-if="listType == 'picture-card'">
          <LoadingOutlined v-if="loading" />
          <UploadOutlined v-else />
          <div class="ant-upload-text">{{ text }}</div>
        </div>
        <a-button v-if="listType == 'picture'" :disabled="disabled">
          <UploadOutlined></UploadOutlined>
          {{ text }}
        </a-button>
      </div>
    </a-upload>
    <a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel()">
      <img alt="example" style="width: 100%" :src="previewImage" />
    </a-modal>
  </div>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted, defineProps } from 'vue';
import { LoadingOutlined, UploadOutlined } from '@ant-design/icons-vue';
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
import { propTypes } from '/@/utils/propTypes';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { useMessage } from '/@/hooks/web/useMessage';
import { getFileAccessHttpUrl, getRandom } from '/@/utils/common/compUtils';
import { uploadUrl } from '/@/api/common/api';
import { getToken } from '/@/utils/auth';
import { useI18n } from '/@/hooks/web/useI18n';
    
const { t } = useI18n();
const { createMessage, createErrorModal } = useMessage();
export default defineComponent({
  name: 'JImageUpload',
  components: { LoadingOutlined, UploadOutlined },
  inheritAttrs: false,
  props: {
    //绑定值
    value: propTypes.oneOfType([propTypes.string, propTypes.array]),
    //按钮文本
    listType: {
      type: String,
      required: false,
      default: 'picture-card',
    },
    //按钮文本
    text: {
      type: String,
      required: false,
      default: t('modal.upload'),
    },
    //这个属性用于控制文件上传的业务路径
    bizPath: {
      type: String,
      required: false,
      default: 'temp',
    },
    //是否禁用
    disabled: {
      type: Boolean,
      required: false,
      default: false,
    },
    //上传数量
    fileMax: {
      type: Number,
      required: false,
      default: 1,
    },
    isBase64: {
      type: Boolean,
      required: false,
      default: false,
    },
  },
  emits: ['options-change', 'change', 'update:value','handle'],
  setup(props, { emit, refs }) {
    const emitData = ref<any[]>([]);
    const attrs = useAttrs();
    const [state] = useRuleFormItem(props, 'value', 'change', emitData);
    //获取文件名
    const getFileName = (path) => {
      if (path.lastIndexOf('\\') >= 0) {
        let reg = new RegExp('\\\\', 'g');
        path = path.replace(reg, '/');
      }
      return path.substring(path.lastIndexOf('/') + 1);
    };
    //token
    const headers = ref<object>({
      'X-Access-Token': getToken(),
    });
    //上传状态
    const loading = ref<boolean>(false);
    //是否是初始化加载
    const initTag = ref<boolean>(true);
    //文件列表
    let uploadFileList = ref<any[]>([]);
    //预览图
    const previewImage = ref<string | undefined>('');
    //预览框状态
    const previewVisible = ref<boolean>(false);
    //计算是否开启多图上传
    const multiple = computed(() => {
      return props['fileMax'] > 1;
    });

    //计算是否可以继续上传
    const uploadVisible = computed(() => {
      return uploadFileList.value.length < props['fileMax'];
    });

    // 是否隐藏上传列表(新增的时候为true)
    const showUploadList = ref(true)
    
     /**
      * 监听value变化 查看传来得数据是base64还是网络地址 从而进行下一步处理
      */
    watch(
      () => props.value,
      (val, prevCount) => {
        // console.log(val,'watch',prevCount);
        
        //update-begin---author:liusq ---date:20230601  for:【issues/556】JImageUpload组件value赋初始值没显示图片------------
        if (val && val instanceof Array) {
          // 正则表达式判断字符串是否以"data:image"开头,并且以";base64,"结尾
          const regex = /^data:image\/[a-zA-Z]*;base64,/;
          if (regex.test(val)) {
            val = val
          }
          val = val.join(',');
        }
        if (initTag.value == true) {
          initFileList(val);
        }
      },
      { immediate: true }
      //update-end---author:liusq ---date:20230601  for:【issues/556】JImageUpload组件value赋初始值没显示图片------------
    );

    /**
     * 初始化文件列表
     */
    function initFileList(paths) {
      // console.log(paths, 'path');

      if (!paths || paths.length == 0) {
        uploadFileList.value = [];
        return;
      }
      let files = [];
      // console.log(paths, 'paths');
      // 正则表达式判断字符串是否以"data:image"开头,并且以";base64,"结尾
      const regex = /^data:image\/[a-zA-Z]*;base64,/;
      if (regex.test(paths)) {
        files.push({
          uid: getRandom(10),
          name: 'image',
          status: 'done',
          url: paths,
          response: {
            status: 'history',
            message: paths,
          },
        });
        uploadFileList.value = files;
      } else {
          //网络地址
        let arr = paths.split(',');
        // console.log(arr, 'arr');
        arr.forEach((value) => {
            //getFileAccessHttpUrl 引入文件 转换网络地址
          let url = getFileAccessHttpUrl(value);
          files.push({
            uid: getRandom(10),
            name: getFileName(value),
            status: 'done',
            url: url,
            response: {
              status: 'history',
              message: value,
            },
          });
        });
        uploadFileList.value = files;
      }
    }

    /**
     * 上传前校验
     */
    function beforeUpload(file) {
      let fileType = file.type;
      if (fileType.indexOf('image') < 0) {
        createMessage.info('请上传图片');
        return false;
      }
    }
    /**
     * 文件上传结果回调
     */
    function handleChange({ file, fileList, event }) {
      // console.log(file, fileList, 'fileList');
      // console.log(1);
      initTag.value = false;
      uploadFileList.value = fileList;
      if (file.status === 'error') {
        createMessage.error(`${file.name} 上传失败.`);
      }
      let fileUrls = [];
      let res = {}
      //上传完成
      if (file.status != 'uploading') {
        fileList.forEach((file) => {
          if (file.status === 'done') {
            //update-begin---author:wangshuai ---date:20221121  for:[issues/248]原生表单内使用图片组件,关闭弹窗图片组件值不会被清空------------
            initTag.value = true;
            //update-end---author:wangshuai ---date:20221121  for:[issues/248]原生表单内使用图片组件,关闭弹窗图片组件值不会被清空------------
            fileUrls.push(file.response.message);

            res = file.response
          }
        });
        if (file.status === 'removed') {
          handleDelete(file);
        }
      }
      // emitData.value = fileUrls.join(',');
      state.value = fileUrls.join(',');
      // state.value = res;
      console.log(res, 'res');


      emit('update:value', fileUrls.join(','));
      emit('handle',res)
    }

    /**
     * 删除图片
     */
    function handleDelete(file) {
      //如有需要新增 删除逻辑
      console.log(file);
    }

    /**
     * 预览图片
     */
    function handlePreview(file) {
      previewImage.value = file.url || file.thumbUrl;
      previewVisible.value = true;
    }

    function getAvatarView() {
      if (uploadFileList.length > 0) {
        let url = uploadFileList[0].url;
        return getFileAccessHttpUrl(url, null);
      }
    }

    function handleCancel() {
      previewVisible.value = false;
    }

    return {
      state,
      attrs,
      previewImage,
      previewVisible,
      uploadFileList,
      multiple,
      headers,
      loading,
      uploadUrl,
      beforeUpload,
      uploadVisible,
      handlePreview,
      handleCancel,
      handleChange,
      showUploadList,
    };
  },
});
</script>
<style scoped>
.ant-upload-select-picture-card i {
  font-size: 32px;
  color: #999;
}

.ant-upload-select-picture-card .ant-upload-text {
  margin-top: 8px;
  color: #666;
}
</style>
在其他页面使用该封装的组件
<a-form-item class="msgBoxItem" :label="t('modal.shopPhoto')" required="true">
  <JImageUpload :fileMax="1" v-model:value="shopPhotoUrl" :biz-path="'SHOP_PHOTO'"
    @handle="(info) => handleChange1(info, 'shop')" :disabled="viewDisabled"
    style="margin-top: 20px;" />
</a-form-item>


// 个张图片的地址 显示
const shopPhotoUrl = ref('')

// 上传图片成功数据
const shopPhotoResult = ref({});

// 修改时 上传图片返回结果
const shopPhotoREdit = ref({});


// JImageUpload上传图片
const handleChange1 = (info, who) => {
  // console.log(info, '上传', who,);
  // 上传成功
  if (info.code == 200) {
    // 上传的是哪个
    const result = info.result
    if (who == 'shop') {
        //上传成功数据
      shopPhotoResult.value = result
      shopPhotoUrl.value = ''
    }
    .......

  } else {
    // 上传失败
    message.error('Upload error')
  }

}

// 监听是否上传图   修改得时候判断是否有重新上传图片,若重新上传则获取新上传数据 (具体数据由返回结果看)
watch(() => shopPhotoResult.value, (n, o) => {
  // console.log(n, o, '上传监听'); 重新上传则赋值新数据否则为空 再在新增修改接口根据shopPhotoREdit判断是否添加参数 如下
  if (n) {
    shopPhotoREdit.value = n
    if (n.base64) {
      shopPhotoUrl.value = n.base64
    }

  } else {
    shopPhotoREdit.value = {}
  }

})
相关推荐
forwardMyLife7 分钟前
element-plus的面包屑组件el-breadcrumb
javascript·vue.js·ecmascript
ice___Cpu8 分钟前
Linux 基本使用和 web 程序部署 ( 8000 字 Linux 入门 )
linux·运维·前端
JYbill10 分钟前
nestjs使用ESM模块化
前端
加油吧x青年29 分钟前
Web端开启直播技术方案分享
前端·webrtc·直播
计算机学姐41 分钟前
基于python+django+vue的影视推荐系统
开发语言·vue.js·后端·python·mysql·django·intellij-idea
luoluoal1 小时前
java项目之基于Spring Boot智能无人仓库管理源码(springboot+vue)
java·vue.js·spring boot
吕彬-前端1 小时前
使用vite+react+ts+Ant Design开发后台管理项目(二)
前端·react.js·前端框架
小白小白从不日白1 小时前
react hooks--useCallback
前端·react.js·前端框架
恩婧1 小时前
React项目中使用发布订阅模式
前端·react.js·前端框架·发布订阅模式
mez_Blog1 小时前
个人小结(2.0)
前端·javascript·vue.js·学习·typescript