hookehuyr

✨ feat(图片上传控件): 样式和功能调整

...@@ -202,6 +202,10 @@ const _sfc_main = create({ ...@@ -202,6 +202,10 @@ const _sfc_main = create({
202 type: Array, 202 type: Array,
203 default: () => ['image', 'video', 'mix'], 203 default: () => ['image', 'video', 'mix'],
204 }, 204 },
205 + imageType: {
206 + type: Array,
207 + default: () => [],
208 + },
205 camera: { 209 camera: {
206 type: String, 210 type: String,
207 default: 'back', 211 default: 'back',
...@@ -248,6 +252,7 @@ const _sfc_main = create({ ...@@ -248,6 +252,7 @@ const _sfc_main = create({
248 'delete', 252 'delete',
249 'update:fileList', 253 'update:fileList',
250 'file-item-click', 254 'file-item-click',
255 + 'image-type-error',
251 ], 256 ],
252 setup(props, { emit }) { 257 setup(props, { emit }) {
253 const fileList = reactive(props.fileList) 258 const fileList = reactive(props.fileList)
...@@ -475,9 +480,18 @@ const _sfc_main = create({ ...@@ -475,9 +480,18 @@ const _sfc_main = create({
475 const maximize = props.maximize * 1 480 const maximize = props.maximize * 1
476 const oversizes = new Array() 481 const oversizes = new Array()
477 files = files.filter((file) => { 482 files = files.filter((file) => {
483 + if (Taro.getEnv() != 'WEAPP') {
484 + file.type = file.originalFileObj.name.split('.').pop()
485 + } else {
486 + file.type = file.tempFilePath.split('.').pop()
487 + }
478 if (file.size > maximize) { 488 if (file.size > maximize) {
479 oversizes.push(file) 489 oversizes.push(file)
480 return false 490 return false
491 + } else if (!props.imageType.includes(file.type)) {
492 + // 控制文件类型上传
493 + emit('image-type-error', file.type)
494 + return false
481 } else { 495 } else {
482 return true 496 return true
483 } 497 }
......
1 <!-- 1 <!--
2 * @Date: 2022-08-31 16:16:49 2 * @Date: 2022-08-31 16:16:49
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2023-04-12 10:59:48 4 + * @LastEditTime: 2023-04-12 14:19:07
5 * @FilePath: /custom_form/src/components/FileUploaderField/index.vue 5 * @FilePath: /custom_form/src/components/FileUploaderField/index.vue
6 * @Description: 文件上传控件 6 * @Description: 文件上传控件
7 --> 7 -->
...@@ -20,19 +20,6 @@ ...@@ -20,19 +20,6 @@
20 v-html="item.component_props.note" 20 v-html="item.component_props.note"
21 style="font-size: 13px; margin-left: 1rem; color: gray; padding-bottom: 0.5rem; padding-top: 0.25rem; white-space: pre-wrap;" 21 style="font-size: 13px; margin-left: 1rem; color: gray; padding-bottom: 0.5rem; padding-top: 0.25rem; white-space: pre-wrap;"
22 /> 22 />
23 - <!-- <div>
24 - <p
25 - v-for="(file, index) in fileList"
26 - :key="index"
27 - style="padding-left: 1rem; margin-bottom: 0.5rem"
28 - >
29 - <p style="font-size: 1rem; word-break: break-all; margin-right: 0.75rem;">
30 - <span>{{ index + 1 }}.&nbsp;{{ file.name }}&nbsp;&nbsp;{{ (file.size / 1024 / 1024).toFixed(2) }}MB</span>
31 - &nbsp;&nbsp;
32 - <span style="color: #e32525; font-size: 0.85rem" @click="beforeDelete(file)">移除</span>
33 - </p>
34 - </p>
35 - </div> -->
36 <div style="padding: 1rem; padding-top: 0.5rem;"> 23 <div style="padding: 1rem; padding-top: 0.5rem;">
37 <nut-uploader 24 <nut-uploader
38 :name="item.name" 25 :name="item.name"
...@@ -49,7 +36,7 @@ ...@@ -49,7 +36,7 @@
49 @failure="uploadFailure" 36 @failure="uploadFailure"
50 @delete="onDelete" 37 @delete="onDelete"
51 @change="onChange" 38 @change="onChange"
52 - @file-item-click="fileItemClick" 39 + @image-type-error="imageTypeError"
53 > 40 >
54 <nut-button shape="square" type="primary"> 41 <nut-button shape="square" type="primary">
55 <template #icon> 42 <template #icon>
...@@ -96,9 +83,12 @@ const HideShow = computed(() => { ...@@ -96,9 +83,12 @@ const HideShow = computed(() => {
96 83
97 const emit = defineEmits(["active"]); 84 const emit = defineEmits(["active"]);
98 85
86 +// // 固定类型限制
87 +// const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'psd', 'tif'];
88 +
99 // // 文件类型中文页面显示 89 // // 文件类型中文页面显示
100 // const type_text = computed(() => { 90 // const type_text = computed(() => {
101 -// return props.item.component_props.file_type; 91 +// return imageTypes.join('/');
102 // }); 92 // });
103 93
104 // 上传文件集合 94 // 上传文件集合
...@@ -121,12 +111,18 @@ const onOversize = (files) => { ...@@ -121,12 +111,18 @@ const onOversize = (files) => {
121 toast_type.value = 'warn'; 111 toast_type.value = 'warn';
122 }; 112 };
123 113
114 +const onStart = (options) => {
115 + // console.warn(options);
116 +}
117 +
124 // 自定义上传逻辑 118 // 自定义上传逻辑
125 const beforeXhrUpload = async (xhr, options) => { 119 const beforeXhrUpload = async (xhr, options) => {
120 + const imgObj = defaultFileList.value[defaultFileList.value.length - 1];
121 + // 判断上传文件格式
122 + imgObj.type = imgObj.type ? imgObj.type : imgObj.name.split(".").pop();
126 // H5环境 123 // H5环境
127 if (process.env.TARO_ENV === 'h5') { 124 if (process.env.TARO_ENV === 'h5') {
128 // 把本地路径转换成file实体 125 // 把本地路径转换成file实体
129 - const imgObj = defaultFileList.value[defaultFileList.value.length - 1];
130 const imgBlob = await fetch(imgObj.url).then(r => r.blob()); 126 const imgBlob = await fetch(imgObj.url).then(r => r.blob());
131 const imgFile = new File([imgBlob], imgObj.name , { type: imgObj.type }); 127 const imgFile = new File([imgBlob], imgObj.name , { type: imgObj.type });
132 // 上传返回file数据结构 128 // 上传返回file数据结构
...@@ -146,7 +142,6 @@ const beforeXhrUpload = async (xhr, options) => { ...@@ -146,7 +142,6 @@ const beforeXhrUpload = async (xhr, options) => {
146 loading.value = false; 142 loading.value = false;
147 } 143 }
148 } else { 144 } else {
149 - const imgObj = defaultFileList.value[defaultFileList.value.length - 1];
150 const fs = Taro.getFileSystemManager() 145 const fs = Taro.getFileSystemManager()
151 fs.getFileInfo({ 146 fs.getFileInfo({
152 filePath: imgObj.url, 147 filePath: imgObj.url,
...@@ -250,9 +245,12 @@ const onDelete = ({ file }) => { ...@@ -250,9 +245,12 @@ const onDelete = ({ file }) => {
250 // 完整数据回调到表单上 245 // 完整数据回调到表单上
251 emit("active", props.item.value); 246 emit("active", props.item.value);
252 } 247 }
253 -// 上传成功,点击队列项回调 248 +
254 -const fileItemClick = (fileItem) => { 249 +//
255 - console.warn(fileItem); 250 +const imageTypeError = (file) => {
251 + toast_msg.value = '请上传指定格式'
252 + toast_show.value = true;
253 + toast_type.value = 'warn';
256 } 254 }
257 255
258 const onChange = ({ fileList }) => { 256 const onChange = ({ fileList }) => {
...@@ -379,7 +377,7 @@ defineExpose({ validFileUploader }); ...@@ -379,7 +377,7 @@ defineExpose({ validFileUploader });
379 } 377 }
380 378
381 .type-text { 379 .type-text {
382 - font-size: 0.9rem; 380 + font-size: 13px;
383 margin-left: 1rem; 381 margin-left: 1rem;
384 padding-bottom: 1rem; 382 padding-bottom: 1rem;
385 color: gray; 383 color: gray;
......
1 <!-- 1 <!--
2 * @Date: 2022-08-31 16:16:49 2 * @Date: 2022-08-31 16:16:49
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2023-01-17 16:44:32 4 + * @LastEditTime: 2023-04-12 15:01:26
5 - * @FilePath: /data-table/src/components/ImageUploaderField/index.vue 5 + * @FilePath: /custom_form/src/components/ImageUploaderField/index.vue
6 * @Description: 图片上传控件 6 * @Description: 图片上传控件
7 --> 7 -->
8 <template> 8 <template>
9 <div v-if="HideShow" class="image-uploader-field"> 9 <div v-if="HideShow" class="image-uploader-field">
10 <div class="label"> 10 <div class="label">
11 - <span v-if="item.component_props.required">&nbsp;*</span> 11 + <text v-if="item.component_props.required">&nbsp;*</text>
12 {{ item.component_props.label }} 12 {{ item.component_props.label }}
13 </div> 13 </div>
14 - <div 14 + <div style="font-size: 12px; color: red; margin-left: 20px;">
15 - v-if="item.component_props.note" 15 + <text>最大图片数为 {{ item.component_props.max_count }} 张</text>,
16 - v-html="item.component_props.note" 16 + <text>单个图片最大体积 {{ item.component_props.max_size }} MB</text>
17 - style="font-size: 0.9rem; margin-left: 1rem; color: gray; padding-bottom: 0.5rem; padding-top: 0.25rem; white-space: pre;" 17 + </div>
18 - /> 18 + <div v-if="item.component_props.note" v-html="item.component_props.note"
19 - <div style="padding: 1rem"> 19 + style="font-size: 13px; margin-left: 1rem; color: gray; padding-bottom: 0.5rem; padding-top: 0.25rem; white-space: pre-wrap;" />
20 - <van-uploader 20 + <div style="padding: 1rem; padding-top: 0.5rem;">
21 + <nut-uploader
21 :name="item.name" 22 :name="item.name"
22 - upload-icon="add" 23 + v-model:file-list="defaultFileList"
23 - :before-read="beforeRead" 24 + :maximum="item.component_props.max_count"
24 - :after-read="afterRead" 25 + :multiple="item.component_props.max_count > 1"
25 - :before-delete="beforeDelete" 26 + :size-type="['compressed']"
26 - v-model="fileList" 27 + :image-type="imageTypes"
27 - :multiple="item.component_props.max_size > 1" 28 + :maximize="max_size"
28 - /> 29 + :before-xhr-upload="beforeXhrUpload"
30 + @oversize="onOversize"
31 + @success="uploadSuccess"
32 + @failure="uploadFailure"
33 + @delete="onDelete"
34 + @change="onChange"
35 + @image-type-error="imageTypeError">
36 + </nut-uploader>
29 </div> 37 </div>
30 - <div class="type-text">上传类型:&nbsp;{{ type_text }}</div> 38 + <div class="type-text">上传格式:{{ type_text }}</div>
31 - <div 39 + <div v-if="show_error" style="padding: 5px 20px; color: red; font-size: 12px;">
32 - v-if="show_empty" 40 + {{ error_msg }}
33 - class="van-field__error-message"
34 - style="padding: 0 1rem 1rem 1rem"
35 - >
36 - 图片上传不能为空
37 </div> 41 </div>
38 - <van-divider /> 42 + <nut-divider :style="{ color: '#ebedf0' }" />
43 + <nut-overlay v-model:visible="loading">
44 + <div class="wrapper" style="color: white; font-size: 15px;">
45 + <Loading />
46 + 上传中...
47 + </div>
48 + </nut-overlay>
49 + <nut-toast :msg="toast_msg" v-model:visible="toast_show" :type="toast_type" />
39 </div> 50 </div>
40 -
41 - <van-overlay :show="loading">
42 - <div class="wrapper" @click.stop>
43 - <van-loading vertical color="#FFFFFF">上传中...</van-loading>
44 - </div>
45 - </van-overlay>
46 </template> 51 </template>
47 52
48 <script setup> 53 <script setup>
49 -/** 54 +import { ref, computed, watch, onMounted, reactive } from "vue";
50 - * 图片上传
51 - * @param name[String] 组件名称
52 - * @param image_type[Array] 图片上传类型
53 - * @param multiple[Boolean] 图片多选
54 - */
55 -import { showSuccessToast, showFailToast, showToast } from "vant";
56 -import _ from "lodash";
57 -import { v4 as uuidv4 } from "uuid";
58 import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from "@/api/common"; 55 import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from "@/api/common";
59 import BMF from "browser-md5-file"; 56 import BMF from "browser-md5-file";
60 -import { useRoute } from "vue-router"; 57 +import { getUrlParams } from "@/utils/tools";
61 -import axios from "axios"; 58 +import { Uploader, Loading } from '@nutui/icons-vue-taro';
62 -import { getEtag } from "@/utils/qetag.js"; // 生成hash值 59 +import Taro from '@tarojs/taro'
63 60
64 -const $route = useRoute();
65 const props = defineProps({ 61 const props = defineProps({
66 item: Object, 62 item: Object,
67 }); 63 });
...@@ -69,156 +65,204 @@ const props = defineProps({ ...@@ -69,156 +65,204 @@ const props = defineProps({
69 const HideShow = computed(() => { 65 const HideShow = computed(() => {
70 return !props.item.component_props.disabled 66 return !props.item.component_props.disabled
71 }) 67 })
68 +
72 const emit = defineEmits(["active"]); 69 const emit = defineEmits(["active"]);
73 -const show_empty = ref(false);
74 70
75 // 固定类型限制 71 // 固定类型限制
76 -const imageTypes = "jpg/jpeg/png/gif/bmp/psd/tif"; 72 +const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'psd', 'tif'];
77 73
78 // 文件类型中文页面显示 74 // 文件类型中文页面显示
79 const type_text = computed(() => { 75 const type_text = computed(() => {
80 - // return props.item.component_props.image_type; 76 + return imageTypes.join('/');
81 - return imageTypes;
82 }); 77 });
83 -// 上传图片集合
84 -const fileList = ref([
85 - // { url: "https://fastly.jsdelivr.net/npm/@vant/assets/leaf.jpeg" },
86 - // Uploader 根据文件后缀来判断是否为图片文件
87 - // 如果图片 URL 中不包含类型信息,可以添加 isImage 标记来声明
88 - // { url: 'https://cloud-image', isImage: true },
89 -]);
90 78
91 -// 上传前置处理 79 +// 上传文件集合
92 -const beforeRead = (file) => { 80 +const fileList = ref([]);
93 - // 类型限制 81 +const defaultFileList = ref([])
94 - // const image_types = _.map(
95 - // props.item.component_props.image_type.split("/"),
96 - // (item) => `image/${item}`
97 - // );
98 - const image_types = _.map(imageTypes.split("/"), (item) => `image/${item}`);
99 82
100 - let flag = true; 83 +// 上传文件体积
101 - if (_.isArray(file)) { 84 +const max_size = computed(() => {
102 - // 多张图片 85 + return props.item.component_props.max_size * 1024 * 1024
103 - const types = _.difference(_.uniq(_.map(file, (item) => item.type)), image_types); // 数组返回不能上传的类型 86 +})
104 - if (types.length) { 87 +
105 - flag = false; 88 +const toast_msg = ref('');
106 - showFailToast("请上传指定格式图片"); 89 +const toast_show = ref(false);
107 - } 90 +const toast_type = ref('success');
108 - if (fileList.value.length + file.length > props.item.component_props.max_count) { 91 +
109 - // 数量限制 92 +// 超过体积大小回调
110 - flag = false; 93 +const onOversize = (files) => {
111 - showToast(`最大上传数量为${props.item.component_props.max_count}张`); 94 + toast_msg.value = `最大图片体积为${props.item.component_props.max_size}MB`
112 - } 95 + toast_show.value = true;
113 - } else { 96 + toast_type.value = 'warn';
114 - if (!_.includes(image_types, file.type)) {
115 - showFailToast("请上传指定格式图片");
116 - flag = false;
117 - }
118 - if (fileList.value.length + 1 > props.item.component_props.max_count) {
119 - // 数量限制
120 - flag = false;
121 - showToast(`最大上传数量为${props.item.component_props.max_count}张`);
122 - }
123 - if ((file.size / 1024 / 1024).toFixed(2) > props.item.component_props.max_size) {
124 - // 体积限制
125 - flag = false;
126 - showToast(
127 - `最大文件体积为${props.item.component_props.max_size}MB`
128 - );
129 - }
130 - }
131 - return flag;
132 }; 97 };
133 98
134 -// 文件读取完成后的回调函数 99 +const onStart = (options) => {
135 -const afterRead = async (files) => { 100 + // console.warn(options);
136 - if (Array.isArray(files)) { 101 +}
137 - // 多张图片上传files是一个数组 102 +
138 - muliUpload(files); 103 +// 自定义上传逻辑
139 - } else { 104 +const beforeXhrUpload = async (xhr, options) => {
140 - const imgUrl = await handleUpload(files); 105 + const imgObj = defaultFileList.value[defaultFileList.value.length - 1];
106 + // 判断上传文件格式
107 + imgObj.type = imgObj.type ? imgObj.type : imgObj.name.split(".").pop();
108 + // H5环境
109 + if (process.env.TARO_ENV === 'h5') {
110 + // 把本地路径转换成file实体
111 + const imgBlob = await fetch(imgObj.url).then(r => r.blob());
112 + const imgFile = new File([imgBlob], imgObj.name, { type: imgObj.type });
113 + // 上传返回file数据结构
114 + const resImgObj = await handleUpload(imgFile);
141 // 上传失败提示 115 // 上传失败提示
142 - if (!imgUrl.src) { 116 + if (!resImgObj) {
143 - files.status = "failed"; 117 + options.onFailure?.(resImgObj, options);
144 - files.message = "上传失败";
145 loading.value = false; 118 loading.value = false;
146 } else { 119 } else {
147 - files.status = ""; 120 + defaultFileList.value[defaultFileList.value.length - 1]['url'] = resImgObj.src;
148 - files.message = ""; 121 + defaultFileList.value[defaultFileList.value.length - 1]['type'] = 'image';
149 fileList.value.push({ 122 fileList.value.push({
150 - // meta_id: imgUrl.meta_id, 123 + name: imgFile.name,
151 - name: files.file.name, 124 + url: resImgObj.src,
152 - url: imgUrl.src, 125 + size: imgFile.size
153 - // isImage: true,
154 }); 126 });
127 + options.onSuccess?.(resImgObj, options);
155 loading.value = false; 128 loading.value = false;
156 } 129 }
130 + } else {
131 + const fs = Taro.getFileSystemManager()
132 + fs.getFileInfo({
133 + filePath: imgObj.url,
134 + success: async (res) => {
135 + const file_info = res;
136 + let suffix = /\.[^\.]+$/.exec(imgObj.name); // 获取后缀
137 + // 获取七牛token
138 + const filename = imgObj.name; // 真实文件名
139 + const getToken = await qiniuTokenAPI({
140 + name: filename,
141 + hash: file_info.digest,
142 + });
143 + // 文件上传七牛云
144 + // 第一次上传
145 + if (getToken.token) {
146 + loading.value = true;
147 + // 自拍图片上传七牛服务器
148 + Taro.uploadFile({
149 + url: 'https://up.qbox.me',
150 + filePath: imgObj.url,
151 + name: `file`,
152 + formData: {
153 + token: getToken.token,
154 + key: `uploadForm/${formCode}/${file_info.digest}${suffix}`
155 + },
156 + })
157 + .then(async (res) => {
158 + res.data = JSON.parse(res.data);
159 + if (res.data.filekey) {
160 + // 保存文件
161 + const { data } = await saveFileAPI({
162 + name: filename,
163 + filekey: res.data.filekey,
164 + hash: file_info.digest,
165 + });
166 + defaultFileList.value[defaultFileList.value.length - 1]['url'] = data.src;
167 + defaultFileList.value[defaultFileList.value.length - 1]['type'] = 'image';
168 + // 加入上传成功队列
169 + fileList.value.push({
170 + name: filename,
171 + url: data.src,
172 + size: file_info.size
173 + });
174 + options.onSuccess?.(data, options);
175 + loading.value = false;
176 + }
177 + })
178 + .catch((error) => {
179 + console.error(error)
180 + options.onFailure?.(error, options);
181 + loading.value = false;
182 + })
183 + }
184 + // 重复上传
185 + if (getToken.data) {
186 + // 加入上传成功队列
187 + fileList.value.push({
188 + name: filename,
189 + url: getToken.data.src,
190 + size: file_info.size
191 + });
192 + options.onSuccess?.(getToken.data, options);
193 + }
194 + }
195 + })
157 } 196 }
158 - // 过滤非包含URL的图片 197 +}
159 - fileList.value = fileList.value.filter((item) => { 198 +
160 - if (item.url) return item; 199 +// 上传成功回调
161 - }); 200 +const uploadSuccess = async ({ data, fileItem, option, responseText }) => {
162 props.item.value = { 201 props.item.value = {
163 - key: "image_uploader", 202 + key: "file_uploader",
164 filed_name: props.item.key, 203 filed_name: props.item.key,
165 - // value: fileList.value.map((item) => item.url),
166 value: fileList.value, 204 value: fileList.value,
167 }; 205 };
168 - show_empty.value = false;
169 // 完整数据回调到表单上 206 // 完整数据回调到表单上
170 emit("active", props.item.value); 207 emit("active", props.item.value);
208 + // 校验数据
209 + validImageUploader();
210 +};
211 +
212 +// 上传失败回调
213 +const uploadFailure = async ({ data, fileItem, option, responseText }) => {
214 + // 真实上传失败才会提示
215 + if (data) {
216 + console.error("上传失败", "fail");
217 + toast_msg.value = '上传失败,请重新尝试!'
218 + toast_show.value = true;
219 + toast_type.value = 'fail';
220 + }
171 }; 221 };
172 222
173 -// 文件删除前的回调函数 223 +// 删除上传队列回调
174 -const beforeDelete = (files) => { 224 +const onDelete = ({ file }) => {
175 fileList.value = fileList.value.filter((item) => { 225 fileList.value = fileList.value.filter((item) => {
176 - if (item.url !== files.url) return item; 226 + if (item.url !== file.url) return item;
177 }); 227 });
178 props.item.value = { 228 props.item.value = {
179 - key: "image_uploader", 229 + key: "file_uploader",
180 filed_name: props.item.key, 230 filed_name: props.item.key,
181 - // value: fileList.value.map((item) => item.url),
182 value: fileList.value, 231 value: fileList.value,
183 }; 232 };
184 // 完整数据回调到表单上 233 // 完整数据回调到表单上
185 emit("active", props.item.value); 234 emit("active", props.item.value);
186 -}; 235 +}
187 236
188 -/********** 上传七牛云获取图片地址 ***********/ 237 +//
189 -const loading = ref(false); 238 +const imageTypeError = (file) => {
190 -const formCode = $route.query.code; // 表单code 239 + toast_msg.value = '请上传指定格式'
191 -// const uuid = () => { 240 + toast_show.value = true;
192 -// let s = []; 241 + toast_type.value = 'warn';
193 -// let hexDigits = "0123456789abcdef"; 242 +}
194 -// for (var i = 0; i < 36; i++) {
195 -// s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
196 -// }
197 -// s[14] = "4";
198 -// s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
199 -// s[8] = s[13] = s[18] = s[23] = "-";
200 243
201 -// var uuid = s.join(""); 244 +const onChange = ({ fileList }) => {
202 -// return uuid; 245 +}
203 -// };
204 246
205 -// 上传图片返回图片URL 247 +/********** 上传七牛云获取文件地址 ***********/
206 -const handleUpload = async (files) => { 248 +const loading = ref(false);
249 +const formCode = getUrlParams(location.href) ? getUrlParams(location.href).code : ''; // 表单code
250 +
251 +// 上传文件返回文件URL
252 +const handleUpload = async (file) => {
207 loading.value = true; 253 loading.value = true;
208 - // 获取HASH值
209 - // const hash = getEtag(files.content);
210 return new Promise((resolve, reject) => { 254 return new Promise((resolve, reject) => {
211 // 获取MD5值 255 // 获取MD5值
212 const bmf = new BMF(); 256 const bmf = new BMF();
213 bmf.md5( 257 bmf.md5(
214 - files.file, 258 + file,
215 async (err, md5) => { 259 async (err, md5) => {
216 if (err) { 260 if (err) {
217 console.log(err); 261 console.log(err);
218 reject(err); 262 reject(err);
219 } 263 }
220 // 获取七牛token 264 // 获取七牛token
221 - const filename = files.file.name; // 真实文件名 265 + const filename = file.name; // 真实文件名
222 const getToken = await qiniuTokenAPI({ 266 const getToken = await qiniuTokenAPI({
223 name: filename, 267 name: filename,
224 hash: md5, 268 hash: md5,
...@@ -227,10 +271,8 @@ const handleUpload = async (files) => { ...@@ -227,10 +271,8 @@ const handleUpload = async (files) => {
227 let imgUrl = ""; 271 let imgUrl = "";
228 // 第一次上传 272 // 第一次上传
229 if (getToken.token) { 273 if (getToken.token) {
230 - files.status = "uploading"; 274 + // 返回数据库真实文件地址
231 - files.message = "上传中..."; 275 + imgUrl = await uploadQiniu(file, getToken.token, filename, md5);
232 - // 返回数据库真实图片地址
233 - imgUrl = await uploadQiniu(files.file, getToken.token, filename, md5);
234 } 276 }
235 // 重复上传 277 // 重复上传
236 if (getToken.data) { 278 if (getToken.data) {
...@@ -245,39 +287,7 @@ const handleUpload = async (files) => { ...@@ -245,39 +287,7 @@ const handleUpload = async (files) => {
245 }); 287 });
246 }; 288 };
247 289
248 -// 多选图片上传遍历 290 +// 生成数据库真实文件地址
249 -var muliUpload = async (files) => {
250 - for (let item of files) {
251 - const res = await handleUpload(item);
252 - // 上传失败提示
253 - if (!res.src) {
254 - item.status = "failed";
255 - item.message = "上传失败";
256 - loading.value = false;
257 - } else {
258 - item.status = "";
259 - item.message = "";
260 - fileList.value.push({
261 - // meta_id: res.meta_id,
262 - name: item.file.name,
263 - url: res.src,
264 - // isImage: true,
265 - });
266 - loading.value = false;
267 - }
268 - }
269 -};
270 -
271 -
272 -
273 -const getType = (file, name) => {
274 - var index1 = name.lastIndexOf(".");
275 - var index2 = file.length;
276 - var type = file.substring(index1, index2).toUpperCase();
277 - return type;
278 -}
279 -
280 -// 生成数据库真实图片地址
281 const uploadQiniu = async (file, token, name, md5) => { 291 const uploadQiniu = async (file, token, name, md5) => {
282 let suffix = /\.[^\.]+$/.exec(name); // 获取后缀 292 let suffix = /\.[^\.]+$/.exec(name); // 获取后缀
283 // let affix = uuidv4(); 293 // let affix = uuidv4();
...@@ -289,27 +299,32 @@ const uploadQiniu = async (file, token, name, md5) => { ...@@ -289,27 +299,32 @@ const uploadQiniu = async (file, token, name, md5) => {
289 let config = { 299 let config = {
290 headers: { "Content-Type": "multipart/form-data" }, 300 headers: { "Content-Type": "multipart/form-data" },
291 }; 301 };
292 - // 自拍图片上传七牛服务器 302 + // 自拍文件上传七牛服务器
293 let qiniuUploadUrl; 303 let qiniuUploadUrl;
294 if (window.location.protocol === 'https:') { 304 if (window.location.protocol === 'https:') {
295 - qiniuUploadUrl = 'https://up.qbox.me'; 305 + qiniuUploadUrl = 'https://up.qbox.me';
296 } else { 306 } else {
297 - qiniuUploadUrl = 'http://upload.qiniu.com'; 307 + qiniuUploadUrl = 'http://upload.qiniu.com';
298 } 308 }
299 - const { filekey, hash, image_info } = await qiniuUploadAPI( 309 + const uploadData = await qiniuUploadAPI(
300 qiniuUploadUrl, 310 qiniuUploadUrl,
301 formData, 311 formData,
302 config 312 config
303 ); 313 );
304 - if (filekey) { 314 + // 上传失败处理
305 - // 保存图片 315 + if (!uploadData) {
316 + loading.value = false;
317 + return false;
318 + }
319 + if (uploadData.filekey) {
320 + // 保存文件
306 const { data } = await saveFileAPI({ 321 const { data } = await saveFileAPI({
307 name, 322 name,
308 - filekey, 323 + filekey: uploadData.filekey,
309 hash: md5, 324 hash: md5,
310 // format: image_info.format, 325 // format: image_info.format,
311 - height: image_info.height, 326 + // height: image_info.height,
312 - width: image_info.width, 327 + // width: image_info.width,
313 }); 328 });
314 return data; 329 return data;
315 } 330 }
...@@ -317,34 +332,40 @@ const uploadQiniu = async (file, token, name, md5) => { ...@@ -317,34 +332,40 @@ const uploadQiniu = async (file, token, name, md5) => {
317 332
318 /****************** END *******************/ 333 /****************** END *******************/
319 334
335 +const show_error = ref(false);
336 +const error_msg = ref('');
337 +
320 // 校验模块 338 // 校验模块
321 const validImageUploader = () => { 339 const validImageUploader = () => {
322 - // 必填项 未上传图片 340 + // 必填项 未上传文件
323 if (props.item.component_props.required && !fileList.value.length) { 341 if (props.item.component_props.required && !fileList.value.length) {
324 - show_empty.value = true; 342 + show_error.value = true;
343 + error_msg.value = '必填项不能为空'
325 } else { 344 } else {
326 - show_empty.value = false; 345 + show_error.value = false;
346 + error_msg.value = ''
327 } 347 }
328 - return !show_empty.value; 348 + return !show_error.value;
329 }; 349 };
330 350
331 defineExpose({ validImageUploader }); 351 defineExpose({ validImageUploader });
332 </script> 352 </script>
333 353
334 -<style lang="less" scoped> 354 +<style lang="less">
335 .image-uploader-field { 355 .image-uploader-field {
336 .label { 356 .label {
337 - padding: 1rem 1rem 0 1rem; 357 + margin-left: 1rem;
338 - font-size: 0.9rem; 358 + padding-bottom: 20px;
359 + font-size: 26px;
339 font-weight: bold; 360 font-weight: bold;
340 361
341 - span { 362 + text {
342 color: red; 363 color: red;
343 } 364 }
344 } 365 }
345 366
346 .type-text { 367 .type-text {
347 - font-size: 0.9rem; 368 + font-size: 22px;
348 margin-left: 1rem; 369 margin-left: 1rem;
349 padding-bottom: 1rem; 370 padding-bottom: 1rem;
350 color: gray; 371 color: gray;
......
...@@ -8,7 +8,7 @@ import AreaPickerField from '@/components/AreaPickerField/index.vue' ...@@ -8,7 +8,7 @@ import AreaPickerField from '@/components/AreaPickerField/index.vue'
8 import DatePickerField from '@/components/DatePickerField/index.vue' 8 import DatePickerField from '@/components/DatePickerField/index.vue'
9 import TimePickerField from '@/components/TimePickerField/index.vue' 9 import TimePickerField from '@/components/TimePickerField/index.vue'
10 import DateTimePickerField from '@/components/DateTimePickerField/index.vue' 10 import DateTimePickerField from '@/components/DateTimePickerField/index.vue'
11 -// import ImageUploaderField from '@/components/ImageUploaderField/index.vue' 11 +import ImageUploaderField from '@/components/ImageUploaderField/index.vue'
12 import FileUploaderField from '@/components/FileUploaderField/index.vue' 12 import FileUploaderField from '@/components/FileUploaderField/index.vue'
13 import PhoneField from '@/components/PhoneField/index.vue' 13 import PhoneField from '@/components/PhoneField/index.vue'
14 import EmailField from '@/components/EmailField/index.vue' 14 import EmailField from '@/components/EmailField/index.vue'
...@@ -105,9 +105,9 @@ export function createComponentType(data) { ...@@ -105,9 +105,9 @@ export function createComponentType(data) {
105 if (item.component_props.tag === 'datetime') { 105 if (item.component_props.tag === 'datetime') {
106 item.component = DateTimePickerField 106 item.component = DateTimePickerField
107 } 107 }
108 - // if (item.component_props.tag === 'image_uploader') { 108 + if (item.component_props.tag === 'image_uploader') {
109 - // item.component = ImageUploaderField 109 + item.component = ImageUploaderField
110 - // } 110 + }
111 if (item.component_props.tag === 'file_uploader') { 111 if (item.component_props.tag === 'file_uploader') {
112 item.component = FileUploaderField 112 item.component = FileUploaderField
113 } 113 }
......