Table of Contents
1. Upload component upload.vue
1.1 Template planning
1.2 Click the Add button
1.2.1 Implement inquiry pop-up box
1.2.2 Realize taking pictures
1.2.3 Implement album selection
1.2.4 Implement file upload
1.2.5 Verify image type and upload
1.2.6 Get image list
1.2.7 Add image attachments within the component
2. Image enlargement component enlarge-image.vue
2.1 Click on the image to enlarge
2.2 Template planning
2.3 The process of using swiper11 to avoid pitfalls
1. Upload component upload.vue
1.1 Template Planning
The template contains three parts:
- A list of uploaded images is displayed. If it is read-only, the delete button will not be displayed. Each image can be enlarged after being clicked.
- Add button display. If there is no picture and it is not read-only, it will be displayed.
- No picture prompt yet
<div class="t-upload"> <ion-grid> <!-- {<!-- -->{ fileList }} --> <!-- Uploaded pictures --> <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> <!-- Add image button --> <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 & amp; & amp; readonly"> <div class="fs-14">No attachments</div> </template> </ion-grid> </div>
1.2 Click the add button
After clicking the Add button, a pop-up box will appear allowing the user to select the image source:
- Photograph
- Album selection
1.2.1 Implement inquiry pop-up box
This is very simple. You can use ionic’s actionSheetController to implement it. The execution method is determined based on the user’s choice.
async addFile(max: number, callback: any) { const actionSheet = await actionSheetController.create({ header: 'Attachment type selection', buttons: [ { text: 'take a photo', 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('Photography failed, please try again'); }); }, }, { text: 'Album', handler: () => { this.slectImagePicker({ maximumImagesCount: max, quality: 50, }) .then((res) => { callback(res, 'img'); }) .catch(() => { publicService.toast('Album opening failed'); }); }, }, { text: 'Cancel', role: 'cancel', handler: () => { console.error('Cancel clicked'); }, }, ], }); await actionSheet.present(); }
1.2.2 Realize taking photos
Install the cordova plugin:
- @awesome-cordova-plugins/[email protected]
- [email protected]
Common problems: When debugging on a real machine, when I click to take a photo, I am prompted that I have passed in illegal parameters.
Solution: Upgrade the camera plug-in version. The original version was 4.1.4. After upgrading to 7.0.0, the problem will be automatically solved.
This method ultimately returns a picture object information
// Used to take photos or select photos from the album import { Camera, CameraOptions } from '@awesome-cordova-plugins/camera'; /** * Photograph * @param opts photo configuration * @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 Realize photo album selection
Install the cordova plugin:
- @awesome-cordova-plugins/[email protected]
- [email protected]
Common problems: In the album selection interface, the confirmation button is in English.
Solution: In node_modules, find the xml file in the plug-in source code, search for relevant English words, and change it to Chinese
This method ultimately returns a set of image object information
// Used to select photos from the album import { ImagePicker, ImagePickerOptions } from '@awesome-cordova-plugins/image-picker'; /** * Photo selection * @param opts * @returns */ slectImagePicker(opts: ImagePickerOptions): Promise<any> { // console.log('Photo selection ImagePicker ---', ImagePicker); return new Promise((resolve, reject) => { ImagePicker.getPictures(opts) .then((res) => { resolve(res); }) .catch(() => { reject('slectImagePicker native error'); }); }); }
1.2.4 Implement file upload
Install the cordova plugin:
- @awesome-cordova-plugins/[email protected]
- [email protected]
Common problems:
- If the data returned by the interface is not a JSON object but a map object, the parsing may fail.
- The file name on the server needs to be unique
- The server address passed to the file-transfer plug-in needs to be encoded using encodeURI.
- The file-transfer plug-in will save the file stream parameters given to the interface into file by default. You can specify the parameter name through fileKey.
solution:
- The data returned by the interface will eventually be stored in res.response. This is a layer encapsulated by the plug-in for us, allowing the backend to write the returned data into the format of a JSON object.
- Use timestamp to ensure name uniqueness, new Date().getTime() + (name || `${pathArr[pathArr.length – 1]}`)
- Encoding server address: encodeURI(API.uploadFile.serviceApi)
- fileKey: ‘form_file’, //The name of the form element, the default is file, you can also negotiate with the backend, the file stream will be stored in this variable
//File upload and download import { FileTransfer, FileUploadOptions, } from '@awesome-cordova-plugins/file-transfer'; /** * File Upload * @param fileUrl file path * @param url server address * @param opts configuration items * @returns */ fileLoad( fileUrl: string, url: string, opts: FileUploadOptions ): Promise<any> { return new Promise((resolve, reject) => { //Create file upload instance 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('File upload error native ---', err); publicService.closeLoading(this.loading); this.loading = null; reject('An error occurred while uploading the file'); }); }); }
1.2.5 Verify image type and upload
/** * File Upload * @param fileUrl attachment address * @paramascriptionTypename attachment name */ function fileUpload(fileUrl: string, name?: string) { // Get file name const pathArr = state.fileUrl.split('?')[0].split('/') || ''; //File format suffix const suffix = state.fileUrl.substring( state.fileUrl.lastIndexOf('.') + 1 ); //File format suffix const suffixs = ['png', 'jpg', 'jpeg', 'svg']; //Upload the file after passing the file type verification let fileTypeValidate = true; if (state.fileUrl & amp; & amp; !suffix) { fileTypeValidate = false; toast('Unsupported file type'); } else if (!suffixs.includes(suffix)) { fileTypeValidate = false; toast(`${suffix} file type not supported`); } // If the file format is illegal, it will not be uploaded. if (!fileTypeValidate) { if (publicService.loading) { publicService.closeLoading(publicService.loading); publicService.loading = null; } return; } // Get file name const fileName = new Date().getTime() + (name || `${pathArr[pathArr.length - 1]}`); nativeService .fileLoad(fileUrl, encodeURI(API.uploadFile.serviceApi), { fileName, // The file name to use when saving the file on the server fileKey: 'form_file', //The name of the form element, the default is file, you can also negotiate with the backend, the file stream will be stored in this variable httpMethod: 'POST', //Upload interface request method params: { //Business data ID (used for business-related attachments) businessKey: props.businessKey, // Form ID (can be empty) - grouping inputFileId: props.inputFileId, // document form_file: fileUrl, //Login user createUser: userInfos.userId, }, }) .then((res) => { console.log('upload.vue upload interface response ---', res.response); const testJX = JSON.parse(res.response); console.log('Try to parse ---', testJX); if (publicService.loading) { publicService.closeLoading(publicService.loading); publicService.loading = null; } // Get file list getFiles(); }) .catch((err) => { console.error(err); }); }
1.2.6 Get picture list
Several scenarios for getting the picture list getFiles:
- After uploading successfully
- After successful deletion
- When the component is initialized
- watch monitors changes in the business parameters passed to the interface, such as businessKey
Each time you obtain the picture list, you should emit the latest picture list information.
1.2.7 Add image attachments within the component
As mentioned above, taking a photo returns a picture information, and selecting an album returns a set of picture information.
According to the type of returned information, you can get the local path and name of the image on the mobile phone, and call the file upload in sequence.
nativeService.addFile( 9 - state.fileList.length, (res: any, fileType: string, name?: string) => { if (fileType === 'photo') { publicService.loading = publicService.sloading('Photos are being uploaded, please wait...'); state.fileUrl = res; // File Upload fileUpload(res, name || ''); } else if (Array.isArray(res)) { if (res. length) { publicService.loading = publicService.sloading('Photo album selection is being uploaded, please wait...'); } res.forEach(async (item, index1) => { state.fileUrl = item; state.fileName = name || ''; // File Upload await fileUpload(item, name || ''); }); } else { publicService.loading = publicService.sloading('Attachment is being uploaded, please wait...'); state.fileUrl = res; state.fileName = name || ''; // File Upload fileUpload(res, name || ''); } } );
2. Image enlargement component enlarge-image.vue
2.1 Click on the image to enlarge
Use ionic modalController to create a pop-up page
Inside the method, pass in information such as the image amplification component, various parameters required by the component, and the default displayed image index.
/** * Open the picture preview interface * @param index currently clicked image index */ async function goEnlargeImage(index: number) { // console.log('t-upload.vue clicked image index ---', 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 Template Planning
Because the blogger is using ionic7, components such as ion-slide no longer exist.
Through the official website prompt Vue Slides Guide: How to Get Swiper for Vue on Ionic Apps, I decided to use the swiper plug-in to meet the requirements.
The component accepts two parameters:
- The picture list pictures is associated with the calculated attribute stateImageList in the component to ensure single data flow.
- The currently activated image index initialSlide, which defaults to 0, is associated with the initialSlide property of swiper.
The template is as follows:
<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' & amp; & amp; item.FILE_SUFFIX.toLowerCase() !== 'mp3') " :src="getImgUrl(item)" :style="{ 'max-height': maxHeight }" /> </div> <!-- {<!-- -->{ getImgUrl(item) }} --> </swiper-slide> </swiper> </ion-content> </ion-page>
2.3 The process of using swiper11 to overcome pitfalls
The ionic official website says to install swiper: npm install swiper@latest
After executing this command, npm will only install swiper/vue for you, but not swiper/modules. This is a very big problem, because modules such as Navigation and Pagination must install swiper/modules before they can be imported. We need to install them manually.
Introduce swiper related content:
// 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'; //Responsive variables const state = reactive({ // swiper extension module // modules: [Navigation, Pagination], // maximum height maxHeight: window.innerHeight - 100, }); // Picture list const stateImageList = computed(() => { return JSON.parse(JSON.stringify(props.pictures)); }); function start(ev: any) { // console.log('ev ---', ev, stateImageList.value); } /** * Close the current page */ function closePage() { modalController.dismiss(); }