upload-image.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <template>
  2. <view class="uni-file-picker__container">
  3. <view class="file-picker__box" v-for="(item,index) in filesList" :key="index" :style="boxStyle">
  4. <view class="file-picker__box-content" :style="borderStyle">
  5. <image class="file-image" :src="item.url" mode="aspectFill" @click.stop="prviewImage(item,index)"></image>
  6. <view v-if="delIcon && !readonly" class="icon-del-box" @click.stop="delFile(index)">
  7. <view class="icon-del"></view>
  8. <view class="icon-del rotate"></view>
  9. </view>
  10. <view v-if="(item.progress && item.progress !== 100) ||item.progress===0 " class="file-picker__progress">
  11. <progress class="file-picker__progress-item" :percent="item.progress === -1?0:item.progress" stroke-width="4"
  12. :backgroundColor="item.errMsg?'#ff5a5f':'#EBEBEB'" />
  13. </view>
  14. <view v-if="item.errMsg" class="file-picker__mask" @click.stop="uploadFiles(item,index)">
  15. 点击重试
  16. </view>
  17. </view>
  18. </view>
  19. <view v-if="filesList.length < limit && !readonly" class="file-picker__box" :style="boxStyle">
  20. <view class="file-picker__box-content is-add" :style="borderStyle" @click="choose">
  21. <slot>
  22. <view class="icon-add"></view>
  23. <view class="icon-add rotate"></view>
  24. </slot>
  25. </view>
  26. </view>
  27. </view>
  28. </template>
  29. <script>
  30. export default {
  31. name: "uploadImage",
  32. emits:['uploadFiles','choose','delFile'],
  33. props: {
  34. filesList: {
  35. type: Array,
  36. default () {
  37. return []
  38. }
  39. },
  40. disabled:{
  41. type: Boolean,
  42. default: false
  43. },
  44. disablePreview: {
  45. type: Boolean,
  46. default: false
  47. },
  48. limit: {
  49. type: [Number, String],
  50. default: 9
  51. },
  52. imageStyles: {
  53. type: Object,
  54. default () {
  55. return {
  56. width: 'auto',
  57. height: 'auto',
  58. border: {}
  59. }
  60. }
  61. },
  62. delIcon: {
  63. type: Boolean,
  64. default: true
  65. },
  66. readonly:{
  67. type:Boolean,
  68. default:false
  69. }
  70. },
  71. computed: {
  72. styles() {
  73. let styles = {
  74. width: 'auto',
  75. height: 'auto',
  76. border: {}
  77. }
  78. return Object.assign(styles, this.imageStyles)
  79. },
  80. boxStyle() {
  81. const {
  82. width = 'auto',
  83. height = 'auto'
  84. } = this.styles
  85. let obj = {}
  86. if (height === 'auto') {
  87. if (width !== 'auto') {
  88. obj.height = this.value2px(width)
  89. obj['padding-top'] = 0
  90. } else {
  91. obj.height = 0
  92. }
  93. } else {
  94. obj.height = this.value2px(height)
  95. obj['padding-top'] = 0
  96. }
  97. if (width === 'auto') {
  98. if (height !== 'auto') {
  99. obj.width = this.value2px(height)
  100. } else {
  101. obj.width = '33.3%'
  102. }
  103. } else {
  104. obj.width = this.value2px(width)
  105. }
  106. let classles = ''
  107. for(let i in obj){
  108. classles+= `${i}:${obj[i]};`
  109. }
  110. return classles
  111. },
  112. borderStyle() {
  113. let {
  114. border
  115. } = this.styles
  116. let obj = {}
  117. if (typeof border === 'boolean') {
  118. obj.border = border ? '1px #eee solid' : 'none'
  119. } else {
  120. let width = (border && border.width) || 1
  121. width = this.value2px(width)
  122. let radius = (border && border.radius) || 5
  123. radius = this.value2px(radius)
  124. obj = {
  125. 'border-width': width,
  126. 'border-style': (border && border.style) || 'solid',
  127. 'border-color': (border && border.color) || '#eee',
  128. 'border-radius': radius
  129. }
  130. }
  131. let classles = ''
  132. for(let i in obj){
  133. classles+= `${i}:${obj[i]};`
  134. }
  135. return classles
  136. }
  137. },
  138. methods: {
  139. uploadFiles(item, index) {
  140. this.$emit("uploadFiles", item)
  141. },
  142. choose() {
  143. this.$emit("choose")
  144. },
  145. delFile(index) {
  146. this.$emit('delFile', index)
  147. },
  148. prviewImage(img, index) {
  149. let urls = []
  150. if(Number(this.limit) === 1&&this.disablePreview&&!this.disabled){
  151. this.$emit("choose")
  152. }
  153. if(this.disablePreview) return
  154. this.filesList.forEach(i => {
  155. urls.push(i.url)
  156. })
  157. uni.previewImage({
  158. urls: urls,
  159. current: index
  160. });
  161. },
  162. value2px(value) {
  163. if (typeof value === 'number') {
  164. value += 'px'
  165. } else {
  166. if (value.indexOf('%') === -1) {
  167. value = value.indexOf('px') !== -1 ? value : value + 'px'
  168. }
  169. }
  170. return value
  171. }
  172. }
  173. }
  174. </script>
  175. <style lang="scss">
  176. .uni-file-picker__container {
  177. /* #ifndef APP-NVUE */
  178. display: flex;
  179. box-sizing: border-box;
  180. /* #endif */
  181. flex-wrap: wrap;
  182. margin: -5px;
  183. }
  184. .file-picker__box {
  185. position: relative;
  186. // flex: 0 0 33.3%;
  187. width: 33.3%;
  188. height: 0;
  189. padding-top: 33.33%;
  190. /* #ifndef APP-NVUE */
  191. box-sizing: border-box;
  192. /* #endif */
  193. }
  194. .file-picker__box-content {
  195. position: absolute;
  196. top: 0;
  197. right: 0;
  198. bottom: 0;
  199. left: 0;
  200. margin: 5px;
  201. border: 1px #eee solid;
  202. border-radius: 8px;
  203. overflow: hidden;
  204. }
  205. .file-picker__progress {
  206. position: absolute;
  207. bottom: 0;
  208. left: 0;
  209. right: 0;
  210. /* border: 1px red solid; */
  211. z-index: 2;
  212. }
  213. .file-picker__progress-item {
  214. width: 100%;
  215. }
  216. .file-picker__mask {
  217. /* #ifndef APP-NVUE */
  218. display: flex;
  219. /* #endif */
  220. justify-content: center;
  221. align-items: center;
  222. position: absolute;
  223. right: 0;
  224. top: 0;
  225. bottom: 0;
  226. left: 0;
  227. color: #fff;
  228. font-size: 12px;
  229. background-color: rgba(0, 0, 0, 0.4);
  230. }
  231. .file-image {
  232. width: 100%;
  233. height: 100%;
  234. }
  235. .is-add {
  236. /* #ifndef APP-NVUE */
  237. display: flex;
  238. /* #endif */
  239. align-items: center;
  240. justify-content: center;
  241. }
  242. .icon-add {
  243. width: 50px;
  244. height: 5px;
  245. background-color: #f1f1f1;
  246. border-radius: 2px;
  247. }
  248. .rotate {
  249. position: absolute;
  250. transform: rotate(90deg);
  251. }
  252. .icon-del-box {
  253. /* #ifndef APP-NVUE */
  254. display: flex;
  255. /* #endif */
  256. align-items: center;
  257. justify-content: center;
  258. position: absolute;
  259. top: 5px;
  260. right: 5px;
  261. height: 26px;
  262. width: 26px;
  263. border-radius: 50%;
  264. background-color: rgba(0, 0, 0, 0.5);
  265. z-index: 2;
  266. transform: rotate(-45deg);
  267. }
  268. .icon-del {
  269. width: 15px;
  270. height: 2px;
  271. background-color: #fff;
  272. border-radius: 2px;
  273. }
  274. </style>