upload-image.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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. const widthDefaultValue = 1
  118. const radiusDefaultValue = 3
  119. if (typeof border === 'boolean') {
  120. obj.border = border ? '1px #eee solid' : 'none'
  121. } else {
  122. let width = (border && border.width) || widthDefaultValue
  123. width = this.value2px(width)
  124. let radius = (border && border.radius) || radiusDefaultValue
  125. radius = this.value2px(radius)
  126. obj = {
  127. 'border-width': width,
  128. 'border-style': (border && border.style) || 'solid',
  129. 'border-color': (border && border.color) || '#eee',
  130. 'border-radius': radius
  131. }
  132. }
  133. let classles = ''
  134. for(let i in obj){
  135. classles+= `${i}:${obj[i]};`
  136. }
  137. return classles
  138. }
  139. },
  140. methods: {
  141. uploadFiles(item, index) {
  142. this.$emit("uploadFiles", item)
  143. },
  144. choose() {
  145. this.$emit("choose")
  146. },
  147. delFile(index) {
  148. this.$emit('delFile', index)
  149. },
  150. prviewImage(img, index) {
  151. let urls = []
  152. if(Number(this.limit) === 1&&this.disablePreview&&!this.disabled){
  153. this.$emit("choose")
  154. }
  155. if(this.disablePreview) return
  156. this.filesList.forEach(i => {
  157. urls.push(i.url)
  158. })
  159. uni.previewImage({
  160. urls: urls,
  161. current: index
  162. });
  163. },
  164. value2px(value) {
  165. if (typeof value === 'number') {
  166. value += 'px'
  167. } else {
  168. if (value.indexOf('%') === -1) {
  169. value = value.indexOf('px') !== -1 ? value : value + 'px'
  170. }
  171. }
  172. return value
  173. }
  174. }
  175. }
  176. </script>
  177. <style lang="scss">
  178. .uni-file-picker__container {
  179. /* #ifndef APP-NVUE */
  180. display: flex;
  181. flex-direction: row;
  182. box-sizing: border-box;
  183. /* #endif */
  184. flex-wrap: wrap;
  185. margin: -5px;
  186. }
  187. .file-picker__box {
  188. position: relative;
  189. // flex: 0 0 33.3%;
  190. width: 33.3%;
  191. height: 0;
  192. padding-top: 33.33%;
  193. /* #ifndef APP-NVUE */
  194. box-sizing: border-box;
  195. /* #endif */
  196. }
  197. .file-picker__box-content {
  198. position: absolute;
  199. top: 0;
  200. right: 0;
  201. bottom: 0;
  202. left: 0;
  203. margin: 5px;
  204. border: 1px #eee solid;
  205. border-radius: 5px;
  206. overflow: hidden;
  207. }
  208. .file-picker__progress {
  209. position: absolute;
  210. bottom: 0;
  211. left: 0;
  212. right: 0;
  213. /* border: 1px red solid; */
  214. z-index: 2;
  215. }
  216. .file-picker__progress-item {
  217. width: 100%;
  218. }
  219. .file-picker__mask {
  220. /* #ifndef APP-NVUE */
  221. display: flex;
  222. /* #endif */
  223. justify-content: center;
  224. align-items: center;
  225. position: absolute;
  226. right: 0;
  227. top: 0;
  228. bottom: 0;
  229. left: 0;
  230. color: #fff;
  231. font-size: 12px;
  232. background-color: rgba(0, 0, 0, 0.4);
  233. }
  234. .file-image {
  235. width: 100%;
  236. height: 100%;
  237. }
  238. .is-add {
  239. /* #ifndef APP-NVUE */
  240. display: flex;
  241. /* #endif */
  242. align-items: center;
  243. justify-content: center;
  244. }
  245. .icon-add {
  246. width: 50px;
  247. height: 5px;
  248. background-color: #f1f1f1;
  249. border-radius: 2px;
  250. }
  251. .rotate {
  252. position: absolute;
  253. transform: rotate(90deg);
  254. }
  255. .icon-del-box {
  256. /* #ifndef APP-NVUE */
  257. display: flex;
  258. /* #endif */
  259. align-items: center;
  260. justify-content: center;
  261. position: absolute;
  262. top: 3px;
  263. right: 3px;
  264. height: 26px;
  265. width: 26px;
  266. border-radius: 50%;
  267. background-color: rgba(0, 0, 0, 0.5);
  268. z-index: 2;
  269. transform: rotate(-45deg);
  270. }
  271. .icon-del {
  272. width: 15px;
  273. height: 2px;
  274. background-color: #fff;
  275. border-radius: 2px;
  276. }
  277. </style>