lsj-upload.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. <template>
  2. <view class="lsj-file" :style="[getStyles]">
  3. <view ref="lsj" class="hFile" :style="[getStyles]" @click="onClick">
  4. <slot><view class="defview" :style="[getStyles]">附件上传</view></slot>
  5. </view>
  6. </view>
  7. </template>
  8. <script>
  9. // 查看文档:https://ext.dcloud.net.cn/plugin?id=5459
  10. import {LsjFile} from './LsjFile.js'
  11. export default {
  12. name: 'Lsj-upload',
  13. props: {
  14. // 打印日志
  15. debug: {type: Boolean,default: false},
  16. // 是否去重文件(同名文件覆盖)
  17. distinct: {type: Boolean,default: false},
  18. // 自动上传
  19. instantly: {type: Boolean,default: false},
  20. // 上传接口参数设置
  21. option: {type: Object,default: ()=>{}},
  22. // 文件大小上限
  23. size: { type: Number, default: 10 },
  24. // 文件选择个数上限,超出后不触发点击
  25. count: { type: Number, default: 9 },
  26. // 是否允许多选文件
  27. multiple: {type:Boolean, default: true},
  28. // 允许上传的文件格式(多个以逗号隔开)
  29. formats: { type: String, default:''},
  30. // input file选择限制
  31. accept: {type: String,default: ''},
  32. // 微信选择文件类型
  33. //all=从所有文件选择,
  34. //video=只能选择视频文件,
  35. //image=只能选择图片文件,
  36. //file=可以选择除了图片和视频之外的其它的文件
  37. wxFileType: { type: String, default: 'all' },
  38. // webviewID需唯一,不同窗口也不要同Id
  39. childId: { type: String, default: 'lsjUpload' },
  40. // 文件选择触发面宽度
  41. width: { type: String, default: '100%' },
  42. // 文件选择触发面高度
  43. height: { type: String, default: '80rpx' },
  44. // top,left,bottom,right仅position=absolute时才需要传入
  45. top: { type: [String, Number], default: '' },
  46. left: { type: [String, Number], default: '' },
  47. bottom: { type: [String, Number], default: '' },
  48. right: { type: [String, Number], default: '' },
  49. // nvue不支持跟随窗口滚动
  50. position: {
  51. type: String,
  52. // #ifdef APP-NVUE
  53. default: 'absolute',
  54. // #endif
  55. // #ifndef APP-NVUE
  56. default: 'static',
  57. // #endif
  58. },
  59. },
  60. data() {
  61. return {
  62. }
  63. },
  64. computed: {
  65. getStyles() {
  66. let styles = {
  67. width: this.width,
  68. height: this.height
  69. }
  70. if (this.position == 'absolute') {
  71. styles['top'] = this.top
  72. styles['bottom'] = this.bottom
  73. styles['left'] = this.left
  74. styles['right'] = this.right
  75. styles['position'] = 'fixed'
  76. }
  77. return styles
  78. }
  79. },
  80. watch: {
  81. option(v) {
  82. // #ifdef APP-PLUS
  83. this.lsjFile&&this.show();
  84. // #endif
  85. }
  86. },
  87. updated() {
  88. // #ifdef APP-PLUS
  89. if (this.isShow) {
  90. this.lsjFile&&this.show();
  91. }
  92. // #endif
  93. },
  94. created() {
  95. uni.$on('$upload-show',this.emitShow);
  96. uni.$on('$upload-hide',this.hide);
  97. },
  98. beforeDestroy() {
  99. uni.$off('$upload-show',this.emitShow);
  100. uni.$off('$upload-hide',this.hide);
  101. // #ifdef APP-PLUS
  102. this.lsjFile.dom.close();
  103. // #endif
  104. },
  105. mounted() {
  106. let pages = getCurrentPages();
  107. this.myRoute = pages[pages.length - 1].route;
  108. this._size = 0;
  109. let WEBID = 'lsj_' + this.childId + new Date().getTime();
  110. this.lsjFile = new LsjFile({
  111. id: WEBID,
  112. debug: this.debug,
  113. width: this.width,
  114. height: this.height,
  115. option: this.option,
  116. instantly: this.instantly,
  117. // 限制条件
  118. prohibited: {
  119. // 是否去重
  120. distinct: this.distinct,
  121. // 大小
  122. size: this.size,
  123. // 允许上传的格式
  124. formats: this.formats,
  125. // 限制选择的格式
  126. accept: this.accept,
  127. count: this.count,
  128. // 是否多选
  129. multiple: this.multiple,
  130. },
  131. onchange: this.onchange,
  132. onprogress: this.onprogress,
  133. });
  134. this.create();
  135. },
  136. methods: {
  137. setFiles(array) {
  138. if (array instanceof Map) {
  139. for (let [key, item] of array) {
  140. item['progress'] = 100;
  141. item['type'] = 'success';
  142. this.lsjFile.files.set(key,item);
  143. }
  144. }
  145. else if (Array.isArray(array)) {
  146. array.forEach(item=>{
  147. if (item.name) {
  148. item['progress'] = 100;
  149. item['type'] = 'success';
  150. this.lsjFile.files.set(item.name,item);
  151. }
  152. });
  153. }
  154. this.onchange(this.lsjFile.files);
  155. },
  156. setData() {
  157. this.lsjFile&&this.lsjFile.setData(...arguments);
  158. },
  159. getDomStyles(callback) {
  160. // #ifndef APP-NVUE
  161. let view = uni
  162. .createSelectorQuery()
  163. .in(this)
  164. .select('.lsj-file')
  165. view.fields(
  166. {
  167. size: true,
  168. rect: true
  169. },
  170. ({ height, width, top, left, right, bottom }) => {
  171. uni.createSelectorQuery()
  172. .selectViewport()
  173. .scrollOffset(({ scrollTop }) => {
  174. return callback({
  175. top: parseInt(top) + parseInt(scrollTop) + 'px',
  176. left: parseInt(left) + 'px',
  177. width: parseInt(width) + 'px',
  178. height: parseInt(height) + 'px'
  179. })
  180. })
  181. .exec()
  182. }
  183. ).exec()
  184. // #endif
  185. // #ifdef APP-NVUE
  186. const dom = weex.requireModule('dom')
  187. dom.getComponentRect(this.$refs.lsj, ({ size: { height, width, top, left, right, bottom } }) => {
  188. return callback({
  189. top: parseInt(top) + 'px',
  190. left: parseInt(left) + 'px',
  191. width: parseInt(width) + 'px',
  192. height: parseInt(height) + 'px',
  193. right: parseInt(right) + 'px',
  194. bottom: parseInt(bottom) + 'px'
  195. })
  196. })
  197. // #endif
  198. },
  199. emitShow() {
  200. let pages = getCurrentPages();
  201. let route = pages[pages.length - 1].route;
  202. if (route === this.myRoute) {
  203. return this.show();
  204. }
  205. },
  206. show() {
  207. this.debug&&console.log('触发show函数');
  208. if (this._size && (this._size >= this.count)) {
  209. return;
  210. }
  211. this.isShow = true;
  212. // #ifdef APP-PLUS
  213. this.lsjFile&&this.getDomStyles(styles => {
  214. this.lsjFile.dom.setStyle(styles)
  215. });
  216. // #endif
  217. // #ifdef H5
  218. this.lsjFile.dom.style.display = 'inline'
  219. // #endif
  220. },
  221. hide() {
  222. this.debug&&console.log('触发hide函数');
  223. this.isShow = false;
  224. // #ifdef APP-PLUS
  225. this.lsjFile&&this.lsjFile.dom.setStyle({
  226. top: '-100px',
  227. left:'0px',
  228. width: '1px',
  229. height: '100px',
  230. });
  231. // #endif
  232. // #ifdef H5
  233. this.lsjFile.dom.style.display = 'none'
  234. // #endif
  235. },
  236. /**
  237. * 手动提交上传
  238. * @param {string}name 文件名称,不传则上传所有type等于waiting和fail的文件
  239. */
  240. upload(name) {
  241. this.lsjFile&&this.lsjFile.upload(name);
  242. },
  243. /**
  244. * @returns {Map} 已选择的文件Map集
  245. */
  246. onchange(files) {
  247. this.$emit('change',files);
  248. this._size = files.size;
  249. return files.size >= this.count ? this.hide() : this.show();
  250. },
  251. /**
  252. * @returns {object} 当前上传中的对象
  253. */
  254. onprogress(item,end=false) {
  255. this.$emit('progress',item);
  256. if (end) {
  257. setTimeout(()=>{
  258. this.$emit('uploadEnd',item);
  259. },0);
  260. }
  261. },
  262. /**
  263. * 移除组件内缓存的某条数据
  264. * @param {string}name 文件名称,不指定默认清除所有文件
  265. */
  266. clear(name) {
  267. this.lsjFile.clear(name);
  268. },
  269. // 创建选择器
  270. create() {
  271. // 若iOS端服务端处理不了跨域就将hybrid目录内的html放到服务端去,并将此处path改成服务器上的地址
  272. let path = '/uni_modules/lsj-upload/hybrid/html/uploadFile.html';
  273. let dom = this.lsjFile.create(path);
  274. // #ifdef H5
  275. this.$refs.lsj.$el.appendChild(dom);
  276. // #endif
  277. // #ifndef APP-PLUS
  278. this.show();
  279. // #endif
  280. // #ifdef APP-PLUS
  281. dom.setStyle({position: this.position});
  282. dom.loadURL(path);
  283. setTimeout(()=>{
  284. // #ifdef APP-NVUE
  285. plus.webview.currentWebview().append(dom);
  286. // #endif
  287. // #ifndef APP-NVUE
  288. this.$root.$scope.$getAppWebview().append(dom);
  289. // #endif
  290. this.show();
  291. },300)
  292. // #endif
  293. },
  294. // 点击选择附件
  295. onClick() {
  296. if (this._size >= this.count) {
  297. this.toast(`只允许上传${this.count}个文件`);
  298. return;
  299. }
  300. // #ifdef MP-WEIXIN
  301. if (!this.isShow) {return;}
  302. let count = this.count - this._size;
  303. this.lsjFile.chooseMessageFile(this.wxFileType,count);
  304. // #endif
  305. },
  306. toast(msg) {
  307. uni.showToast({
  308. title: msg,
  309. icon: 'none'
  310. });
  311. }
  312. }
  313. }
  314. </script>
  315. <style scoped>
  316. .lsj-file {
  317. display: inline-block;
  318. }
  319. .defview {
  320. background-color: #007aff;
  321. color: #fff;
  322. border-radius: 10rpx;
  323. display: flex;
  324. align-items: center;
  325. justify-content: center;
  326. font-size: 28rpx;
  327. }
  328. .hFile {
  329. position: relative;
  330. overflow: hidden;
  331. }
  332. </style>