vue-editify 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <div class="editify-tooltip" :class="{ block: block }" @mouseenter="showContent" @mouseleave="hideContent">
3
+ <div ref="target" class="editify-tooltip-target">
4
+ <slot></slot>
5
+ </div>
6
+ <Layer v-model="show" :node="node" border border-color="#000" background="#000" show-triangle color="#fff" placement="bottom" animation="fade">
7
+ <div class="editify-tooltip-content">{{ content }}</div>
8
+ </Layer>
9
+ </div>
10
+ </template>
11
+ <script>
12
+ import Layer from './Layer'
13
+ export default {
14
+ name: 'Tooltip',
15
+ props: {
16
+ //提示内容
17
+ content: {
18
+ type: String,
19
+ default: ''
20
+ },
21
+ //是否禁用
22
+ disabled: {
23
+ type: Boolean,
24
+ default: false
25
+ },
26
+ //是否块级
27
+ block: {
28
+ type: Boolean,
29
+ default: false
30
+ }
31
+ },
32
+ data() {
33
+ return {
34
+ show: false,
35
+ node: null
36
+ }
37
+ },
38
+ components: {
39
+ Layer
40
+ },
41
+ methods: {
42
+ showContent() {
43
+ if (this.disabled) {
44
+ return
45
+ }
46
+ this.node = this.$refs.target
47
+ this.show = true
48
+ },
49
+ hideContent() {
50
+ if (this.disabled) {
51
+ return
52
+ }
53
+ this.show = false
54
+ }
55
+ }
56
+ }
57
+ </script>
58
+ <style lang="less" scoped>
59
+ .editify-tooltip {
60
+ position: relative;
61
+ display: inline-block;
62
+
63
+ .editify-tooltip-target {
64
+ display: inline-block;
65
+ }
66
+
67
+ .editify-tooltip-content {
68
+ display: block;
69
+ padding: 6px 10px;
70
+ font-size: @font-size;
71
+ white-space: nowrap;
72
+ }
73
+
74
+ &.block {
75
+ display: block;
76
+
77
+ .editify-tooltip-target {
78
+ display: block;
79
+ }
80
+ }
81
+ }
82
+ </style>
@@ -0,0 +1,159 @@
1
+ <template>
2
+ <div class="editify-triangle" :style="style" :data-editify-placement="placement">
3
+ <div class="editify-triangle-el" :style="elStyle"></div>
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'Triangle',
10
+ props: {
11
+ //位置
12
+ placement: {
13
+ type: String,
14
+ default: 'top',
15
+ validator(value) {
16
+ return ['top', 'left', 'right', 'bottom'].includes(value)
17
+ }
18
+ },
19
+ //边框颜色
20
+ color: {
21
+ type: String,
22
+ default: null
23
+ },
24
+ //背景色
25
+ background: {
26
+ type: String,
27
+ default: null
28
+ }
29
+ },
30
+ computed: {
31
+ style() {
32
+ if (this.placement == 'top') {
33
+ return {
34
+ borderBottomColor: this.color ? this.color : ''
35
+ }
36
+ }
37
+ if (this.placement == 'bottom') {
38
+ return {
39
+ borderTopColor: this.color ? this.color : ''
40
+ }
41
+ }
42
+ if (this.placement == 'left') {
43
+ return {
44
+ borderRightColor: this.color ? this.color : ''
45
+ }
46
+ }
47
+ if (this.placement == 'right') {
48
+ return {
49
+ borderLeftColor: this.color ? this.color : ''
50
+ }
51
+ }
52
+ },
53
+ elStyle() {
54
+ if (this.placement == 'top') {
55
+ return {
56
+ borderBottomColor: this.background ? this.background : ''
57
+ }
58
+ }
59
+ if (this.placement == 'bottom') {
60
+ return {
61
+ borderTopColor: this.background ? this.background : ''
62
+ }
63
+ }
64
+ if (this.placement == 'left') {
65
+ return {
66
+ borderRightColor: this.background ? this.background : ''
67
+ }
68
+ }
69
+ if (this.placement == 'right') {
70
+ return {
71
+ borderLeftColor: this.background ? this.background : ''
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ </script>
78
+
79
+ <style lang="less" scoped>
80
+ .editify-triangle {
81
+ position: relative;
82
+ display: inline-block;
83
+ width: 0;
84
+ height: 0;
85
+ border-color: transparent;
86
+ border-style: solid;
87
+ border-width: @triangle-size;
88
+
89
+ .editify-triangle-el {
90
+ position: absolute;
91
+ display: inline-block;
92
+ width: 0;
93
+ height: 0;
94
+ border-color: transparent;
95
+ border-style: solid;
96
+ border-width: calc(@triangle-size - 1px);
97
+ }
98
+
99
+ &[data-editify-placement='top'] {
100
+ border-top: none;
101
+ border-bottom-color: @border-color;
102
+
103
+ .editify-triangle-el {
104
+ border-top: none;
105
+ border-bottom-color: @background;
106
+ left: 1px;
107
+ top: 1.5px;
108
+ right: auto;
109
+ bottom: 0;
110
+ margin-left: -@triangle-size;
111
+ }
112
+ }
113
+
114
+ &[data-editify-placement='bottom'] {
115
+ border-bottom: none;
116
+ border-top-color: @border-color;
117
+
118
+ .editify-triangle-el {
119
+ border-bottom: none;
120
+ border-top-color: @background;
121
+ left: 1px;
122
+ bottom: 1.5px;
123
+ top: auto;
124
+ right: auto;
125
+ margin-left: -@triangle-size;
126
+ }
127
+ }
128
+
129
+ &[data-editify-placement='left'] {
130
+ border-left: none;
131
+ border-right-color: @border-color;
132
+
133
+ .editify-triangle-el {
134
+ border-left: none;
135
+ border-right-color: @background;
136
+ left: 1.5px;
137
+ top: 1px;
138
+ right: auto;
139
+ bottom: auto;
140
+ margin-top: -@triangle-size;
141
+ }
142
+ }
143
+
144
+ &[data-editify-placement='right'] {
145
+ border-right: none;
146
+ border-left-color: @border-color;
147
+
148
+ .editify-triangle-el {
149
+ border-right: none;
150
+ border-left-color: @background;
151
+ right: 1.5px;
152
+ top: 1px;
153
+ bottom: auto;
154
+ left: auto;
155
+ margin-top: -@triangle-size;
156
+ }
157
+ }
158
+ }
159
+ </style>
@@ -0,0 +1,138 @@
1
+ <template>
2
+ <div class="editify-colors">
3
+ <div class="editify-colors-header" @click="selectColor({ value: '' })">
4
+ <Icon value="remove"></Icon>
5
+ <span>{{ $editTrans('defaultColor') }}</span>
6
+ </div>
7
+ <div class="editify-colors-list">
8
+ <div class="editify-color" :class="{ active: value == item.value }" v-for="item in data" :style="{ borderColor: value == item.value ? color : '' }">
9
+ <Tooltip block :content="item.label" :disabled="!tooltip">
10
+ <div @click="selectColor(item)" class="editify-color-el" :style="{ background: item.value }"></div>
11
+ </Tooltip>
12
+ </div>
13
+ </div>
14
+ </div>
15
+ </template>
16
+ <script>
17
+ import Icon from '../base/Icon'
18
+ import Tooltip from '../base/Tooltip'
19
+ export default {
20
+ name: 'Colors',
21
+ emits: ['change'],
22
+ props: {
23
+ //颜色数组
24
+ data: {
25
+ type: Array,
26
+ default: function () {
27
+ return []
28
+ }
29
+ },
30
+ //选中的颜色
31
+ value: {
32
+ type: String,
33
+ default: null
34
+ },
35
+ //激活状态颜色
36
+ color: {
37
+ type: String,
38
+ default: ''
39
+ },
40
+ //是否使用工具提示
41
+ tooltip: {
42
+ type: Boolean,
43
+ default: false
44
+ }
45
+ },
46
+ data() {
47
+ return {}
48
+ },
49
+ inject: ['$editTrans'],
50
+ components: {
51
+ Icon,
52
+ Tooltip
53
+ },
54
+ methods: {
55
+ //选择颜色
56
+ selectColor(item) {
57
+ this.$emit('change', item.value)
58
+ }
59
+ }
60
+ }
61
+ </script>
62
+ <style lang="less" scoped>
63
+ .editify-colors {
64
+ display: block;
65
+ color: @font-color;
66
+ font-size: @font-size;
67
+ padding: 6px 10px;
68
+ box-sizing: border-box;
69
+ width: 244px;
70
+
71
+ .editify-colors-header {
72
+ display: flex;
73
+ justify-content: flex-start;
74
+ align-items: center;
75
+ position: relative;
76
+ padding: 6px 10px;
77
+ transform: all 200ms;
78
+ margin-bottom: 10px;
79
+ opacity: 0.8;
80
+
81
+ &::after {
82
+ content: '';
83
+ width: 100%;
84
+ height: 1px;
85
+ background-color: @background-darker;
86
+ position: absolute;
87
+ left: 0;
88
+ bottom: -5px;
89
+ }
90
+
91
+ :deep(.editify-icon) {
92
+ margin-right: 10px;
93
+ }
94
+
95
+ &:hover {
96
+ cursor: pointer;
97
+ background-color: @background-dark;
98
+ opacity: 1;
99
+ }
100
+
101
+ &:active {
102
+ opacity: 1;
103
+ background-color: @background-darker;
104
+ }
105
+ }
106
+
107
+ .editify-colors-list {
108
+ display: flex;
109
+ justify-content: flex-start;
110
+ flex-wrap: wrap;
111
+
112
+ .editify-color {
113
+ display: block;
114
+ padding: 4px;
115
+ border: 1px solid transparent;
116
+ border-radius: 2px;
117
+
118
+ &.active {
119
+ border-color: @font-color-light;
120
+ }
121
+
122
+ .editify-color-el {
123
+ display: block;
124
+ width: 16px;
125
+ height: 16px;
126
+ border: 1px solid @border-color;
127
+ border-radius: 2px;
128
+ cursor: pointer;
129
+ transition: all 200ms;
130
+
131
+ &:hover {
132
+ transform: scale(1.2);
133
+ }
134
+ }
135
+ }
136
+ }
137
+ }
138
+ </style>
@@ -0,0 +1,316 @@
1
+ <template>
2
+ <div class="editify-image">
3
+ <div class="editify-image-header">
4
+ <div @click="current = 'upload'" class="editify-image-header-item" :class="{ active: current == 'upload' }" :style="activeStyle('upload')">{{ $editTrans('uploadImage') }}</div>
5
+ <div @click="current = 'remote'" class="editify-image-header-item" :class="{ active: current == 'remote' }" :style="activeStyle('remote')">{{ $editTrans('remoteImage') }}</div>
6
+ <div class="editify-image-header-slider" :class="current" :style="{ backgroundColor: color || '' }"></div>
7
+ </div>
8
+ <!-- 网络图片 -->
9
+ <div class="editify-image-remote" v-if="current == 'remote'">
10
+ <input v-model.trim="remoteUrl" :placeholder="$editTrans('imageUrlPlaceholder')" @blur="handleInputBlur" @focus="handleInputFocus" />
11
+ <div class="editify-image-remote-footer" :style="{ color: color }">
12
+ <span @click="insertRemoteImage">{{ $editTrans('insert') }}</span>
13
+ </div>
14
+ </div>
15
+ <!-- 上传图片 -->
16
+ <div class="editify-image-upload" v-else>
17
+ <Icon value="upload"></Icon>
18
+ <input :multiple="multiple" accept="image/*" @change="selectFile" type="file" />
19
+ </div>
20
+ </div>
21
+ </template>
22
+ <script>
23
+ import Dap from 'dap-util'
24
+ import Icon from '../base/Icon'
25
+ export default {
26
+ name: 'InsertImage',
27
+ emits: ['change', 'insert'],
28
+ props: {
29
+ //主题色
30
+ color: {
31
+ type: String,
32
+ default: ''
33
+ },
34
+ //支持的图片类型数组
35
+ accept: {
36
+ type: Array,
37
+ default: null
38
+ },
39
+ //是否支持多选
40
+ multiple: {
41
+ type: Boolean,
42
+ default: false
43
+ },
44
+ //单个文件最大值
45
+ maxSize: {
46
+ type: Number,
47
+ default: null
48
+ },
49
+ //单个文件最小值
50
+ minSize: {
51
+ type: Number,
52
+ default: null
53
+ },
54
+ //是否自定义上传图片
55
+ customUpload: {
56
+ type: Function,
57
+ default: null
58
+ },
59
+ //处理上传图片异常
60
+ handleError: {
61
+ type: Function,
62
+ default: null
63
+ }
64
+ },
65
+ inject: ['$editTrans'],
66
+ data() {
67
+ return {
68
+ current: 'upload', //当前展示的面板,取值remote和upload
69
+ remoteUrl: '' //远程图片链接
70
+ }
71
+ },
72
+ computed: {
73
+ activeStyle() {
74
+ return name => {
75
+ if (this.current == name) {
76
+ return {
77
+ color: this.color
78
+ }
79
+ }
80
+ return {}
81
+ }
82
+ }
83
+ },
84
+ components: {
85
+ Icon
86
+ },
87
+ watch: {
88
+ //监听current变更触发change事件
89
+ current() {
90
+ this.$emit('change')
91
+ }
92
+ },
93
+ methods: {
94
+ //选择文件
95
+ async selectFile(e) {
96
+ const inputEle = e.currentTarget
97
+ const files = inputEle.files
98
+ if (!files.length) {
99
+ return
100
+ }
101
+ let filterFiles = []
102
+ for (let i = 0; i < files.length; i++) {
103
+ const file = files[i]
104
+ const suffix = this.getSuffix(file)
105
+ const isMatch = this.accept.some(item => {
106
+ return item.toLocaleLowerCase() == suffix.toLocaleLowerCase()
107
+ })
108
+ //后缀不符合
109
+ if (!isMatch) {
110
+ //如果自定义了异常处理
111
+ if (typeof this.handleError == 'function') {
112
+ this.handleError.apply(this, ['suffixError', file])
113
+ }
114
+ continue
115
+ }
116
+ //超过最大值
117
+ if (this.maxSize && file.size / 1024 > this.maxSize) {
118
+ //如果自定义了异常处理
119
+ if (typeof this.handleError == 'function') {
120
+ this.handleError.apply(this, ['maxSizeError', file])
121
+ }
122
+ continue
123
+ }
124
+ //没达到最小值
125
+ if (this.minSize && file.size / 1024 < this.minSize) {
126
+ //如果自定义了异常处理
127
+ if (typeof this.handleError == 'function') {
128
+ this.handleError.apply(this, ['minSizeError', file])
129
+ }
130
+ continue
131
+ }
132
+ filterFiles.push(file)
133
+ }
134
+ //有文件可上传
135
+ if (filterFiles.length) {
136
+ //自定义上传方法
137
+ if (typeof this.customUpload == 'function') {
138
+ this.customUpload.apply(this, [filterFiles])
139
+ }
140
+ //默认上传方法
141
+ else {
142
+ let images = []
143
+ for (let i = 0; i < filterFiles.length; i++) {
144
+ const url = await Dap.file.dataFileToBase64(filterFiles[i])
145
+ images.push(url)
146
+ }
147
+ images.forEach(url => {
148
+ this.$emit('insert', url)
149
+ })
150
+ }
151
+ }
152
+ //清空文件选择框
153
+ inputEle.value = ''
154
+ },
155
+ //获取文件后缀
156
+ getSuffix(file) {
157
+ const index = file.name.lastIndexOf('.')
158
+ if (index <= 0) {
159
+ return ''
160
+ }
161
+ return file.name.substring(index + 1)
162
+ },
163
+ //输入框获取焦点
164
+ handleInputFocus(e) {
165
+ if (this.color) {
166
+ e.currentTarget.style.borderColor = this.color
167
+ }
168
+ },
169
+ //输入框失去焦点
170
+ handleInputBlur(e) {
171
+ e.currentTarget.style.borderColor = ''
172
+ },
173
+ //插入网络图片
174
+ insertRemoteImage() {
175
+ this.$emit('insert', this.remoteUrl)
176
+ }
177
+ }
178
+ }
179
+ </script>
180
+ <style lang="less" scoped>
181
+ .editify-image {
182
+ display: block;
183
+ width: 280px;
184
+ padding: 10px 14px;
185
+
186
+ .editify-image-header {
187
+ display: flex;
188
+ justify-content: flex-start;
189
+ align-items: center;
190
+ width: 100%;
191
+ margin-bottom: 20px;
192
+ position: relative;
193
+ padding-bottom: 6px;
194
+
195
+ .editify-image-header-slider {
196
+ position: absolute;
197
+ width: 50px;
198
+ height: 2px;
199
+ border-radius: 2px;
200
+ left: 0;
201
+ bottom: 0;
202
+ transition: left 200ms;
203
+
204
+ &.upload {
205
+ left: 5px;
206
+ }
207
+
208
+ &.remote {
209
+ left: 85px;
210
+ }
211
+ }
212
+
213
+ .editify-image-header-item {
214
+ display: block;
215
+ text-align: center;
216
+ font-size: @font-size;
217
+ color: @font-color;
218
+ opacity: 0.8;
219
+ transition: all 200ms;
220
+ width: 60px;
221
+ overflow: hidden;
222
+ white-space: nowrap;
223
+ text-overflow: ellipsis;
224
+
225
+ &:hover {
226
+ opacity: 1;
227
+ cursor: pointer;
228
+ }
229
+
230
+ &:first-child {
231
+ margin-right: 20px;
232
+ }
233
+
234
+ &.active {
235
+ opacity: 1;
236
+ color: @font-color-dark;
237
+ }
238
+ }
239
+ }
240
+
241
+ .editify-image-remote {
242
+ display: block;
243
+ width: 100%;
244
+
245
+ input {
246
+ appearance: none;
247
+ -webkit-appearance: none;
248
+ -moz-appearance: none;
249
+ display: block;
250
+ width: 100%;
251
+ margin: 0 0 10px 0;
252
+ padding: 4px 2px;
253
+ border: none;
254
+ font-size: @font-size;
255
+ color: @font-color;
256
+ border-bottom: 1px solid @border-color;
257
+ line-height: 1.5;
258
+ transition: border-color 500ms;
259
+ background-color: transparent;
260
+ outline: none;
261
+ box-sizing: border-box;
262
+
263
+ &::-webkit-input-placeholder,
264
+ &::placeholder {
265
+ color: @font-color-disabled;
266
+ font-family: inherit;
267
+ font-size: inherit;
268
+ vertical-align: middle;
269
+ }
270
+ }
271
+
272
+ .editify-image-remote-footer {
273
+ display: flex;
274
+ justify-content: flex-end;
275
+ align-items: center;
276
+ width: 100%;
277
+ font-size: @font-size;
278
+ opacity: 0.8;
279
+ transition: all 200ms;
280
+
281
+ &:hover {
282
+ cursor: pointer;
283
+ opacity: 1;
284
+ }
285
+ }
286
+ }
287
+
288
+ .editify-image-upload {
289
+ display: flex;
290
+ justify-content: center;
291
+ align-items: center;
292
+ width: 100%;
293
+ padding: 15px 0;
294
+ font-size: 36px;
295
+ opacity: 0.8;
296
+ transition: all 200ms;
297
+ position: relative;
298
+
299
+ &:hover {
300
+ cursor: pointer;
301
+ opacity: 1;
302
+ }
303
+
304
+ input {
305
+ opacity: 0;
306
+ position: absolute;
307
+ left: 0;
308
+ top: 0;
309
+ width: 100%;
310
+ height: 100%;
311
+ z-index: 1;
312
+ cursor: pointer;
313
+ }
314
+ }
315
+ }
316
+ </style>