My_upload.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <template>
  2. <view v-if="custom">
  3. <up-upload @after-read="afterRead" :deletable="!disabled" @delete="deletePic"
  4. :max-count="disabled ? fileList.length : maxCount" :accept="accept" multiple @clickPreview="clickPreview">
  5. <slot />
  6. </up-upload>
  7. </view>
  8. <up-upload v-else :fileList="fileList" @after-read="afterRead" :deletable="!disabled" @delete="deletePic"
  9. :max-count="disabled ? fileList.length : maxCount" :accept="accept" multiple @clickPreview="clickPreview" />
  10. </template>
  11. <script setup>
  12. import { ref, reactive, defineProps, defineEmits, getCurrentInstance, onUnmounted } from 'vue'
  13. const emit = defineEmits(['uploadCallback', 'startUploading'])
  14. const props = defineProps({
  15. accept: {
  16. type: String,
  17. default: "image"
  18. },
  19. maxCount: {
  20. type: [String, Number],
  21. default: 99
  22. },
  23. uploadCallback: {
  24. type: Function
  25. },
  26. startUploading: {
  27. type: Function
  28. },
  29. fileList: {
  30. type: Array,
  31. default: reactive([])
  32. },
  33. usetype: {
  34. type: String,
  35. default: 'default'
  36. },
  37. ownertable: {
  38. type: String,
  39. default: 'temporary'
  40. },
  41. ownerid: {
  42. type: [String, Number],
  43. default: 1
  44. },
  45. disabled: {
  46. type: Boolean,
  47. default: false
  48. },
  49. custom: {
  50. type: Boolean,
  51. default: false
  52. }
  53. })
  54. const { $Http } = getCurrentInstance().proxy;
  55. const deleteList = reactive([]); // 用于存储待删除的文件列表
  56. const clickPreview = (e) => {
  57. uni.previewImage({
  58. urls: props.fileList.map(v => v.url),
  59. current: e.url,
  60. loop: true,
  61. })
  62. }
  63. // 文件读取后处理
  64. const afterRead = ({ file }) => {
  65. emit('startUploading', file);
  66. file.forEach(v => {
  67. // #ifdef H5
  68. getArrayBuffer(v).then(data => {
  69. data.data.url = v.url;
  70. console.log("data.data", data.data)
  71. handleUploadFile(requestType(v), data.data)
  72. props.fileList.push({
  73. ...v,
  74. status: 'uploading',
  75. message: '上传中',
  76. });
  77. })
  78. // #endif
  79. // #ifndef H5
  80. uni.getFileSystemManager().readFile({
  81. filePath: v.url,
  82. success: data => {
  83. data.data.url = v.url;
  84. handleUploadFile(requestType(v), data.data)
  85. props.fileList.push({
  86. ...v,
  87. status: 'uploading',
  88. message: '上传中',
  89. });
  90. },
  91. fail: console.error
  92. })
  93. // #endif
  94. })
  95. }
  96. // 获取文件类型信息
  97. const requestType = (file) => {
  98. let ext = ''
  99. // #ifdef H5
  100. ext = file.name.substring(file.name.lastIndexOf(".") + 1)
  101. // #endif
  102. // #ifndef H5
  103. ext = file.type?.split("/")[1] ||
  104. file.url.substring(file.url.lastIndexOf(".") + 1) ||
  105. file.name.substring(file.name.lastIndexOf(".") + 1)
  106. // #endif
  107. return {
  108. "classname": "system.attachment.huawei.OBS",
  109. "method": "getFileName",
  110. "content": {
  111. "filename": `${Date.now() + file.size}.${ext}`,
  112. "filetype": ext,
  113. "parentid": uni.getStorageSync('siteP').appfolderid
  114. }
  115. }
  116. }
  117. // 处理文件上传
  118. const handleUploadFile = (file, data) => {
  119. $Http.basic(file).then(res => {
  120. console.log("上传文件成功", res)
  121. if (res.msg == "成功") {
  122. uploadFile(res.data, data)
  123. } else {
  124. uni.showToast({
  125. title: `${file.content.filename}上传失败`,
  126. icon: "none"
  127. })
  128. }
  129. })
  130. }
  131. // 获取ArrayBuffer (H5专用)
  132. const getArrayBuffer = (file) => {
  133. return new Promise((resolve, reject) => {
  134. const xhr = new XMLHttpRequest()
  135. xhr.open('GET', file.url, true)
  136. xhr.responseType = 'blob'
  137. xhr.onload = function () {
  138. if (this.status === 200) {
  139. const myBlob = this.response
  140. const files = new File(
  141. [myBlob],
  142. file.name,
  143. { type: file.type },
  144. )
  145. const reader = new FileReader()
  146. reader.readAsArrayBuffer(files)
  147. reader.onload = () => resolve({
  148. data: reader.result
  149. })
  150. reader.onerror = error => reject(error)
  151. } else {
  152. reject(`文件加载失败: ${this.status}`)
  153. }
  154. }
  155. xhr.onerror = () => reject('网络请求失败')
  156. xhr.send()
  157. })
  158. }
  159. // 上传文件到服务器
  160. const uploadFile = (res, data) => {
  161. uni.request({
  162. url: res.uploadurl,
  163. method: "PUT",
  164. data,
  165. header: { 'Content-Type': 'application/octet-stream' },
  166. success: () => {
  167. $Http.basic({
  168. "classname": "system.attachment.huawei.OBS",
  169. "method": "uploadSuccess",
  170. "content": { "serialfilename": res.serialfilename }
  171. }).then(s => {
  172. console.log("文件上传反馈", s)
  173. handleFileLink([{
  174. attachmentid: s.data.attachmentids[0],
  175. url: data.url
  176. }], "temporary", 1, props.usetype)
  177. }).catch(console.error)
  178. },
  179. fail: console.error
  180. })
  181. }
  182. function handleFileLink(list, ownertable = "temporary", ownerid = 1, usetype = 'default', resolve = () => { }) {
  183. if (list.length == 0) return resolve(true);
  184. let content = {
  185. ownertable,
  186. ownerid,
  187. usetype,
  188. attachmentids: list.map(v => v.attachmentid),
  189. siteid: uni.getStorageSync("userMsg").siteid
  190. }
  191. $Http.basic({
  192. "classname": "system.attachment.Attachment",
  193. "method": "createFileLink",
  194. content
  195. }).then(res => {
  196. console.log('跟进记录绑定附件', res)
  197. resolve(res.code == '1')
  198. if (res.code != '1') return uni.showToast({
  199. title: res.msg,
  200. icon: "none"
  201. })
  202. list.forEach(v => {
  203. const file = props.fileList.find(s => v.url === s.url);
  204. delete file.status;
  205. delete file.message;
  206. if (file) {
  207. Object.assign(file, res.data.find(s => s.attachmentid === v.attachmentid));
  208. }
  209. });
  210. emit('uploadCallback', { fileList: props.fileList, attachmentids: content.attachmentids })
  211. })
  212. }
  213. // 保存所有的附件绑定到表上,有在上传的文件不能保存
  214. const isUploading = (showToast = true) => {
  215. let res = props.fileList.some(file => file.status === 'uploading');
  216. if (res && showToast) uni.showToast({
  217. title: '文件正在上传中,请稍后再试',
  218. icon: 'none'
  219. });
  220. return res
  221. }
  222. // 保存接口 接受数据调用handleFileLink
  223. const saveFileLinks = (ownertable, ownerid, usetype = 'default') => {
  224. // 如果有待删除的文件,先删除
  225. deleteList.length && deleteFile(deleteList);
  226. return new Promise((resolve, reject) => {
  227. const list = props.fileList;
  228. console.log("list", list)
  229. if (list.length) {
  230. return handleFileLink(list, ownertable, ownerid, usetype, resolve);
  231. } else {
  232. resolve(true)
  233. }
  234. })
  235. }
  236. function deletePic({ file, index, name }) {
  237. uni.showModal({
  238. cancelText: '取消',
  239. confirmText: '删除',
  240. content: '是否确定删除该文件?',
  241. title: '提示',
  242. success: ({ confirm }) => {
  243. if (confirm) {
  244. console.log("删除文件", file);
  245. if (file.ownertable == 'temporary') {
  246. // 临时文件直接删除
  247. deleteFile([file]).then(res => {
  248. if (res) {
  249. props.fileList.splice(index, 1);
  250. emit('uploadCallback', { fileList: props.fileList });
  251. }
  252. })
  253. } else {
  254. deleteList.push(file)
  255. props.fileList.splice(index, 1);
  256. emit('uploadCallback', { fileList: props.fileList })
  257. }
  258. }
  259. },
  260. })
  261. }
  262. // 直接删除文件
  263. const deleteFile = (arr) => {
  264. return new Promise((resolve, reject) => {
  265. let list = arr.filter(file => file.linksid);
  266. if (list.length) {
  267. $Http.basic({
  268. "classname": "system.attachment.Attachment",
  269. "method": "deleteFileLink",
  270. "content": {
  271. linksids: list.map(v => v.linksid),
  272. }
  273. }).then(res => {
  274. console.log("删除文件", res);
  275. resolve(res.code == 1)
  276. if (res.code != 1) uni.showToast({
  277. title: res.msg,
  278. icon: "none"
  279. })
  280. })
  281. } else {
  282. resolve(true)
  283. }
  284. })
  285. }
  286. // 清空临时文件 ownertable == 'temporary'
  287. const clearTemporaryFiles = (arr = props.fileList) => {
  288. let list = arr.filter(file => file.ownertable == 'temporary' && file.linksid);
  289. if (list.length) $Http.basic({
  290. "classname": "system.attachment.Attachment",
  291. "method": "deleteFileLink",
  292. "content": {
  293. linksids: list.map(v => v.linksid),
  294. }
  295. }).then(res => {
  296. console.log("清空临时文件", res);
  297. })
  298. }
  299. // 在页面销毁的时候 自动清空所有的临时文件
  300. onUnmounted(() => {
  301. clearTemporaryFiles();
  302. })
  303. defineExpose({ isUploading, handleFileLink, saveFileLinks, deleteFile })
  304. </script>