使用 ionic + cordova + vue3 实现相册选择、拍照,并上传、预览图片

news2024/11/25 5:04:04

目录

1.上传组件 upload.vue

1.1 模板规划

1.2 点击添加按钮

1.2.1 实现询问弹框

1.2.2 实现拍照 

1.2.3 实现相册选择

 1.2.4 实现文件上传

1.2.5 校验图片类型并上传

1.2.6 获取图片列表

1.2.7 在组件内 添加图片附件

2.图片放大组件 enlarge-image.vue

2.1 点击图片放大

2.2 模板规划

2.3 使用 swiper11 踩坑的过程


1.上传组件 upload.vue

1.1 模板规划

模板包含三部分:

  • 已经上传的图片列表展示,若只读,则不展示删除按钮,每个图片点击后都可以被放大
  • 添加按钮展示,若没有图片,并且非只读,则展示
  • 暂无图片提示
  <div class="t-upload">
    <ion-grid>
      <!-- {{ fileList }} -->

      <!-- 已经上传的图片 -->
      <template v-if="fileList?.length">
        <ion-col v-for="(img, index) in fileList" :key="img?.FILE_ID" size="4">
          <img
            class="file"
            :src="getImgUrl(img)"
            alt=""
            @click="goEnlargeImage(index)"
          />
          <img
            v-if="!readonly"
            class="delete"
            src="@/assets/image/common/upload-delete.png"
            @click.stop="removeFile(img?.FILE_ID)"
          />
        </ion-col>
      </template>

      <!-- 添加图片按钮 -->
      <template v-if="!readonly">
        <ion-col v-if="!fileList?.length || fileList?.length < 9" size="4">
          <img
            class="add-file"
            src="@/assets/image/common/upload-add.png"
            @click="addMediaFile()"
          />
        </ion-col>
      </template>

      <template v-if="!fileList?.length && readonly">
        <div class="fs-14">暂无附件</div>
      </template>
    </ion-grid>
  </div>

1.2 点击添加按钮

点击添加按钮后,会出现一个弹框,让用户选择图片来源:

  • 拍照
  • 相册选择

1.2.1 实现询问弹框

这个很简单,使用 ionic 的 actionSheetController 即可实现,根据用户的选择,决定执行的方法

  async addFile(max: number, callback: any) {
    const actionSheet = await actionSheetController.create({
      header: '附件类型选择',
      buttons: [
        {
          text: '拍照',
          handler: () => {
            this.camera({
              quality: 100,
              destinationType: 1,
              sourceType: 1,
              targetWidth: 1080,
              targetHeight: 1920,
              mediaType: 0,
              encodingType: 1,
            })
              .then(async (res) => {
                callback(res, 'photo');
              })
              .catch((err) => {
                publicService.toast('拍照失败,请重试');
              });
          },
        },
        {
          text: '相册',
          handler: () => {
            this.slectImagePicker({
              maximumImagesCount: max,
              quality: 50,
            })
              .then((res) => {
                callback(res, 'img');
              })
              .catch(() => {
                publicService.toast('相册打开失败');
              });
          },
        },
        {
          text: '取消',
          role: 'cancel',
          handler: () => {
            console.error('Cancel clicked');
          },
        },
      ],
    });
    await actionSheet.present();
  }

1.2.2 实现拍照 

安装 cordova 插件:

  • @awesome-cordova-plugins/camera@6.4.0
  • cordova-plugin-camera@7.0.0

容易出现的问题:在真机调试时,点击拍照,提示我传入了非法参数

解决方案:升级 camera 插件版本,原来用的版本是 4.1.4,升级到 7.0.0 后 自动解决问题 

此方法最终返回一个图片对象信息

// 用于拍照或从相册选择照片
import { Camera, CameraOptions } from '@awesome-cordova-plugins/camera';

  /**
   * 拍照
   * @param opts 拍照配置
   * @returns
   */
  camera(opts: CameraOptions): Promise<any> {
    return new Promise((resolve, reject) => {
      Camera.getPicture(opts)
        .then((res: ChooserResult) => {
          resolve(res);
        })
        .then((error) => {
          reject('native camera error');
        });
    });
  }

1.2.3 实现相册选择

安装 cordova 插件:

  • @awesome-cordova-plugins/image-picker@6.4.0
  • cordova-plugin-telerik-imagepicker@2.3.6

容易出现的问题:在相册选择界面中,确认取消按钮是英文

解决方案:在 node_modules 里,找插件源码中的 xml 文件,搜索相关英文单词,改成中文

此方法最终返回一组图片对象信息

// 用于从相册中选择照片
import { ImagePicker, ImagePickerOptions } from '@awesome-cordova-plugins/image-picker';

  /**
   * 照片选择
   * @param opts
   * @returns
   */
  slectImagePicker(opts: ImagePickerOptions): Promise<any> {
    // console.log('照片选择 ImagePicker ---', ImagePicker);
    return new Promise((resolve, reject) => {
      ImagePicker.getPictures(opts)
        .then((res) => {
          resolve(res);
        })
        .catch(() => {
          reject('slectImagePicker native error');
        });
    });
  }

 1.2.4 实现文件上传

安装 cordova 插件:

  • @awesome-cordova-plugins/file-transfer@6.4.0
  • cordova-plugin-file-transfer@1.7.1

容易出现的问题:

  • 若接口返回的数据不是 JSON 对象,而是 map 对象,则容易解析失败
  • 服务器上的文件名字,需要保证唯一性
  • 给 file-transfer 插件传递的服务器地址,需要使用 encodeURI 进行编码
  • file-transfer 插件会把 给接口的文件流参数,默认存到 file 里,可以通过 fileKey 指定参数名

解决方案:

  • 接口返回的数据 最终会在 res.response 中存储,这是插件替我们封装了一层,让后端把返回的数据写成 JSON 对象的格式
  • 使用时间戳保证名字唯一性,new Date().getTime() + (name || `${pathArr[pathArr.length - 1]}`)
  • 编码服务器地址:encodeURI(API.uploadFile.serviceApi)
  • fileKey: 'form_file', // 表单元素名称,默认为 file,也可以和后端协商,文件流会存储在这个变量中

// 文件上传下载
import {
  FileTransfer,
  FileUploadOptions,
} from '@awesome-cordova-plugins/file-transfer';

  /**
   * 文件上传
   * @param fileUrl 文件路径
   * @param url 服务器地址
   * @param opts 配置项
   * @returns
   */
  fileLoad(
    fileUrl: string,
    url: string,
    opts: FileUploadOptions
  ): Promise<any> {
    return new Promise((resolve, reject) => {
      // 创建文件上传实例
      const file = FileTransfer.create();
      file
        .upload(fileUrl, url, opts, false)
        .then((res) => {
          publicService.closeLoading(this.loading);
          this.loading = null;
          resolve(res);
        })
        .catch((err) => {
          console.log('文件上传错误 native ---', err);
          publicService.closeLoading(this.loading);
          this.loading = null;
          reject('文件上传发生错误');
        });
    });
  }

1.2.5 校验图片类型并上传

    /**
     * 文件上传
     * @param fileUrl 附件地址
     * @paramascriptionTypename 附件名称
     */
    function fileUpload(fileUrl: string, name?: string) {
      // 获取文件名称
      const pathArr = state.fileUrl.split('?')[0].split('/') || '';
      // 文件格式后缀
      const suffix = state.fileUrl.substring(
        state.fileUrl.lastIndexOf('.') + 1
      );
      // 文件格式后缀
      const suffixs = ['png', 'jpg', 'jpeg', 'svg'];

      // 文件类型验证  通过后再上传文件
      let fileTypeValidate = true;

      if (state.fileUrl && !suffix) {
        fileTypeValidate = false;
        toast('不支持的文件类型');
      } else if (!suffixs.includes(suffix)) {
        fileTypeValidate = false;
        toast(`不支持${suffix}文件类型`);
      }

      // 若文件格式不合法,则不进行上传
      if (!fileTypeValidate) {
        if (publicService.loading) {
          publicService.closeLoading(publicService.loading);
          publicService.loading = null;
        }
        return;
      }

      // 获取文件名称
      const fileName = new Date().getTime() + (name || `${pathArr[pathArr.length - 1]}`);
      nativeService
        .fileLoad(fileUrl, encodeURI(API.uploadFile.serviceApi), {
          fileName, // 将文件保存在服务器上时,要使用的文件名
          fileKey: 'form_file', // 表单元素名称,默认为 file,也可以和后端协商,文件流会存储在这个变量中
          httpMethod: 'POST', // 上传接口请求方式
          params: {
            // 业务数据ID(用于业务关联附件)
            businessKey: props.businessKey,
            // 表单ID(可为空)- 分组
            inputFileId: props.inputFileId,
            // 文件
            form_file: fileUrl,
            // 登录用户
            createUser: userInfos.userId,
          },
        })
        .then((res) => {
          console.log('upload.vue 上传接口响应 ---', res.response);
          const testJX = JSON.parse(res.response);
          console.log('尝试解析 ---', testJX);

          if (publicService.loading) {
            publicService.closeLoading(publicService.loading);
            publicService.loading = null;
          }
          // 获取文件列表
          getFiles();
        })
        .catch((err) => {
          console.error(err);
        });
    }

1.2.6 获取图片列表

获取图片列表 getFiles 的几个场景:

  • 上传成功后
  • 删除成功后
  • 组件初始化的时候
  • watch 监听到 给接口传递的业务参数 发生变化后,比如 businessKey

每次获取了图片列表后,都应该 emit 最新的图片列表信息

 

1.2.7 在组件内 添加图片附件

上面说过,拍照返回的是一个图片信息,相册选择返回的是一组图片信息

根据返回信息的类型,可以拿到图片在手机本地的路径、名称,并依次调用文件上传

nativeService.addFile(
  9 - state.fileList.length,
  (res: any, fileType: string, name?: string) => {
    if (fileType === 'photo') {
      publicService.loading = publicService.sloading('拍照上传中,请稍候...');
      state.fileUrl = res;
      // 文件上传
      fileUpload(res, name || '');
    } else if (Array.isArray(res)) {
      if (res.length) {
        publicService.loading = publicService.sloading('相册选择照片上传中,请稍候...');
      }
      res.forEach(async (item, index1) => {
        state.fileUrl = item;
        state.fileName = name || '';
        // 文件上传
        await fileUpload(item, name || '');
      });
    } else {
      publicService.loading = publicService.sloading('附件上传中,请稍候...');
      state.fileUrl = res;
      state.fileName = name || '';
      // 文件上传
      fileUpload(res, name || '');
    }
  }
);

2.图片放大组件 enlarge-image.vue

2.1 点击图片放大

使用 ionic modalController 创建一个弹框页面

在方法内部传入 图片放大组件、组件需要的各种参数、默认展示的图片索引 等信息

    /**
     * 打开图片预览界面
     * @param index 当前点击的图片索引
     */
    async function goEnlargeImage(index: number) {
      // console.log('t-upload.vue 点击的图片索引 ---', index);
      const modal = await modalController.create({
        component: EnlargeImage as any,
        componentProps: {
          pictures: state.fileList,
          initialSlide: index,
          time: new Date().getTime() + '',
        },
        cssClass: 'enlarge-image-modal',
      });
      await modal.present();
    }

2.2 模板规划

因为博主使用的是 ionic7,已经不存在 ion-slide 等组件了

通过官网提示 Vue Slides Guide: How to Get Swiper for Vue on Ionic Apps,决定使用 swiper 插件实现需求

组件接受俩参数:

  • 图片列表 pictures,在组件里用计算属性 stateImageList 进行关联,保证单项数据流
  • 当前激活的图片索引 initialSlide,默认为 0,和 swiper 的 initialSlide 属性关联绑定

模板如下:

  <ion-page>
    <ion-header>
      <ion-toolbar color="primary">
        <ion-buttons slot="end" @click="closePage()">
          <ion-icon class="close-icon" :icon="close"></ion-icon>
        </ion-buttons>
      </ion-toolbar>
    </ion-header>

    <ion-content>
      <swiper :initialSlide="initialSlide" @transitionStart="start($event)">
        <swiper-slide v-for="(item, index) in stateImageList" :key="index">
          <!-- <div class="img-title">
            {{ item.FILE_NAME }}
          </div> -->
          <div class="img-box" :style="{ 'max-height': maxHeight }">
            <img
              v-if="
                !item.FILE_SUFFIX.toLowerCase() ||
                (item.FILE_SUFFIX.toLowerCase() !== 'mp4' &&
                  item.FILE_SUFFIX.toLowerCase() !== 'mp3')
              "
              :src="getImgUrl(item)"
              :style="{ 'max-height': maxHeight }"
            />
          </div>
          <!-- {{ getImgUrl(item) }} -->
        </swiper-slide>
      </swiper>
    </ion-content>
  </ion-page>

2.3 使用 swiper11 踩坑的过程

ionic 官网说的安装 swiper:npm install swiper@latest

执行了这个命令,npm 只会给你装上 swiper/vue,没给你装 swiper/modules,这样问题非常大,因为 Navigation, Pagination 等模块必须安装 swiper/modules,才能进行引入,咱需要手动安装哦

引入 swiper 相关内容:

// swiper
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Navigation, Pagination } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import '@ionic/vue/css/ionic-swiper.css';

    // 响应式变量
    const state = reactive({
      // swiper 扩展模块
      // modules: [Navigation, Pagination],
      // 最大高度
      maxHeight: window.innerHeight - 100,
    });

    // 图片列表
    const stateImageList = computed(() => {
      return JSON.parse(JSON.stringify(props.pictures));
    });

    function start(ev: any) {
      // console.log('ev ---', ev, stateImageList.value);
    }

    /**
     * 关闭当前页面
     */
    function closePage() {
      modalController.dismiss();
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1135417.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

selenium+python自动化测试 —— 解决无法启动IE浏览器及报错问题!

前言&#xff1a;记录启动IE浏览器的报错及解决方法。 错误1&#xff1a; selenium.common.exceptions.WebDriverException: Message: IEDriverServer.exe executable needs to be in PATH. Please download from http://selenium-release.storage.googleapis.com/index.html…

基于 ARM+FPGA+AD平台的多类型同步信号采集仪开发及试验验证(一)上位机设计

采集仪上位机设计 本章开发了一款基于 C# 的上位机软件&#xff0c;用于对多类型同步信号采集仪的各项功能 进行操作。从采集仪的数据传输需求出发&#xff0c;上位机利用以太网 UDP 协议实现与采集仪 的数据交互&#xff0c;包括向采集仪发送控制信息与配置信息、接收采…

一文详解手眼标定公式推导

文章目录 一、坐标系运算规则1、坐标系运算规则一(点)2、坐标系运算规则二(坐标系)3、齐次变换二、手眼标定公式推导1、眼在手外2、眼在手上3、解方程AX = XB后记假设手眼标定坐标系表示如下: 机械臂基底坐标系 – base机械臂末端坐标系 – end相机坐标系 – camera标定板坐…

最新PHP号卡商城V1.31 号卡推广管理系统源码/手机卡流量卡推广网站源码

源码简介&#xff1a; 最近看卡号推广比较火,但是苦于不能综合起来供客户选择,于是找了一套源码分享给大家。最新PHP号卡商城V1.31 号卡推广管理系统源码&#xff0c;它为手机卡流量卡推广网站源码。 更新日志&#xff1a; v1.31 1.修复自助开通分站时&#xff0c;低版本数据…

如何在用pip配置文件设置HTTP爬虫IP

首先&#xff0c;定义问题&#xff1a;在 Pip 中设置HTTP爬虫IP服务器&#xff0c;以便在网络上进行访问和下载。 亲身经验&#xff1a;我曾经遇到过类似问题&#xff0c;通过设置HTTP爬虫IP服务器成功解决了网络访问问题。 数据和引证&#xff1a;根据 pip 官方文档&#xff…

界面控件DevExtreme v23.1 - UI组件 UI模板库增强

DevExtreme拥有高性能的HTML5 / JavaScript小部件集合&#xff0c;使您可以利用现代Web开发堆栈&#xff08;包括React&#xff0c;Angular&#xff0c;ASP.NET Core&#xff0c;jQuery&#xff0c;Knockout等&#xff09;构建交互式的Web应用程序。从Angular和Reac&#xff0c…

分享8款人声分离的软件,总有一款你喜欢的

随着音乐制作技术的不断发展&#xff0c;人声分离软件成为了音乐制作过程中不可或缺的工具之一。人声分离软件可以帮助音乐制作者将多个人声部分分离出来&#xff0c;以便单独进行编辑和处理。 以下是一些人声分离软件的信息介绍&#xff1a; 1、音分轨人声分离软件&#xff1…

【AICFD案例操作】汽车外气动分析

AICFD是由天洑软件自主研发的通用智能热流体仿真软件&#xff0c;用于高效解决能源动力、船舶海洋、电子设备和车辆运载等领域复杂的流动和传热问题。软件涵盖了从建模、仿真到结果处理完整仿真分析流程&#xff0c;帮助工业企业建立设计、仿真和优化相结合的一体化流程&#x…

UWB常规TOF、TDOA、AOA算法

UWB定位的最基础技术是TOF测距、TDOA到达时间差、AOA到达角度测量&#xff1a; 定位算法&#xff1a;TOF、TDOA、AOA&#xff0f;PDOA UWB定位算法 (1) TOF&#xff08;Time of Flight飞行时间&#xff09;&#xff1a; 以基站为圆心&#xff0c;以标签和基站距离为半径画圆…

Camtasia2023屏幕录制和视频剪辑标杆软件,制作微课/游戏视频必备工具

Camtasia可不是一款简单的屏幕录制软件&#xff0c;这可是集合了屏幕录制和视频剪辑于一体的全能软件。 大多数用户都是使用Windows自带的录频方式进行屏幕录制&#xff0c;使用这种方法录制屏幕很“鸡肋”&#xff0c;比如说&#xff1a;无法区域录制、无法更改鼠标样式、无法…

专业的图片校正修复软件DxO ViewPoint简体中文安装

DxO ViewPoint是一款专业的图像校正软件&#xff0c;旨在帮助摄影师和图像设计师解决各种图像处理难题&#xff0c;并创造出理想的画面效果。该软件结合了最新的图像处理技术和精确的镜头校正方法&#xff0c;可以修复图像中的透视、畸变、色差、暗角等问题&#xff0c;同时对图…

矢量图形编辑软件Illustrator 2023 mac中文版软件特点(ai2023) v27.9

illustrator 2023 mac是一款矢量图形编辑软件&#xff0c;用于创建和编辑排版、图标、标志、插图和其他类型的矢量图形。 illustrator 2023 mac软件特点 矢量图形&#xff1a;illustrator创建的图形是矢量图形&#xff0c;可以无限放大而不失真&#xff0c;这与像素图形编辑软…

易基因: WGBS等从DNA甲基化揭示杀鲑气单胞菌灭活疫苗对大菱鲆的免疫力:抗性育种

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 DNA甲基化是表观遗传学中最重要的修饰之一&#xff0c;在免疫应答中发挥着重要作用。自引进大菱鲆&#xff08;Scophthalmus maximus&#xff0c;商品名&#xff1a;多宝鱼&#xff09;以…

面试必考精华版Leetcode1926. 迷宫中离入口最近的出口

题目&#xff1a; 代码&#xff08;2023年10月26日 首刷看解析&#xff09;&#xff1a; class Solution { public:int nearestExit(vector<vector<char>>& maze, vector<int>& entrance) {int m maze.size();int n maze[0].size();vector<int…

男怕入错行!程序员千万别选错赛道

不知道有没有小伙伴和我一样&#xff0c;上大学报软件工程是奔着开发游戏去的 进去之后发现和自己想的完全不一样 不仅学的东西和游戏开发没啥关系&#xff0c;关系是导师基本是摸鱼的 而且全要靠自学&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 本人当年…

AGI超级互联启动会落幕,UCC启动城市节点,促算力普及千家万户

10月24日&#xff0c;由中关村超互联新基建产业创新联盟、中国智能计算产业联盟主办&#xff0c;互联科技、鼎好DH3联合承办的“AGI超级互联启动会&#xff08;AGI Ultra Cross Connects Kick-off Convention&#xff09;”在北京鼎好DH3圆满举行。 本次活动汇聚众多行业专家、…

遍历树形结构记录

例如: 这是递归对树形结构的遍历 findMatchingValue(json1,json2){ if(json1.defaultLabel&&json2.some(item>item.titlejson1.defaultLabel)){ //将匹配的值保存起来 this.matchedValue.push(json1.defaultLabel) } if(json1.childrens&&json1.childrens.…

【C++项目】高并发内存池第四讲 申请内存过程介绍流程介绍

申请内存过程介绍 1.主函数执行2.ThreadCache3.CentralCache4.PageCache 1.主函数执行 先从内存池申请内存 获取ThreadCache对象&#xff0c;然后去ThreadCache对象的Allocate申请&#xff01; 2.ThreadCache 这里要计算对齐函数和相应的桶的下标&#xff0c;这个之前介绍T…

JavaScript事件处理:探索DOM事件和事件监听器

目录 DOM事件简介 什么是事件&#xff1f; 事件处理程序 在HTML中添加事件处理程序 事件类型 事件对象 事件监听器 事件冒泡与事件捕获 常见的事件类型和用法 总结 在Web开发中&#xff0c;JavaScript事件处理是一个重要的概念。通过事件处理&#xff0c;我们可以对用…

【linux】倒计时小程序

倒计时小程序 第一步&#xff08;创建一个目录&#xff09;mkdir processbar&#xff1a; 进入目录cd processbar&#xff1a; 第二步&#xff08;创建一个.h文件【声明】&#xff0c;两个.c文件【实现】和【函数调用】的文件&#xff09;touch processBar.h touch processB…