upload-image.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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. box-sizing: border-box;
  182. /* #endif */
  183. flex-wrap: wrap;
  184. margin: -5px;
  185. }
  186. .file-picker__box {
  187. position: relative;
  188. // flex: 0 0 33.3%;
  189. width: 33.3%;
  190. height: 0;
  191. padding-top: 33.33%;
  192. /* #ifndef APP-NVUE */
  193. box-sizing: border-box;
  194. /* #endif */
  195. }
  196. .file-picker__box-content {
  197. position: absolute;
  198. top: 0;
  199. right: 0;
  200. bottom: 0;
  201. left: 0;
  202. margin: 5px;
  203. border: 1px #eee solid;
  204. border-radius: 5px;
  205. overflow: hidden;
  206. }
  207. .file-picker__progress {
  208. position: absolute;
  209. bottom: 0;
  210. left: 0;
  211. right: 0;
  212. /* border: 1px red solid; */
  213. z-index: 2;
  214. }
  215. .file-picker__progress-item {
  216. width: 100%;
  217. }
  218. .file-picker__mask {
  219. /* #ifndef APP-NVUE */
  220. display: flex;
  221. /* #endif */
  222. justify-content: center;
  223. align-items: center;
  224. position: absolute;
  225. right: 0;
  226. top: 0;
  227. bottom: 0;
  228. left: 0;
  229. color: #fff;
  230. font-size: 12px;
  231. background-color: rgba(0, 0, 0, 0.4);
  232. }
  233. .file-image {
  234. width: 100%;
  235. height: 100%;
  236. }
  237. .is-add {
  238. /* #ifndef APP-NVUE */
  239. display: flex;
  240. /* #endif */
  241. align-items: center;
  242. justify-content: center;
  243. }
  244. .icon-add {
  245. width: 50px;
  246. height: 5px;
  247. background-color: #f1f1f1;
  248. border-radius: 2px;
  249. }
  250. .rotate {
  251. position: absolute;
  252. transform: rotate(90deg);
  253. }
  254. .icon-del-box {
  255. /* #ifndef APP-NVUE */
  256. display: flex;
  257. /* #endif */
  258. align-items: center;
  259. justify-content: center;
  260. position: absolute;
  261. top: 3px;
  262. right: 3px;
  263. height: 26px;
  264. width: 26px;
  265. border-radius: 50%;
  266. background-color: rgba(0, 0, 0, 0.5);
  267. z-index: 2;
  268. transform: rotate(-45deg);
  269. }
  270. .icon-del {
  271. width: 15px;
  272. height: 2px;
  273. background-color: #fff;
  274. border-radius: 2px;
  275. }
  276. </style>