├── .eslintignore ├── .gitignore ├── postcss.config.js ├── src ├── assets │ ├── demo.png │ ├── vip-logo.png │ └── screenshot.gif ├── style │ ├── fonts │ │ ├── iconfont.eot │ │ ├── iconfont.ttf │ │ └── iconfont.svg │ ├── iconfont.scss │ └── index.scss ├── index.js ├── ajax.js └── index.vue ├── example ├── index.js ├── components │ └── Hello.vue └── App.vue ├── bili.config.js ├── .eslintrc.js ├── .release-it.json ├── package.json ├── README-CN.MD └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | *.scss -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | *.log 4 | .idea/ 5 | dist 6 | package-lock.json -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynecz/vue-img-inputer/HEAD/src/assets/demo.png -------------------------------------------------------------------------------- /src/assets/vip-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynecz/vue-img-inputer/HEAD/src/assets/vip-logo.png -------------------------------------------------------------------------------- /src/assets/screenshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynecz/vue-img-inputer/HEAD/src/assets/screenshot.gif -------------------------------------------------------------------------------- /src/style/fonts/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynecz/vue-img-inputer/HEAD/src/style/fonts/iconfont.eot -------------------------------------------------------------------------------- /src/style/fonts/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynecz/vue-img-inputer/HEAD/src/style/fonts/iconfont.ttf -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import ImgInputer from './index.vue' 2 | import './style/index.scss' 3 | 4 | export default ImgInputer 5 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue/dist/vue.esm' 2 | import App from './App.vue' 3 | import imgInputer from '../src/index.js' 4 | 5 | Vue.component('ImgInputer', imgInputer) 6 | 7 | /* eslint-disable no-new */ 8 | new Vue({ 9 | el: '#app', 10 | template: '', 11 | components: { App } 12 | }) 13 | -------------------------------------------------------------------------------- /bili.config.js: -------------------------------------------------------------------------------- 1 | const copy = require('rollup-plugin-copied') 2 | 3 | module.exports = { 4 | plugins: [ 5 | 'vue', 6 | 7 | copy([ 8 | { 9 | from: './src/style/fonts', 10 | to: './dist/fonts', 11 | emitFiles: true 12 | } 13 | ]) 14 | ], 15 | 16 | format: ['cjs', 'umd', 'es'], 17 | filename: 'index[suffix].js' 18 | } 19 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 5 | extends: 'standard', 6 | // required to lint *.vue files 7 | plugins: ['vue'], 8 | // add your custom rules here 9 | rules: { 10 | // allow async-await 11 | 'space-before-function-paren': 'off', 12 | 'generator-star-spacing': 'off', 13 | 'no-undef': 'off' 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/components/Hello.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 43 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "preReleaseId": null, 3 | "pkgFiles": ["package.json"], 4 | "use": "git.tag", 5 | "scripts": { 6 | "beforeStart": null, 7 | "beforeBump": null, 8 | "afterBump": null, 9 | "beforeStage": null, 10 | "changelog": "git log --pretty=format:\"* %s (%h)\" [REV_RANGE]", 11 | "afterRelease": null 12 | }, 13 | "git": { 14 | "requireCleanWorkingDir": false, 15 | "requireUpstream": false, 16 | "addUntrackedFiles": false, 17 | "commit": true, 18 | "commitMessage": "build(release): ${version}", 19 | "commitArgs": "", 20 | "tag": true, 21 | "tagName": "${version}", 22 | "tagAnnotation": "Release ${version}", 23 | "tagArgs": "", 24 | "push": true, 25 | "pushArgs": "", 26 | "pushRepo": "origin" 27 | }, 28 | "prompt": { 29 | "commit": true, 30 | "tag": true, 31 | "push": true, 32 | "ghRelease": true, 33 | "glRelease": false, 34 | "publish": true 35 | } 36 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-img-inputer", 3 | "version": "2.1.6", 4 | "author": "waynecz <451578533@qq.com>", 5 | "license": "MIT", 6 | "description": "A graceful image type inputer / uploader", 7 | "main": "dist/index.js", 8 | "module": "dist/index.es.js", 9 | "scripts": { 10 | "dev": "npx poi example/index", 11 | "release": "npx release-it", 12 | "build:demo": "npx poi build", 13 | "build": "rm -rf ./dist && npx bili" 14 | }, 15 | "files": [ 16 | "dist/*" 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/waynecz/vue-img-inputer" 21 | }, 22 | "keywords": [ 23 | "file-input", 24 | "image", 25 | "vue", 26 | "components", 27 | "ui" 28 | ], 29 | "bugs": { 30 | "url": "https://github.com/waynecz/vue-img-inputer/issues" 31 | }, 32 | "peerDependencies": { 33 | "vue": "^2.5.2" 34 | }, 35 | "devDependencies": { 36 | "autoprefixer": "^9.4.2", 37 | "babel-eslint": "^8.2.3", 38 | "bili": "^3.1.2", 39 | "eslint-config-standard": "^11.0.0", 40 | "eslint-plugin-import": "^2.12.0", 41 | "eslint-plugin-node": "^6.0.1", 42 | "eslint-plugin-promise": "^3.8.0", 43 | "eslint-plugin-standard": "^3.1.0", 44 | "eslint-plugin-vue": "^4.5.0", 45 | "node-sass": "^4.9.0", 46 | "poi": "^10.2.7", 47 | "release-it": "^9.8.1", 48 | "rollup-plugin-copied": "^0.0.2", 49 | "rollup-plugin-eslint": "^4.0.0", 50 | "rollup-plugin-vue": "^4.2.0", 51 | "sass-loader": "^7.1.0", 52 | "vue": "^2.5.17", 53 | "vue-template-compiler": "^2.5.16" 54 | }, 55 | "engines": { 56 | "node": ">= 8.0.0", 57 | "npm": ">= 6.0.0" 58 | }, 59 | "browserslist": [ 60 | "> 1%", 61 | "last 2 versions", 62 | "not ie <= 9" 63 | ] 64 | } 65 | -------------------------------------------------------------------------------- /src/ajax.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Code copied from https://github.com/ElemeFE/element/blob/dev/packages/upload/src/ajax.js 3 | */ 4 | 5 | function getError(action, option, xhr) { 6 | let msg 7 | if (xhr.response) { 8 | msg = `${xhr.response.error || xhr.response}` 9 | } else if (xhr.responseText) { 10 | msg = `${xhr.responseText}` 11 | } else { 12 | msg = `fail to post ${action} ${xhr.status}` 13 | } 14 | 15 | const err = new Error(msg) 16 | err.status = xhr.status 17 | err.method = 'post' 18 | err.url = action 19 | return err 20 | } 21 | 22 | function getBody(xhr) { 23 | const text = xhr.responseText || xhr.response 24 | if (!text) { 25 | return text 26 | } 27 | 28 | try { 29 | return JSON.parse(text) 30 | } catch (e) { 31 | return text 32 | } 33 | } 34 | 35 | export default function upload(option) { 36 | if (typeof XMLHttpRequest === 'undefined') { 37 | return 38 | } 39 | 40 | const xhr = new XMLHttpRequest() 41 | const action = option.action 42 | 43 | if (xhr.upload) { 44 | xhr.upload.onprogress = function progress(e) { 45 | if (e.total > 0) { 46 | e.percent = (e.loaded / e.total) * 100 47 | } 48 | option.onProgress(e) 49 | } 50 | } 51 | 52 | const formData = new FormData() 53 | 54 | if (option.data) { 55 | Object.keys(option.data).forEach(key => { 56 | formData.append(key, option.data[key]) 57 | }) 58 | } 59 | 60 | formData.append(option.filename, option.file, option.file.name) 61 | 62 | xhr.onerror = function error(e) { 63 | option.onError(e) 64 | } 65 | 66 | xhr.onload = function onload() { 67 | if (xhr.status < 200 || xhr.status >= 300) { 68 | return option.onError(getError(action, option, xhr)) 69 | } 70 | 71 | option.onSuccess(getBody(xhr)) 72 | } 73 | 74 | xhr.open('post', action, true) 75 | 76 | if (option.withCredentials && 'withCredentials' in xhr) { 77 | xhr.withCredentials = true 78 | } 79 | 80 | const headers = option.headers || {} 81 | 82 | for (let item in headers) { 83 | if (headers.hasOwnProperty(item) && headers[item] !== null) { 84 | xhr.setRequestHeader(item, headers[item]) 85 | } 86 | } 87 | xhr.send(formData) 88 | return xhr 89 | } 90 | -------------------------------------------------------------------------------- /src/style/iconfont.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "ico"; 3 | src: url('./fonts/iconfont.eot'); 4 | src: url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff'), url('./fonts/iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 5 | url('./fonts/iconfont.svg') format('svg'); 6 | /* iOS 4.1- */ 7 | } 8 | 9 | .ico { 10 | font-family: "ico" !important; 11 | font-size: 16px; 12 | font-style: normal; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | } 16 | 17 | .ico-clip:before { 18 | content: "\e62d"; 19 | } 20 | 21 | .ico-img:before { 22 | content: "\e62f"; 23 | } 24 | 25 | .ico-upload:before { 26 | content: "\e6a5"; 27 | } 28 | 29 | .ico-img-light:before { 30 | content: "\e624"; 31 | } -------------------------------------------------------------------------------- /src/style/fonts/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /README-CN.MD: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | vip 5 |

6 |
7 | 8 |

9 | GitHub release 10 | poi 11 | Bili 12 |

13 | 14 |
15 | 16 |

17 | 18 | 教程: 从0开始做一个文件选择(上传)组件 19 | | 20 | 查看 Demo 21 | 22 |

23 | 24 |
25 | 26 | ## 简介 27 | 28 | - 就像 ``,不支持多图片选择 29 | - 支持自动上传功能 30 | - 拖拽选择、预览、也可以当 `` 使用 31 | - 两套主题,其他高度定制 32 | 33 |
34 | 35 | ## 截图 36 | 37 |

38 | 39 |

40 | 41 |
42 | 43 | ## 使用 44 | 45 | ```bash 46 | npm install vue-img-inputer 47 | ``` 48 | 49 | ```javascript 50 | import ImgInputer from 'vue-img-inputer' 51 | import 'vue-img-inputer/dist/index.css' 52 | 53 | Vue.component('ImgInputer', ImgInputer) 54 | ``` 55 | 56 | ```html 57 | 58 | ``` 59 | 60 |
61 | 62 | ## 接口 63 | 64 |
自动上传 相关 65 |
66 | 67 | 1. **`auto-upload`**: Boolean 68 | 设置开启自动上传功能 69 | 70 | 2. **`action`**: String 71 | 上传的 URL 72 | 73 | 3. **`upload-key`**: String 74 | **default**: `file` 75 | 图片在 form-data 中的 key 名 76 | 77 | 4. **`extra-data`**: Object 78 | 上传的额外数据 79 | 80 | 5. **`headers`**: Object 81 | 上传的额外 headers 82 | 83 | 6. **`with-cookie`**: Boolean 84 | 是否携带 cookie 85 | 86 | 7. **`on-start`**: Function 87 | **params**: func ( file ) 88 | 上传开始的钩子函数 89 | 90 | 8. **`on-progress`**: Function 91 | **params**: func ( event, file ) 92 | 上传中的钩子函数,可以通过 `event.percent` 获得当前上传进度 93 | 94 | 9. **`on-success`**: Function 95 | **params**: func ( res, file ) 96 | 上传成功的钩子函数,`res` 是服务端返回的数据 97 | 98 | 10. **`on-error`**: Function 99 | **params**: func ( err, file ) 100 | 上传失败的钩子函数 101 | 102 |
103 |
104 | 105 |
Input 标签 相关 106 |
107 | 108 | 1. **`accept`**: String 109 | **default**: `image/*,video/*;` 110 | 建议设置成像 `image/jpg,image/gif;` 之类的具体指,不然可能造成文件夹呼出特别慢 111 | 112 | 2. **`placeholder`**: String 113 | **default**: `点击或拖拽选择图片` 114 | 115 | 3. **`id`**: String 116 | **default**: random string in 4 length 117 | 118 | 4. **`readonly`**: Boolean 119 | 120 | 5. **`capture`**: Boolean 121 | **default**: `false` 122 | 在移动端是否直接呼出相机 123 | 124 | 6. **`max-size`**: Number 125 | **default**: 5120 126 | 图片大小限制 (KB) 127 | 128 | 7. **`name`**: Boolean 129 | 原生 name 属性 130 | 131 | 8. **`任意 input 的原生属性`**: any 132 | 任意 input 的原生属性都将继承给内部的 input 标签 133 | 134 |
135 |
136 | 137 |
视觉 相关 138 |
139 | 140 | 1. **`img-src`**: String 141 | 图片回填地址,设置后组件将会像 `` 标签一样 142 | 143 | 2. **`theme`**: String 144 | **default**: `material` 145 | 两套主题 (light / material) 146 | 147 | 3. **`size`**: String 148 | small / normal / large 149 | 150 | 4. **`icon`**: String 151 | clip / img / img2 152 | 153 | 5. **`ali-icon`**: String 154 | 如果你用了 [iconfont.cn](http://iconfont.cn/), 可以设置你项目里的 icon unicode 值 155 | 156 | 6. **`no-mask`**: Boolean 157 | 去除 hover 蒙版 158 | 159 | 7. **`no-hover-effect`**: Boolean 160 | 去除所有 hover 效果 161 | 162 | 8. **`bottom-text`**: String 163 | **default**: `点击或拖拽图片以修改` 164 | hover 后底部的文字 165 | 166 | 9. **`readonly-tip-text`**: String 167 | **default**: `不可更改` 168 | 只读情况下底部的文字 169 | 170 |
171 |
172 | 173 |
事件 174 |
175 | 176 | 1. **`on-change`**: Function 177 | **params**: func ( file, fileName ) 178 | 文件更改时的钩子函数 179 | 180 | 2. **`onExceed`**: Function 181 | **params**: func ( file ) 182 | 文件超出最大设置时的钩子函数 183 | 184 |
185 |
186 | 187 |
组件方法 188 |
189 | 190 | 1. **`reset`** 191 | 重置组建的数据,但不重置 `img-src` 192 | 193 |
194 |
195 | -------------------------------------------------------------------------------- /example/App.vue: -------------------------------------------------------------------------------- 1 | 113 | 114 | 155 | 156 | 218 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | vip 5 |

6 |
7 | 8 |

9 | GitHub release 10 | poi 11 | Bili 12 |

13 | 14 |
15 | 16 |

17 | 18 | 点击查看中文文档 19 | | 20 | Have a glance at Demo 21 | 22 |

23 | 24 |
25 | 26 | ## Intro 27 | 28 | - `` like, only support single image 29 | - auto-upload support 30 | - drop-select / preview / use as `` as well 31 | - optional skins 32 | 33 |
34 | 35 | ## Screenshots 36 | 37 |

38 | 39 |

40 | 41 |
42 | 43 | ## Install and Usage 44 | 45 | ```bash 46 | npm install vue-img-inputer 47 | ``` 48 | 49 | ```javascript 50 | import ImgInputer from 'vue-img-inputer' 51 | import 'vue-img-inputer/dist/index.css' 52 | 53 | Vue.component('ImgInputer', ImgInputer) 54 | ``` 55 | 56 | ```html 57 | 58 | ``` 59 | 60 |
61 | 62 | ## API 63 | 64 |
Auto upload related 65 |
66 | 67 | 1. **`auto-upload`**: Boolean 68 | set to enable auto-upload 69 | 70 | 2. **`action`**: String 71 | request URL 72 | 73 | 3. **`upload-key`**: String 74 | **default**: `file` 75 | key name in form-data 76 | 77 | 4. **`extra-data`**: Object 78 | extra data been appended in request's form-data 79 | 80 | 5. **`headers`**: Object 81 | additional headers of request 82 | 83 | 6. **`with-cookie`**: Boolean 84 | whether cookies will been sent 85 | 86 | 7. **`on-start`**: Function 87 | **params**: func ( file ) 88 | hook function when upload start 89 | 90 | 8. **`on-progress`**: Function 91 | **params**: func ( event, file ) 92 | hook function when uploading, get progress by `event.percent` 93 | 94 | 9. **`on-success`**: Function 95 | **params**: func ( res, file ) 96 | hook function when upload success, `res` is response from server 97 | 98 | 10. **`on-error`**: Function 99 | **params**: func ( err, file ) 100 | hook function when upload failed 101 | 102 |
103 |
104 | 105 |
Input tag related 106 |
107 | 108 | 1. **`accept`**: String 109 | **default**: `image/*,video/*;` 110 | suggest to set a specific value like `image/jpg,image/gif;` for wildcard will lead to a serious delay 111 | 112 | 2. **`placeholder`**: String 113 | **default**: `Drop file here or click` 114 | 115 | 3. **`id`**: String 116 | **default**: random string in 4 length 117 | id of input tag 118 | 119 | 4. **`readonly`**: Boolean 120 | 121 | 5. **`capture`**: Boolean 122 | **default**: `false` 123 | whether use camera directly in mobile 124 | 125 | 6. **`max-size`**: Number 126 | **default**: 5120 127 | max-size of image (KB) 128 | 129 | 7. **`name`**: Boolean 130 | name of input tag 131 | 132 | 8. **`any input's attribute`** 133 | any input's original attributes set on component will be inherited by inner input tag 134 | 135 |
136 |
137 | 138 |
UI related 139 |
140 | 141 | 1. **`img-src`**: String 142 | image resource let component behavior like `` 143 | 144 | 2. **`theme`**: String 145 | **default**: `material` 146 | two themes optional (light / material) 147 | 148 | 3. **`size`**: String 149 | small / normal / large 150 | 151 | 4. **`icon`**: String 152 | clip / img / img2 153 | 154 | 5. **`ali-icon`**: String 155 | if you use [iconfont.cn](http://iconfont.cn/), set unicode of any icon to custom 156 | 157 | 6. **`no-mask`**: Boolean 158 | remove mask when hover 159 | 160 | 7. **`no-hover-effect`**: Boolean 161 | remove all hover effect (include text) when hover 162 | 163 | 8. **`bottom-text`**: String 164 | **default**: `Drop file here or click to change` 165 | text in the bottom when hover 166 | 167 | 9. **`no-multiple-text`**: String 168 | **default**: `Not support multiple files` 169 | visible only the state is `readonly`, cover bottom-text 170 | 171 | 10. **`exceed-size-text`**: String 172 | **default**: `The size of file should less than: ${maxSize}` 173 | visible only the state is `readonly`, cover bottom-text 174 | 175 | 11. **`no-action-text`**: String 176 | **default**: `Action hasn't set up yet` 177 | visible only the state is `readonly`, cover bottom-text 178 | 179 | 12. **`readonly-tip-text`**: String 180 | **default**: `Readonly` 181 | visible only the state is `readonly`, cover bottom-text 182 | 183 |
184 |
185 | 186 |
Events 187 |
188 | 189 | 1. **`onChange`**: Function 190 | **params**: func ( file, fileName ) 191 | hook function when file selected 192 | 193 | 2. **`onExceed`**: Function 194 | **params**: func ( file ) 195 | hook function when file exceed max-size 196 | 197 |
198 |
199 | 200 |
Methods 201 |
202 | 203 | 1. **`reset`** 204 | reset componet but will not clear `img-src` 205 | 206 |
207 |
208 | -------------------------------------------------------------------------------- /src/style/index.scss: -------------------------------------------------------------------------------- 1 | @import './iconfont'; 2 | 3 | .img-inputer { 4 | position: relative; 5 | display: inline-block; 6 | width: 260px; 7 | height: 150px; 8 | border-radius: 5px; 9 | background: #f0f0f0; 10 | box-shadow: 0 1px 6px rgba(0, 0, 0, 0.117647), 0 1px 4px rgba(0, 0, 0, 0.117647); 11 | transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1); 12 | 13 | &--small { 14 | width: 160px; 15 | height: 100px; 16 | 17 | .img-inputer__placeholder { 18 | font-size: 12px; 19 | top: 65%; 20 | } 21 | .img-inputer__icon { 22 | font-size: 28px !important; 23 | top: 38%; 24 | } 25 | } 26 | 27 | &--large { 28 | width: 460px; 29 | height: 250px; 30 | } 31 | 32 | &:hover { 33 | transform: scale(1.015); 34 | box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 35 | 0 2px 4px -1px rgba(0, 0, 0, 0.3); 36 | } 37 | 38 | &.nhe:hover { 39 | transform: scale(1); 40 | box-shadow: 0 1px 6px rgba(0, 0, 0, 0.117647), 0 1px 4px rgba(0, 0, 0, 0.117647); 41 | } 42 | 43 | &--light { 44 | background: #fbfdff; 45 | border: 1px solid #97a8be; 46 | box-shadow: none; 47 | 48 | .img-inputer__icon { 49 | color: #97a8be; 50 | } 51 | 52 | &:hover { 53 | transform: scale(1); 54 | box-shadow: none; 55 | border-color: #20a0ff; 56 | } 57 | } 58 | 59 | &--loading { 60 | overflow: hidden; 61 | 62 | &:hover { 63 | transform: scale(1); 64 | box-shadow: 0 1px 6px rgba(0, 0, 0, 0.117647), 0 1px 4px rgba(0, 0, 0, 0.117647); 65 | } 66 | } 67 | 68 | &__loading { 69 | position: absolute; 70 | width: 100%; 71 | height: 100%; 72 | z-index: 11; 73 | background: mix(black, transparent, 50); 74 | 75 | &-indicator { 76 | position: absolute; 77 | z-index: 2; 78 | top: 13px; 79 | left: 13px; 80 | border-radius: 50%; 81 | width: 10px; 82 | height: 10px; 83 | border: 2px solid #fff; 84 | border-left-color: transparent; 85 | animation: vip-spin 0.5s linear infinite; 86 | } 87 | 88 | &-process { 89 | width: 100%; 90 | height: 100%; 91 | background: mix(#00ffc2, transparent, 26); 92 | transform: translate3d(-100%, 0, 0); 93 | transition: 0.1s linear; 94 | } 95 | } 96 | 97 | &__state { 98 | position: absolute; 99 | top: 13px; 100 | left: 13px; 101 | border-radius: 50%; 102 | width: 16px; 103 | z-index: 2; 104 | height: 16px; 105 | 106 | &.success { 107 | background: rgb(16, 201, 77); 108 | 109 | &::after { 110 | position: absolute; 111 | content: ''; 112 | top: 5px; 113 | left: 3px; 114 | width: 10px; 115 | height: 5px; 116 | box-shadow: 4px 0 0 -2px #fff inset, 0 -4px 0 -2px #fff inset; 117 | transform: translateY(-0.5px) rotate(-45deg); 118 | } 119 | } 120 | 121 | &.fail { 122 | background: #e55; 123 | &::after, 124 | &::before { 125 | position: absolute; 126 | content: ''; 127 | display: block; 128 | width: 8px; 129 | height: 2px; 130 | background: #fff; 131 | top: 50%; 132 | left: 50%; 133 | } 134 | 135 | &::before { 136 | transform: translate(-50%, -50%) rotate(45deg); 137 | } 138 | 139 | &::after { 140 | transform: translate(-50%, -50%) rotate(-45deg); 141 | } 142 | } 143 | } 144 | 145 | > i { 146 | position: absolute; 147 | font-size: 34px !important; 148 | left: 50%; 149 | top: 40%; 150 | color: #757575; 151 | transform: translate(-50%, -50%); 152 | } 153 | 154 | &__placeholder { 155 | position: absolute; 156 | margin: 0; 157 | font-size: 14px; 158 | width: 100%; 159 | color: #aaa; 160 | top: 62%; 161 | text-align: center; 162 | } 163 | 164 | &__preview-box { 165 | position: absolute; 166 | top: 0; 167 | bottom: 0; 168 | left: 0; 169 | right: 0; 170 | border-radius: 5px; 171 | overflow: hidden; 172 | background: #333; 173 | } 174 | 175 | &__preview-img { 176 | position: relative; 177 | top: 0; 178 | z-index: 2; 179 | width: 100%; 180 | } 181 | 182 | &__label { 183 | position: absolute; 184 | top: 0; 185 | left: 0; 186 | bottom: 0; 187 | right: 0; 188 | z-index: 10; 189 | cursor: pointer; 190 | margin-bottom: 0; 191 | 192 | &:hover { 193 | ~ .img-inputer__mask { 194 | display: block; 195 | } 196 | } 197 | } 198 | 199 | &__inputer { 200 | position: absolute; 201 | left: -9999px; 202 | } 203 | 204 | &__mask { 205 | display: none; 206 | position: absolute; 207 | z-index: 9; 208 | top: 0; 209 | left: 0; 210 | right: 0; 211 | bottom: 0; 212 | border-radius: 5px; 213 | text-align: center; 214 | background: rgba(0, 0, 0, 0.22); 215 | } 216 | 217 | &__file-name { 218 | color: white; 219 | font-size: 13px; 220 | padding-top: 10px; 221 | margin: 0; 222 | display: inline-block; 223 | max-width: 90%; 224 | text-overflow: ellipsis; 225 | white-space: nowrap; 226 | overflow: hidden; 227 | } 228 | 229 | &__change { 230 | position: absolute; 231 | bottom: 0; 232 | margin: 0; 233 | font-size: 12px; 234 | border-radius: 0 0 5px 5px; 235 | background: rgba(0, 0, 0, 0.7); 236 | text-align: center; 237 | color: white; 238 | width: 100%; 239 | padding: 9px 0; 240 | } 241 | 242 | &__err { 243 | color: #e55; 244 | font-size: 12px; 245 | position: absolute; 246 | bottom: -28px; 247 | width: 100%; 248 | } 249 | } 250 | 251 | @keyframes vip-spin { 252 | to { 253 | transform: rotate(360deg); 254 | } 255 | } 256 | 257 | .vip-move-in { 258 | &-enter-active, 259 | &-leave-active { 260 | transition: 0.15s cubic-bezier(0.4, 0, 0.2, 1); 261 | will-change: trnsform; 262 | } 263 | &-enter, 264 | &-leave-active { 265 | opacity: 0; 266 | transform: translateX(30%); 267 | } 268 | } 269 | 270 | .vip-zoom-in { 271 | &-enter-active, 272 | &-leave-active { 273 | transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1); 274 | 275 | will-change: trnsform; 276 | } 277 | &-enter, 278 | &-leave-active { 279 | transform: scale(0); 280 | } 281 | } 282 | 283 | .vip-fade { 284 | &-enter-active, 285 | &-leave-active { 286 | transition: 0.15s cubic-bezier(0.4, 0, 0.2, 1); 287 | will-change: opacity; 288 | } 289 | &-enter, 290 | &-leave-active { 291 | opacity: 0; 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /src/index.vue: -------------------------------------------------------------------------------- 1 | 118 | 119 | 577 | --------------------------------------------------------------------------------