vue-editify 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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>