├── .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 |
2 | 优雅的图片输入、上传组件
自动上传、图片预览、拖拽、回填、主题可选、高度可定制
多用于非单个上传,随其余表单字段一起上传的图片
3 |
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 |
39 |
--------------------------------------------------------------------------------
/README-CN.MD:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
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 |
2 |
3 |

4 |
5 |
6 |
7 |
8 |
9 |
16 |
17 | default
18 |
19 |
20 |
21 |
22 |
30 |
31 | auto-upload
32 |
33 |
34 |
35 |
36 |
37 |
38 | icon="img"
39 |
40 |
41 |
42 |
43 |
44 |
45 | theme="light"
46 |
47 |
48 |
49 |
50 |
51 |
52 | placeholder="..."
53 |
54 |
55 |
56 |
57 |
58 |
59 | img-src="..."
60 |
61 |
62 |
63 |
64 |
71 |
72 | readonly
73 |
74 |
75 |
76 |
77 |
85 |
86 | no-mask
87 |
88 |
89 |
90 |
91 |
92 |
93 | no-hover-effect
94 |
95 |
96 |
97 |
98 |
99 |
100 | size="small"
101 |
102 |
103 |
104 |
105 |
106 |
107 | size="large"
108 |
109 |
110 |
111 |
112 |
113 |
114 |
155 |
156 |
218 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
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 |
2 |
117 |
118 |
119 |
577 |
--------------------------------------------------------------------------------