@neatui/nuxt 0.1.0

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.
Files changed (102) hide show
  1. package/README.md +3 -0
  2. package/package.json +43 -0
  3. package/rollup.config.js +35 -0
  4. package/src/components/basic/IDraggable.vue +84 -0
  5. package/src/components/basic/IDraggable@b.vue +80 -0
  6. package/src/components/basic/IFollowView.vue +211 -0
  7. package/src/components/basic/IPickerView.vue +351 -0
  8. package/src/components/basic/IRouterView.vue +360 -0
  9. package/src/components/basic/IScrollView.vue +127 -0
  10. package/src/components/basic/Icon.vue +54 -0
  11. package/src/components/basic/LayerView/Layer.vue +339 -0
  12. package/src/components/basic/LayerView/index.ts +5 -0
  13. package/src/components/basic/index.ts +7 -0
  14. package/src/components/display/Avatar.vue +1 -0
  15. package/src/components/display/Badge.vue +1 -0
  16. package/src/components/display/Calendar.vue +245 -0
  17. package/src/components/display/CalendarReg.vue +245 -0
  18. package/src/components/display/Card.vue +1 -0
  19. package/src/components/display/Carousel.vue +1 -0
  20. package/src/components/display/ChartView.vue +123 -0
  21. package/src/components/display/Collapse.vue +1 -0
  22. package/src/components/display/Desriptions.vue +1 -0
  23. package/src/components/display/Empty.vue +1 -0
  24. package/src/components/display/Image.vue +112 -0
  25. package/src/components/display/List.vue +1 -0
  26. package/src/components/display/PhotoEditor.vue +181 -0
  27. package/src/components/display/PhotoViewer.vue +50 -0
  28. package/src/components/display/Popover.vue +1 -0
  29. package/src/components/display/QRCode.vue +1 -0
  30. package/src/components/display/Segmented.vue +1 -0
  31. package/src/components/display/Statistic.vue +1 -0
  32. package/src/components/display/Table.vue +1 -0
  33. package/src/components/display/Tabs.vue +1 -0
  34. package/src/components/display/Tag.vue +1 -0
  35. package/src/components/display/Timeline.vue +1 -0
  36. package/src/components/display/Tooltip.vue +1 -0
  37. package/src/components/display/Tour.vue +1 -0
  38. package/src/components/display/Tree.vue +431 -0
  39. package/src/components/display/TreeView.vue +225 -0
  40. package/src/components/display/index.ts +8 -0
  41. package/src/components/form/Cascader.vue +435 -0
  42. package/src/components/form/DatePicker.vue +124 -0
  43. package/src/components/form/DateRangePicker@v2.vue.backup +224 -0
  44. package/src/components/form/DateRangePicker@v3.vue +116 -0
  45. package/src/components/form/DateRangeView@v3.vue +386 -0
  46. package/src/components/form/DateView.vue +229 -0
  47. package/src/components/form/DateView@v2.vue +386 -0
  48. package/src/components/form/DateView@v3.vue +471 -0
  49. package/src/components/form/EditUpload.vue +4 -0
  50. package/src/components/form/ImgUpload.vue +174 -0
  51. package/src/components/form/Input.vue.backup +230 -0
  52. package/src/components/form/Input@v3.vue +267 -0
  53. package/src/components/form/InputNumber.vue +200 -0
  54. package/src/components/form/InputRange.vue +235 -0
  55. package/src/components/form/MoreSelect.vue.backup +144 -0
  56. package/src/components/form/MoreSelect@v3.vue +195 -0
  57. package/src/components/form/MoreSelectList.vue +125 -0
  58. package/src/components/form/MoreSelectPanel@v3.vue +190 -0
  59. package/src/components/form/MoreSelectPicker.vue +124 -0
  60. package/src/components/form/MoreSelectTags.vue +124 -0
  61. package/src/components/form/PageMoreSelect.vue +187 -0
  62. package/src/components/form/PageSelect.vue +189 -0
  63. package/src/components/form/SearchMoreSelect.vue +173 -0
  64. package/src/components/form/SearchSelect.vue.backup +194 -0
  65. package/src/components/form/SearchSelect@v3.vue +202 -0
  66. package/src/components/form/Select@v3.vue +201 -0
  67. package/src/components/form/SelectList.vue +58 -0
  68. package/src/components/form/SelectPicker.vue +97 -0
  69. package/src/components/form/SelectTags.vue +52 -0
  70. package/src/components/form/SelectTree/SelectTree@v1.vue +227 -0
  71. package/src/components/form/Switch.vue +135 -0
  72. package/src/components/form/TextArea.vue +193 -0
  73. package/src/components/form/TimePicker.vue +11 -0
  74. package/src/components/form/TimeView.vue +244 -0
  75. package/src/components/form/Upload.vue +346 -0
  76. package/src/components/form/index.ts +82 -0
  77. package/src/components/loader/FormLoader/FormLoader@v2.vue +422 -0
  78. package/src/components/loader/FormLoader/FormLoader@v3.vue.backup +318 -0
  79. package/src/components/loader/FormLoader/index.ts +2 -0
  80. package/src/components/loader/FormLoader@v1/FormLoader.vue +506 -0
  81. package/src/components/loader/FormLoader@v1/FormRender.vue +277 -0
  82. package/src/components/loader/LimitLoader/LimitLoader.vue.backup +131 -0
  83. package/src/components/loader/LimitLoader/LimitLoader@v2.vue.backup +174 -0
  84. package/src/components/loader/LimitLoader/LimitLoader@v3.vue +183 -0
  85. package/src/components/loader/LimitLoader/index.ts +2 -0
  86. package/src/components/loader/TableLoader/TableColView.vue +115 -0
  87. package/src/components/loader/TableLoader/TableLoader.vue +360 -0
  88. package/src/components/loader/TableLoader/index.ts +2 -0
  89. package/src/components/loader/ViewLoader/ViewLoader@v1.vue +256 -0
  90. package/src/components/loader/ViewLoader/index.ts +2 -0
  91. package/src/components/loader/index.ts +5 -0
  92. package/src/components/tools/FormDraftsView.vue +330 -0
  93. package/src/components/tools/FormVerifyView.vue +206 -0
  94. package/src/components/tools/MoreTools.vue +74 -0
  95. package/src/components/tools/MoreTools@v2.vue +74 -0
  96. package/src/components/tools/Pagination@a.vue +222 -0
  97. package/src/components/tools/Pagination@b.vue +221 -0
  98. package/src/components/tools/index.ts +5 -0
  99. package/src/index.ts +9 -0
  100. package/src/shims-vue.d.ts +5 -0
  101. package/src/store/myui.ts +50 -0
  102. package/tsconfig.json +24 -0
@@ -0,0 +1,471 @@
1
+ <template>
2
+ <div ui-date="@a" class="fekit-date-view pr" :class="`${frame ? 'b-solid bk-line b-xs r-sm' : ''}`" ui-flex="col xy" style="width: 31em; height: 24.5em">
3
+ <div class="flex-block" ui-flex="row xy">
4
+ <div style="width: 20em; height: 100%">
5
+ <div ui-date-head="" class="h-xs+ flex-fixed nx-sl mt-ss ny-ss b-solid bk-back -bb-xs" ui-flex="row xm">
6
+ <div ui-flex="row lm" class="o-lm">
7
+ <div ui-btn="@a s none :square" @click="prev('y')">
8
+ <Icon name="double-arrow-left" />
9
+ </div>
10
+ <div ui-btn="@a s none :square" @click="prev('m')">
11
+ <Icon name="arrow-left" />
12
+ </div>
13
+ </div>
14
+ <div ui-flex="row cm" class="nx-ss-sub">
15
+ <div ui-flex="row cm" class="nx-ss-sub">
16
+ <p class="nx-ss-sub nowrap" ui-flex="row cm" ui-btns="@a none xs">
17
+ <b @click="setPanelType('year')">{{ state.curr.y }}年</b>
18
+ <b @click="setPanelType('month')">{{ state.curr.m }}月</b>
19
+ </p>
20
+ </div>
21
+ </div>
22
+ <div ui-flex="row rm" class="o-lm">
23
+ <div ui-btn="@a s none :square" @click="next('m')">
24
+ <Icon name="arrow-right" />
25
+ </div>
26
+ <div ui-btn="@a s none :square" @click="next('y')">
27
+ <Icon name="double-arrow-right" />
28
+ </div>
29
+ </div>
30
+ </div>
31
+ <div ui-date-body="" class="flex-block pr">
32
+ <div class="full nb-sm">
33
+ <ul ui-flex="row am" class="fs-xs nx-sm nt-sm">
34
+ <li class="flex-block ac"><span>日</span></li>
35
+ <li class="flex-block ac"><span>一</span></li>
36
+ <li class="flex-block ac"><span>二</span></li>
37
+ <li class="flex-block ac"><span>三</span></li>
38
+ <li class="flex-block ac"><span>四</span></li>
39
+ <li class="flex-block ac"><span>五</span></li>
40
+ <li class="flex-block ac"><span>六</span></li>
41
+ </ul>
42
+ <ul ui-date-days="" ui-flex="col xm" class="flex-wrap nx-sm">
43
+ <li v-for="(week, idx) in state.curr.days" :key="idx" :ui-date-week="idx + 1" class="flex-block" ui-flex="row am" style="margin-bottom: -0.25em">
44
+ <div v-for="(item, i) in week" :key="i" class="pr flex-block nb-sm" ui-flex="col xy">
45
+ <div
46
+ v-if="item.day"
47
+ :ui-date-day="item.day"
48
+ class="flex-block"
49
+ :class="`${item.range ? 'bg-main-xs' : ''} ${item.sta ? 'r-lt-sl r-lb-sl' : ''} ${item.end ? 'r-rt-sl r-rb-sl' : ''}`"
50
+ ui-flex="col cm"
51
+ @click="change(item)"
52
+ @mouseenter="setRangeValue(item)"
53
+ >
54
+ <div
55
+ ui-flex="col cm"
56
+ class="r-sm b-solid bk-none b-xs my-ss ux-scale"
57
+ style="width: 1.8em; height: 1.8em"
58
+ :class="`${item.isLastMonth || item.isNextMonth ? 'co-idle' : item.isHoliday ? 'co-risk' : ''} ${
59
+ item.selected ? 'active bg-main-lm co-fore' : 'hover-bg-weak'
60
+ }`"
61
+ >
62
+ <span>{{ item.day }}</span>
63
+ </div>
64
+ </div>
65
+ <div class="pa full-x lh-xs ny-ss ob-no ol-no nowrap s-ml bold" style="transform-origin: 50% 100%" ui-flex="row ct" v-html="extras(item)"></div>
66
+ </div>
67
+ </li>
68
+ </ul>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ <div style="width: 11em" class="flex-fixed b-solid bk-back bl-xs" ui-flex="col xy">
73
+ <div ui-date-head="" class="h-xs+ flex-fixed nx-sl mt-ss ny-ss b-solid bk-back -bb-xs" ui-flex="row cm">
74
+ <div ui-flex="row cm" class="nx-ss-sub">
75
+ <div ui-flex="row cm" class="nx-ss-sub">
76
+ <p class="nx-ss-sub nowrap" ui-flex="row cm" ui-btns="@a none xs">
77
+ <b @click="setPanelType('year')">时间</b>
78
+ </p>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ <div ui-date-body="" class="flex-block pr nl-ss n-ss-sub ac">
83
+ <div class="full" ui-row="mob-8">
84
+ <div class="full" ui-scroll=":y" style="padding-bottom: 15.2em">
85
+ <ul ui-btns="@a none s :block">
86
+ <li>00</li>
87
+ <li>01</li>
88
+ <li>02</li>
89
+ <li>03</li>
90
+ <li>04</li>
91
+ <li>05</li>
92
+ <li>06</li>
93
+ <li>07</li>
94
+ <li>08</li>
95
+ <li>09</li>
96
+ <li>10</li>
97
+ <li>11</li>
98
+ <li>12</li>
99
+ <li>13</li>
100
+ <li>14</li>
101
+ <li>15</li>
102
+ <li>16</li>
103
+ <li>17</li>
104
+ <li>18</li>
105
+ <li>19</li>
106
+ <li>20</li>
107
+ <li>21</li>
108
+ <li>22</li>
109
+ <li>23</li>
110
+ </ul>
111
+ </div>
112
+ <div class="full" ui-scroll=":y" style="padding-bottom: 15.2em">
113
+ <ul ui-btns="@a none s :block">
114
+ <li>00</li>
115
+ <li>01</li>
116
+ <li>02</li>
117
+ <li>03</li>
118
+ <li>04</li>
119
+ <li>05</li>
120
+ <li>06</li>
121
+ <li>07</li>
122
+ <li>08</li>
123
+ <li>09</li>
124
+
125
+ <li>10</li>
126
+ <li>11</li>
127
+ <li>12</li>
128
+ <li>13</li>
129
+ <li>14</li>
130
+ <li>15</li>
131
+ <li>16</li>
132
+ <li>17</li>
133
+ <li>18</li>
134
+ <li>19</li>
135
+
136
+ <li>20</li>
137
+ <li>21</li>
138
+ <li>22</li>
139
+ <li>23</li>
140
+ <li>24</li>
141
+ <li>25</li>
142
+ <li>26</li>
143
+ <li>27</li>
144
+ <li>28</li>
145
+ <li>29</li>
146
+
147
+ <li>30</li>
148
+ <li>31</li>
149
+ <li>32</li>
150
+ <li>33</li>
151
+ <li>34</li>
152
+ <li>35</li>
153
+ <li>36</li>
154
+ <li>37</li>
155
+ <li>38</li>
156
+ <li>39</li>
157
+
158
+ <li>40</li>
159
+ <li>41</li>
160
+ <li>42</li>
161
+ <li>43</li>
162
+ <li>44</li>
163
+ <li>45</li>
164
+ <li>46</li>
165
+ <li>47</li>
166
+ <li>48</li>
167
+ <li>49</li>
168
+
169
+ <li>50</li>
170
+ <li>51</li>
171
+ <li>52</li>
172
+ <li>53</li>
173
+ <li>54</li>
174
+ <li>55</li>
175
+ <li>56</li>
176
+ <li>57</li>
177
+ <li>58</li>
178
+ <li>59</li>
179
+ </ul>
180
+ </div>
181
+ <div class="full" ui-scroll=":y" style="padding-bottom: 15.2em">
182
+ <ul ui-btns="@a none s :block">
183
+ <li>00</li>
184
+ <li>01</li>
185
+ <li>02</li>
186
+ <li>03</li>
187
+ <li>04</li>
188
+ <li>05</li>
189
+ <li>06</li>
190
+ <li>07</li>
191
+ <li>08</li>
192
+ <li>09</li>
193
+
194
+ <li>10</li>
195
+ <li>11</li>
196
+ <li>12</li>
197
+ <li>13</li>
198
+ <li>14</li>
199
+ <li>15</li>
200
+ <li>16</li>
201
+ <li>17</li>
202
+ <li>18</li>
203
+ <li>19</li>
204
+
205
+ <li>20</li>
206
+ <li>21</li>
207
+ <li>22</li>
208
+ <li>23</li>
209
+ <li>24</li>
210
+ <li>25</li>
211
+ <li>26</li>
212
+ <li>27</li>
213
+ <li>28</li>
214
+ <li>29</li>
215
+
216
+ <li>30</li>
217
+ <li>31</li>
218
+ <li>32</li>
219
+ <li>33</li>
220
+ <li>34</li>
221
+ <li>35</li>
222
+ <li>36</li>
223
+ <li>37</li>
224
+ <li>38</li>
225
+ <li>39</li>
226
+
227
+ <li>40</li>
228
+ <li>41</li>
229
+ <li>42</li>
230
+ <li>43</li>
231
+ <li>44</li>
232
+ <li>45</li>
233
+ <li>46</li>
234
+ <li>47</li>
235
+ <li>48</li>
236
+ <li>49</li>
237
+
238
+ <li>50</li>
239
+ <li>51</li>
240
+ <li>52</li>
241
+ <li>53</li>
242
+ <li>54</li>
243
+ <li>55</li>
244
+ <li>56</li>
245
+ <li>57</li>
246
+ <li>58</li>
247
+ <li>59</li>
248
+ </ul>
249
+ </div>
250
+ </div>
251
+ </div>
252
+ </div>
253
+ </div>
254
+ <div v-if="tools" ui-date-foot="" ui-flex="row xm" class="flex-fixed nx-sl ny-sl b-solid bk-line-mm bt-xs">
255
+ <div ui-flex="row lm" class="nl-ss">
256
+ <p v-if="state.panel" ui-flex="row lm">
257
+ <span ui-btn="@a xs none" class="nx-ss" @click="setPanelType('date')">{{ state.panel }}</span>
258
+ <span v-if="!state.isNoTime" ui-btn="@a xs none" class="nx-ss" @click="setPanelType('time')"> {{ state.time.h }}:{{ state.time.m }}:{{ state.time.s }} </span>
259
+ </p>
260
+ <div v-else-if="state.shortcuts?.length" ui-flex="row lm">
261
+ <div class="mr-sm-sub nx-sl-sub ny-ss-sub lh-ss nowrap fs-ss dib-sub" ui-scroll=":x y:hidden">
262
+ <p v-for="(item, idx) in state.shortcuts" :key="idx" class="bg-main-xs co-main r-xl" ui-btn="@a xs none :round" @click="item?.func">{{ item?.text }}</p>
263
+ <p class="bg-main-xs co-main r-xl" ui-btn="@a xs none :round"><Icon class="co-main" name="more" /></p>
264
+ </div>
265
+ </div>
266
+ <div>&nbsp;</div>
267
+ </div>
268
+ <div class="ml-sm-sub" ui-flex="row rm">
269
+ <button v-if="state.panel" ui-btn="@a xs none" class="nx-ss co-risk" @click="remove">清除</button>
270
+ <button v-if="state.panel && !state.isNoTime" ui-btn="@a xs none" class="nx-ss co-main" @click="update">确定</button>
271
+ </div>
272
+ </div>
273
+ </div>
274
+ </template>
275
+ <script setup lang="ts">
276
+ import { computed, reactive, watch } from 'vue';
277
+ import { idate, isDateString, isObject, isString } from '@fekit/utils';
278
+ import { Icon } from '../basic';
279
+
280
+ // 创建日期
281
+ const cDate = (date: any = null) => {
282
+ return isDateString(date) ? new Date(date) : new Date();
283
+ };
284
+
285
+ // 快捷预设
286
+ const shortcutsList: any = {
287
+ yesterday: {
288
+ text: '昨天',
289
+ func() {
290
+ change({ date: idate(new Date().setDate(new Date().getDate() - 1)).format('YYYY-MM-DD') });
291
+ }
292
+ },
293
+ today: {
294
+ text: '今天',
295
+ func() {
296
+ change({ date: idate(new Date()).format('YYYY-MM-DD') });
297
+ }
298
+ },
299
+ tomorrow: {
300
+ text: '明天',
301
+ func() {
302
+ change({ date: idate(new Date().setDate(new Date().getDate() + 1)).format('YYYY-MM-DD') });
303
+ }
304
+ }
305
+ };
306
+
307
+ const emits: any = defineEmits(['update:modelValue', 'change', 'update']);
308
+ interface TimeProps {
309
+ h: boolean | number;
310
+ m?: boolean | number;
311
+ s?: boolean | number;
312
+ }
313
+ interface Props {
314
+ modelValue?: string | number;
315
+ frame: boolean;
316
+ tools?: boolean;
317
+ range?: boolean;
318
+ format?: string;
319
+ picker?: 'date' | 'week' | 'month' | 'quarter';
320
+ holiday?: object;
321
+ shortcuts?: Array<'yesterday' | 'today' | 'tomorrow'>;
322
+ extras?: (item: any) => string;
323
+ time?: boolean | TimeProps;
324
+ }
325
+ const props: any = withDefaults(defineProps<Props>(), {
326
+ // 值
327
+ modelValue: '',
328
+ // 是否显示框架
329
+ frame: false,
330
+ // 是否是示工具
331
+ tools: true,
332
+ // 时间范围
333
+ range: false,
334
+ // 面板类型
335
+ picker: 'date',
336
+ // 额外内容
337
+ extras: (item: any) => {
338
+ return `<i class="dib o-ms mb-xs r-xl ${item.today ? 'bg-main' : 'bg-none'}" style="width:.6em; height:.6em;"></i>`;
339
+ },
340
+ // 快捷选择
341
+ shortcuts: () => ['yesterday', 'today', 'tomorrow'],
342
+ // 数据格式化
343
+ format: 'YYYY-MM-DD',
344
+ // 节假日
345
+ holiday: () => ({}),
346
+ time: false
347
+ });
348
+
349
+ // 内部数据
350
+ const state: any = reactive({
351
+ // 面板显示时间
352
+ panel: '',
353
+ // 当前时间计算
354
+ curr: computed(() => {
355
+ const date = idate(state.panel);
356
+ const days = date.calendar({ group: true, value: [state.panel] });
357
+ const { y = 0, M: m = 0, d = 0 } = date.attr || {};
358
+ return { days, y, m, d };
359
+ }),
360
+ type: 'date',
361
+ // 值
362
+ value: '',
363
+ // 快捷选择
364
+ shortcuts: computed(() => {
365
+ return props.shortcuts.map((item: any) => {
366
+ return isString(item) ? shortcutsList[item] : item;
367
+ });
368
+ }),
369
+ // 时间
370
+ time: {
371
+ h: '00',
372
+ m: '00',
373
+ s: '00'
374
+ },
375
+ isNoTime: computed(() => {
376
+ if (isObject(props.time)) {
377
+ return !(props.time.h || props.time.m || props.time.s);
378
+ } else {
379
+ return props.time;
380
+ }
381
+ })
382
+ });
383
+
384
+ watch(
385
+ () => props.modelValue,
386
+ (value: any) => {
387
+ // 面板显示日期
388
+ state.panel = value || '';
389
+ // 值
390
+ state.value = value || '';
391
+ // 时间
392
+ },
393
+ { deep: true, immediate: true }
394
+ );
395
+
396
+ // 更新时间
397
+ const upload = (a: any = 'm', b: any = 1) => {
398
+ const date = cDate(state.panel);
399
+ if (a === 'm') {
400
+ date.setMonth(date.getMonth() + b);
401
+ } else {
402
+ date.setFullYear(date.getFullYear() + b);
403
+ }
404
+ return idate(date).format(props.format);
405
+ };
406
+
407
+ // 日期前翻
408
+ const prev = (type: any = 'm') => {
409
+ state.panel = upload(type, -1);
410
+ };
411
+ // 日期后翻
412
+ const next = (type: any = 'm') => {
413
+ state.panel = upload(type, 1);
414
+ };
415
+
416
+ // 向外通信
417
+ const change = (item: any) => {
418
+ state.panel = item ? idate(item.date).format(props.format) : item;
419
+ emits('change', state.value);
420
+ if (state.isNoTime) {
421
+ console.log('这是没有时间的');
422
+ update();
423
+ }
424
+ };
425
+
426
+ // 清空日期
427
+ const remove = () => {
428
+ state.panel = '';
429
+ update();
430
+ };
431
+
432
+ // 更新数据
433
+ const update = () => {
434
+ emits('update:modelValue', state.panel);
435
+ emits('update', state.panel);
436
+ };
437
+
438
+ // 设置范围
439
+ const setRangeValue = (item: any) => {};
440
+ // 设置面板类型
441
+ const setPanelType = (type: any) => {
442
+ state.type = type;
443
+ };
444
+ // 设置年份
445
+ const setYear = (y: any) => {
446
+ state.type = 'month';
447
+ };
448
+ // 设置月份
449
+ const setMonth = (m: any) => {
450
+ state.type = 'date';
451
+ };
452
+ </script>
453
+ <style lang="scss">
454
+ @keyframes am-editing {
455
+ 0%,
456
+ 20% {
457
+ opacity: 1;
458
+ }
459
+ 40%,
460
+ 60% {
461
+ opacity: 0;
462
+ }
463
+ 80%,
464
+ 100% {
465
+ opacity: 1;
466
+ }
467
+ }
468
+ .am-editing {
469
+ animation: am-editing 1s linear infinite;
470
+ }
471
+ </style>
@@ -0,0 +1,4 @@
1
+ <template>
2
+ <div>upload</div>
3
+ </template>
4
+ <script setup lang="ts"></script>
@@ -0,0 +1,174 @@
1
+ <template>
2
+ <div ui-flex="row" class="mc-upload">
3
+ <template v-if="files && files.length > 0">
4
+ <draggable :list="files" @update="update" :force-fallback="false" item-key="name" chosen-class="chosen" animation="200" class="mc-upload-img">
5
+ <template #item="{ element, index }">
6
+ <div class="mc-upload-img-item">
7
+ <Image :src="element" :files="files" :initialSlide="index" v-bind="$attrs" @delImg="delImg" />
8
+ </div>
9
+ </template>
10
+ </draggable>
11
+ </template>
12
+ <template v-if="(!multiple && files && files.length === 0) || (multiple && (!limit || (limit && files && files.length < limit)))">
13
+ <slot>
14
+ <div ui-flex="row cm" ui-form="@a type:upload tips:hover" :class="`upload bg-fore b-solid bk-case b-xs mb-sl ${uploadClass}`" :style="uploadStyle">
15
+ <div v-if="tips" ui-form-tips>
16
+ {{ tips }}
17
+ </div>
18
+ <input v-if="!crop" type="file" class="pa input" :multiple="multiple" @change="change" />
19
+ <div v-else class="input pa" @click="uploadClick"></div>
20
+ <div class="co-note ac input-icon">
21
+ <i class="icon icon-upload fs-mm"></i>
22
+ <div class="fs-ss">上传图片</div>
23
+ </div>
24
+ </div>
25
+ </slot>
26
+ </template>
27
+
28
+ <Layer :id="layerId" am="as">
29
+ <PhotoEditor :layerId="layerId" :action="action" @fileChange="uploadImg"></PhotoEditor>
30
+ </Layer>
31
+ </div>
32
+ </template>
33
+ <script setup lang="ts">
34
+ import { ref, watch } from 'vue';
35
+ import { Image, PhotoEditor } from '../display';
36
+ import draggable from 'vuedraggable';
37
+ import Compressor from 'compressorjs';
38
+ import { Layer, LayerById } from '../basic';
39
+ import { isArray } from '@fekit/utils';
40
+
41
+ const layerId = Math.random().toString(36).slice(-6);
42
+
43
+ interface Props {
44
+ limit: number; // 文件限制数量
45
+ uploadFn: (action: string, formData: FormData) => Promise<string>; // 上传图片接口
46
+ uploadClass?: string;
47
+ uploadStyle?: string;
48
+ id?: string;
49
+ tips?: string; // 提示文案
50
+ modelValue?: string | any[];
51
+ accept?: string; // 文件类型
52
+ multiple?: boolean; // 是否可以上传多个文件
53
+ crop?: boolean; // 是否需要抠图处理
54
+ action?: string; // 上传路径
55
+ }
56
+
57
+ const props = withDefaults(defineProps<Props>(), {
58
+ uploadClass: '',
59
+ uploadStyle: '',
60
+ id: '',
61
+ tips: '',
62
+ modelValue: '',
63
+ accept: 'image/*',
64
+ multiple: false,
65
+ crop: false,
66
+ action: '/common/updateFile'
67
+ });
68
+
69
+ const emit = defineEmits(['update:modelValue', 'change']);
70
+
71
+ const files = ref<any>(isArray(props.modelValue) ? props.modelValue : props.modelValue ? [{ imgUrl: props.modelValue }] : []);
72
+
73
+ // 删除图片
74
+ const delImg = (src: any) => {
75
+ files.value = files.value.filter((item: any) => {
76
+ return item.imgUrl !== src.imgUrl;
77
+ });
78
+ };
79
+
80
+ const uploadClick = () => {
81
+ LayerById(layerId).show();
82
+ };
83
+
84
+ const update = () => {
85
+ emit('update:modelValue', isArray(props.modelValue) ? files.value : files.value.length > 0 ? files.value[0].imgUrl : '');
86
+ };
87
+
88
+ const change = async ({ target: { files: _filesList = [] } = {} }: any = {}) => {
89
+ const _files = [];
90
+ for (let i = 0; i < _filesList.length; i++) {
91
+ const file = _filesList[i];
92
+ const blob = URL.createObjectURL(file);
93
+ _files.push({ blob, file });
94
+ }
95
+
96
+ for (let i = 0; i < _files.length; i++) {
97
+ let { file = '' } = _files[i];
98
+ // 若为图片,则进行压缩处理
99
+ if (/image/.test(file.type)) {
100
+ const _blob: any = await new Promise((resolve, reject) => {
101
+ new Compressor(file, {
102
+ quality: 1,
103
+ checkOrientation: false,
104
+ success: resolve,
105
+ maxWidth: 1000,
106
+ maxHeight: 700,
107
+ error: reject
108
+ });
109
+ });
110
+ const { name = '' }: any = _blob || {};
111
+ file = new File([_blob], name);
112
+ }
113
+ uploadImg(file);
114
+ }
115
+ };
116
+
117
+ // 上传图片
118
+ const uploadImg = async (file: any) => {
119
+ const formData = new FormData();
120
+ formData.append('file', file);
121
+ const imgUrl = await props.uploadFn(props.action, formData);
122
+ if (imgUrl) files.value = [...files.value, { imgUrl }];
123
+ };
124
+
125
+ watch(
126
+ () => [props.modelValue, files.value],
127
+ (n: any, o: any) => {
128
+ if (n[0] !== o[0]) {
129
+ files.value = isArray(props.modelValue) ? props.modelValue : props.modelValue ? [{ imgUrl: props.modelValue }] : [];
130
+ }
131
+ if (n[1] !== o[1]) {
132
+ emit('update:modelValue', isArray(props.modelValue) ? files.value : files.value.length > 0 ? files.value[0].imgUrl : '');
133
+ }
134
+ },
135
+ { deep: true }
136
+ );
137
+
138
+ defineExpose({ change, uploadClick });
139
+ </script>
140
+ <style lang="scss">
141
+ .mc-upload {
142
+ flex-wrap: wrap;
143
+
144
+ .upload {
145
+ width: 8em;
146
+ height: 8em;
147
+ border-radius: 0.25em;
148
+ transition:
149
+ all 0.35s,
150
+ background-color 0s;
151
+ // background-color: #ffffff;
152
+ position: relative;
153
+
154
+ .input {
155
+ opacity: 0;
156
+ height: 100%;
157
+ width: 100%;
158
+ }
159
+ .input-icon {
160
+ line-height: 1.3;
161
+ }
162
+ }
163
+
164
+ &-img {
165
+ display: flex;
166
+ flex-direction: row;
167
+
168
+ &-item {
169
+ margin-right: 0.75rem;
170
+ margin-bottom: 0.75rem;
171
+ }
172
+ }
173
+ }
174
+ </style>