free-fe-core-modules 0.0.16 → 0.0.17

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 (57) hide show
  1. package/components/Basic/EIcon.vue +3 -0
  2. package/free-field/Fields/AgreementCheck.js +8 -8
  3. package/free-field/Fields/ApiCall.js +1 -1
  4. package/free-field/Fields/Boolean.js +19 -52
  5. package/free-field/Fields/Category.js +1 -1
  6. package/free-field/Fields/Check.js +8 -3
  7. package/free-field/Fields/Column.vue +12 -9
  8. package/free-field/Fields/Customize.js +1 -1
  9. package/free-field/Fields/Date.js +1 -1
  10. package/free-field/Fields/DateRange.js +2 -2
  11. package/free-field/Fields/DynamicList.js +2 -1
  12. package/free-field/Fields/File.vue +393 -0
  13. package/free-field/Fields/FileList.vue +460 -0
  14. package/free-field/Fields/FileListCombined.vue +172 -0
  15. package/free-field/Fields/FixedList.vue +4 -3
  16. package/free-field/Fields/{InputFieldList.vue → FreeFieldList.vue} +34 -11
  17. package/free-field/Fields/Image.vue +361 -0
  18. package/free-field/Fields/ImageList.vue +353 -0
  19. package/free-field/Fields/ImageListCombined.vue +108 -0
  20. package/free-field/Fields/Labels.vue +3 -5
  21. package/free-field/Fields/MixedTable.vue +2 -2
  22. package/free-field/Fields/Number.js +1 -1
  23. package/free-field/Fields/NumberRange.vue +9 -8
  24. package/free-field/Fields/Password.js +1 -1
  25. package/free-field/Fields/Permission.vue +2 -2
  26. package/free-field/Fields/PermissionEditor.vue +2 -2
  27. package/free-field/Fields/QueryFilters.vue +1 -1
  28. package/free-field/Fields/RadioList.vue +3 -3
  29. package/free-field/Fields/Rich.vue +27 -4
  30. package/free-field/Fields/Row.vue +13 -10
  31. package/free-field/Fields/Search.vue +25 -25
  32. package/free-field/Fields/Select.vue +17 -13
  33. package/free-field/Fields/SelectionChain.vue +1 -1
  34. package/free-field/Fields/Separator.js +1 -1
  35. package/free-field/Fields/SingleList.vue +1 -1
  36. package/free-field/Fields/Static.js +1 -1
  37. package/free-field/Fields/String.js +15 -3
  38. package/free-field/Fields/Tabs.vue +9 -9
  39. package/free-field/Fields/Text.js +2 -1
  40. package/free-field/Fields/Time.vue +1 -1
  41. package/free-field/Fields/TimeRange.vue +2 -2
  42. package/free-field/Fields/Year.js +1 -1
  43. package/free-field/Fields/YearRange.vue +2 -2
  44. package/free-field/Fields/index.js +20 -13
  45. package/free-field/composible/fieldWrapper.js +136 -10
  46. package/free-field/composible/useFreeField.js +17 -6
  47. package/free-field/composible/useUploader.js +320 -0
  48. package/free-field/style.scss +173 -0
  49. package/i18n/en-us/index.js +3 -0
  50. package/i18n/fields/en-us/index.js +1 -0
  51. package/i18n/fields/zh-cn/index.js +1 -0
  52. package/i18n/zh-cn/index.js +3 -0
  53. package/index.js +7 -0
  54. package/package.json +1 -1
  55. package/view/menu/index.vue +6 -6
  56. package/view/system/index.vue +1 -1
  57. package/free-field/style.sass +0 -11
@@ -0,0 +1,393 @@
1
+ <template>
2
+ <div class="row free-field-file" v-if="Field">
3
+ <span
4
+ :class="`field-label ${(Field.Label && Field.Label.trim().length)
5
+ ? '' : 'field-label-empty'} ${Field.Required ? 'required' : ''}`"
6
+ v-if="Field.Label !== void 0"
7
+ >
8
+ <q-tooltip
9
+ v-if="Field.Description"
10
+ anchor="top right"
11
+ >{{Field.Description}}</q-tooltip>
12
+ {{Field.Label || ''}}
13
+ <span
14
+ v-if="Field.Required"
15
+ class="required-mark"
16
+ >*</span>
17
+ </span>
18
+
19
+ <q-uploader
20
+ @uploaded="uploaded"
21
+ @removed="removeFile"
22
+ @rejected="filesRejected"
23
+ ref="uploader"
24
+ :factory="factoryFn"
25
+ auto-upload
26
+ :max-file-size="maxFileSize"
27
+ :accept="acceptedFileTypes"
28
+ :class="`q-ma-xs ${hasError ? 'free-field--error' : ''}`"
29
+ >
30
+ <template v-slot:list="scope">
31
+ <div
32
+ v-if="Field.Options && Field.Options.AsLink && fieldData.value?.length"
33
+ class="file-link row full-width ellipsis items-center"
34
+ >
35
+ <div
36
+ class="row ellipsis full-width"
37
+ v-for="(file, index) in fieldData.value"
38
+ :key="index"
39
+ >
40
+ <q-btn
41
+ icon="cloud_download"
42
+ dense
43
+ flat
44
+ ></q-btn>
45
+ <a
46
+ class="ellipsis"
47
+ target="_blank"
48
+ :href="$filter('serverPath', file.id)"
49
+ :download="file.name"
50
+ >
51
+ {{ file.name }}
52
+ <q-tooltip>{{ file.name }}</q-tooltip>
53
+ </a>
54
+ </div>
55
+ </div>
56
+ <div v-else>
57
+ <div
58
+ class="uploader-btns row no-wrap items-center"
59
+ v-if="!dense"
60
+ >
61
+ <q-spinner
62
+ v-if="scope.isUploading"
63
+ class="q-uploader__spinner"
64
+ />
65
+ <q-btn
66
+ v-if="!scope.isUploading && !Field.ReadOnly"
67
+ type="a"
68
+ icon="cloud_upload"
69
+ dense
70
+ flat
71
+ class="upload-btn"
72
+ label="点击上传"
73
+ >
74
+ <q-uploader-add-trigger v-if="!Field.ReadOnly" />
75
+ </q-btn>
76
+ <q-btn
77
+ v-if="scope.isUploading && !Field.ReadOnly"
78
+ icon="clear"
79
+ @click="scope.abort"
80
+ class="clear-btn"
81
+ round
82
+ dense
83
+ flat
84
+ ></q-btn>
85
+ <slot name="warning"></slot>
86
+ </div>
87
+
88
+ <q-item v-if="dense && fieldData.value?.length">
89
+ <q-item-section v-if="fieldData.value?.length && fieldData.value[0].name">
90
+ <q-item-label class="full-width ellipsis">
91
+ {{ fieldData.value[0].name }}
92
+ <q-tooltip>{{ fieldData.value[0].name }}</q-tooltip>
93
+ </q-item-label>
94
+ </q-item-section>
95
+
96
+ <q-item-section side>
97
+ <q-btn
98
+ v-if="!scope.isUploading && !Field.ReadOnly"
99
+ type="a"
100
+ icon="cloud_upload"
101
+ class="upload-btn"
102
+ dense
103
+ flat
104
+ >
105
+ <q-uploader-add-trigger v-if="!Field.ReadOnly" />
106
+ </q-btn>
107
+ </q-item-section>
108
+ </q-item>
109
+
110
+ <div
111
+ v-else-if="fieldData.value?.length"
112
+ class="file-list row items-start justify-start"
113
+ >
114
+ <q-card
115
+ flat
116
+ class="file-list-item"
117
+ v-for="(file, index) in fieldData.value"
118
+ :key="index"
119
+ >
120
+ <e-icon
121
+ class="file-image"
122
+ :name="fileThumb(file)"
123
+ thumb
124
+ :relative="filePreviewType(file) !== 'image'"
125
+ @click="preview(file)"
126
+ >
127
+ <div class="view-btn-wrapper absolute-full justify-center text-center">
128
+ <q-btn
129
+ flat
130
+ class="view-btn full-height full-width"
131
+ >查看</q-btn>
132
+ </div>
133
+ </e-icon>
134
+ <span class="file-name full-width ellipsis">
135
+ <a
136
+ v-if="file && file.id"
137
+ target="_blank"
138
+ :href="$filter('serverPath', file.id)"
139
+ :download="file.name">
140
+ {{ file.name }}
141
+ </a>
142
+ <span v-else-if="file && file.name">
143
+ {{file.name}}
144
+ </span>
145
+ <q-tooltip>{{ file.name }}</q-tooltip>
146
+ </span>
147
+
148
+ <span class="file-size full-width ellipsis">
149
+ Size: {{ file.sizeLabel || file.__sizeLabel }}
150
+ </span>
151
+
152
+ <q-btn
153
+ flat
154
+ dense
155
+ round
156
+ class="delete-btn"
157
+ icon="close"
158
+ @click="scope.removeFile(file)"
159
+ v-if="!Field.ReadOnly"
160
+ />
161
+ </q-card>
162
+ </div>
163
+ <div
164
+ class="free-field--error-tag"
165
+ v-if="hasError"
166
+ >
167
+ <e-icon name="error"></e-icon>
168
+ </div>
169
+ </div>
170
+ </template>
171
+ </q-uploader>
172
+ <q-dialog
173
+ class="image-preview-dialog"
174
+ flat
175
+ full-width
176
+ full-height
177
+ v-model="showPreview"
178
+ style="background: rgba(0,0,0,0)"
179
+ >
180
+ <div class="image-preview">
181
+ <q-icon name="close"
182
+ class="absolute cursor-pointer bg-white text-primary"
183
+ style="border-radius: 6px;border: 1px solid primary;right: 0;"
184
+ round size="20px"
185
+ @click="showPreview=false"></q-icon>
186
+ <q-img
187
+ v-if="previewType=== 'image'"
188
+ contain
189
+ :src="previewFile"
190
+ @click="showPreview=false"
191
+ style="height: 100%; max-width: 100%;"
192
+ >
193
+ </q-img>
194
+
195
+ <q-pdfviewer
196
+ v-if="previewType === 'pdf'"
197
+ v-model="showPreview"
198
+ @click="showPreview=false"
199
+ @error="pdfError"
200
+ @load="pdfLoad"
201
+ :src="previewFile"
202
+ type="pdfjs"
203
+ style="height: 100%; max-width: 100%;"
204
+ />
205
+ </div>
206
+ </q-dialog>
207
+ </div>
208
+ </template>
209
+
210
+ <script>
211
+ import { computed, defineComponent, getCurrentInstance, ref } from 'vue';
212
+ import { useFreeField, freeFieldProps } from '../composible/useFreeField';
213
+ import { useFormValidator} from '../../composible/useFormValidator';
214
+ import { useUploader } from '../composible/useUploader';
215
+
216
+ export default defineComponent({
217
+ name: 'InputFieldFile',
218
+ props: {
219
+ ...freeFieldProps,
220
+ },
221
+ fieldInfo: {
222
+ Category: 'Upload',
223
+ Label: '文件',
224
+ Value: 'File',
225
+ Extra: [
226
+ {
227
+ Type: 'String',
228
+ Label: '支持的文件类型',
229
+ Name: 'Options.Ext',
230
+ Default: 'pdf,doc,docx',
231
+ },
232
+ {
233
+ Type: 'String',
234
+ Label: '最大文件大小',
235
+ Name: 'MaxValue',
236
+ Default: '10m',
237
+ },
238
+ {
239
+ Type: 'Boolean',
240
+ Label: '紧凑样式',
241
+ Name: 'Options.Dense',
242
+ Default: false,
243
+ },
244
+ {
245
+ Type: 'Boolean',
246
+ Label: '显示为链接',
247
+ Name: 'Options.AsLink',
248
+ Default: false,
249
+ },
250
+ ],
251
+ Description: '',
252
+ },
253
+ emits: ['input'],
254
+ setup(props, { expose, emit }) {
255
+ const { proxy:vm } = getCurrentInstance();
256
+ const { fieldData, setFieldData } = useFreeField(props);
257
+ const {
258
+ showPreview,
259
+ previewType,
260
+ previewFile,
261
+ maxFileSize,
262
+ maxTotalSize,
263
+ acceptedFileTypes,
264
+ fileThumb,
265
+ filePreviewType,
266
+ filePreview,
267
+ filesRejected,
268
+ preview,
269
+ } = useUploader(props);
270
+
271
+ const uploader = ref(null);
272
+ const hasError = ref(false);
273
+
274
+ const selfValidate = () => {
275
+ if (props.Field?.Required) {
276
+ hasError.value = fieldData.value?.length <= 0;
277
+ return fieldData.value?.length > 0;
278
+ }
279
+
280
+ const rules = Array.isArray(typeof props.Field.Rules) ? props.Field.Rules : [props.Field.Rules];
281
+
282
+ let isValid = true;
283
+ for (let i = 0; i < rules.length; i += 1) {
284
+ const r = rules[i];
285
+
286
+ if (typeof r === 'function') {
287
+ isValid = isValid && r(fieldData.value);
288
+ }
289
+ }
290
+
291
+ hasError.value = !isValid;
292
+ return isValid;
293
+ };
294
+
295
+ const factoryFn = () => {
296
+ return {
297
+ url: props.Field.url || `${vm.ctx.config.baseUrl}/upload`,
298
+ fieldName: 'file',
299
+ };
300
+ };
301
+
302
+ const uploaded = (info) => {
303
+ const uploadedFiles = [];
304
+ for (let i = 0; i < info.files.length; i += 1) {
305
+ const file = info.files[i];
306
+
307
+ const { xhr } = file;
308
+ let res;
309
+ if (xhr && xhr.response) {
310
+ if (typeof xhr.response === 'string') {
311
+ //
312
+ res = JSON.parse(xhr.response);
313
+ } else if (typeof xhr.response === 'object') {
314
+ //
315
+ res = xhr.response;
316
+ } else {
317
+ //
318
+ return;
319
+ }
320
+
321
+ if (res && res.msg === 'OK') {
322
+ uploadedFiles.push({
323
+ id: res.data.id,
324
+ // eslint-disable-next-line no-underscore-dangle
325
+ sizeLabel: file.__sizeLabel,
326
+ name: file.name,
327
+ size: file.size,
328
+ type: file.type,
329
+ });
330
+ }
331
+ } else if (file.id) {
332
+ // old files
333
+ uploadedFiles.push(file);
334
+ }
335
+ }
336
+
337
+ console.log('uploadedFiles', uploadedFiles);
338
+
339
+ setFieldData(uploadedFiles, emit);
340
+ selfValidate();
341
+ }
342
+
343
+ const { validate } = useFormValidator();
344
+ expose({
345
+ validate,
346
+ });
347
+
348
+ return {
349
+ fieldData,
350
+ setFieldData,
351
+
352
+ showPreview,
353
+ previewType,
354
+ previewFile,
355
+ maxFileSize,
356
+ maxTotalSize,
357
+ acceptedFileTypes,
358
+ fileThumb,
359
+ filePreviewType,
360
+ filePreview,
361
+ filesRejected,
362
+ preview,
363
+ uploader,
364
+
365
+ hasError,
366
+ selfValidate,
367
+ factoryFn,
368
+ uploaded,
369
+ dense: computed(() => props.Field?.dense || props.Field?.Options?.Dense),
370
+ removeFile: () => {
371
+ setFieldData([], emit);
372
+ selfValidate();
373
+ },
374
+ };
375
+ },
376
+ });
377
+ </script>
378
+
379
+ <style lang="sass" scoped>
380
+ .file-link
381
+ &>div
382
+ display: inline-block
383
+ white-space: nowrap
384
+ overflow: hidden
385
+ & a
386
+ white-space: nowrap
387
+ overflow: hidden
388
+ </style>
389
+
390
+ <style lang="sass">
391
+ .q-uploader__header
392
+ display: none
393
+ </style>