My_upload.vue 9.0 KB

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