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
@@ -1,6 +1,23 @@
1
1
  <template>
2
- <div class="input-field-list">
2
+ <div class="free-field-list row full-width">
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
+
3
19
  <dynamic-list
20
+ class="col"
4
21
  :Field="localField"
5
22
  :values="values"
6
23
  readonly
@@ -79,9 +96,10 @@ const clipBoardStore = {
79
96
  };
80
97
 
81
98
  export default defineComponent({
82
- name: 'InputFieldList',
99
+ name: 'FreeFieldList',
83
100
  emits:['save', 'delete', 'cancel','paste:fields', 'batch:fields'],
84
101
  fieldInfo: {
102
+ DataType: 'Array',
85
103
  Category: 'Advanced',
86
104
  Label: '字段列表',
87
105
  Value: 'FieldList',
@@ -103,15 +121,20 @@ export default defineComponent({
103
121
  const changedFields = ref([]);
104
122
  const fieldList = ref(null);
105
123
 
106
- const editingFieldField = ref(undefined);
124
+ const editingFieldField = computed(() => ({
125
+ Type: 'FieldEditor',
126
+ Name: '.',
127
+ show: showEditor.value,
128
+ }));
107
129
 
108
- watchEffect(() => {
109
- editingFieldField.value = {
110
- Type: 'FieldEditor',
111
- Name: '.',
112
- show: showEditor.value,
113
- };
114
- });
130
+ // watchEffect(() => {
131
+ // });
132
+
133
+ // editingFieldField.value = {
134
+ // Type: 'FieldEditor',
135
+ // Name: '.',
136
+ // show: showEditor.value,
137
+ // };
115
138
 
116
139
  const localField = computed(() => ({
117
140
  Type: 'DynamicList',
@@ -166,7 +189,7 @@ export default defineComponent({
166
189
 
167
190
  if (fData?.length) {
168
191
  newIndex = fData.map((fd) => fd.Index).sort((a,b) => a.Index-b.Index).filter((fd) => !!fd).pop();
169
- newIndex = (newIndex || 0) + 1;
192
+ newIndex = (Number(newIndex) || 0) + 1;
170
193
  }
171
194
 
172
195
  editingField.value = {
@@ -0,0 +1,361 @@
1
+ <template>
2
+ <div class="row free-field-image" 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 && !Field.dense">
7
+ <q-tooltip v-if="Field.Description" anchor="top right">
8
+ {{
9
+ Field.Description
10
+ }}
11
+ </q-tooltip>
12
+ {{ Field.Label || '' }}
13
+ <span v-if="Field.Required" class="required-mark">*</span>
14
+ </span>
15
+ <q-uploader
16
+ @uploaded="uploaded"
17
+ @removed="removeFile"
18
+ @rejected="filesRejected"
19
+ ref="uploader"
20
+ :factory="factoryFn"
21
+ auto-upload
22
+ :max-file-size="maxFileSize"
23
+ :class="(Field.dense ?
24
+ `no-shadow dense` : `${Field.onlyIcon ? 'only-icon' : ''}`)
25
+ + (hasError ? ' free-field--error' : '')"
26
+ >
27
+ <template v-slot:list="scope">
28
+ <div class="uploader-btns row no-wrap items-center" v-if="!Field.dense && !(Field.onlyIcon || onlyIcon)">
29
+ <q-spinner v-if="scope.isUploading" class="q-uploader__spinner" />
30
+ <q-btn
31
+ v-if="!scope.isUploading"
32
+ type="a"
33
+ icon="cloud_upload"
34
+ dense
35
+ flat
36
+ class="upload-btn"
37
+ label="点击上传"
38
+ :disabled="Field.ReadOnly"
39
+ >
40
+ <q-uploader-add-trigger v-if="!Field.ReadOnly" />
41
+ </q-btn>
42
+ <q-btn
43
+ v-if="scope.isUploading"
44
+ icon="clear"
45
+ @click="scope.abort"
46
+ round
47
+ class="clear-btn"
48
+ dense
49
+ flat
50
+ :disabled="Field.ReadOnly"
51
+ ></q-btn>
52
+ <slot name="warning"></slot>
53
+ </div>
54
+
55
+ <q-btn
56
+ v-if="(Field.onlyIcon || onlyIcon) && (!fieldData.value || !fieldData.value[0] || !fieldData.value[0].id)"
57
+ type="a"
58
+ :icon="fieldData?.value?.length ? 'check' : 'cloud_upload'"
59
+ dense
60
+ flat
61
+ :disabled="Field.ReadOnly"
62
+ >
63
+ <q-uploader-add-trigger />
64
+ </q-btn>
65
+
66
+ <div v-else-if="(Field.onlyIcon || onlyIcon) && fieldData.value && fieldData.value[0]?.id">
67
+ <q-img
68
+ :src="$filter('serverThumb', `${fieldData.value[0].id}`)"
69
+ style="width:32px; max-height:32px;min-height:32px;"
70
+ >
71
+ </q-img>
72
+ <q-uploader-add-trigger/>
73
+ </div>
74
+
75
+ <q-item v-else-if="Field.dense" class="items-center q-pa-none">
76
+ <q-item-section v-if="fieldData?.value?.length && fieldData.value[0].id" thumbnail>
77
+ <q-img
78
+ :src="`${ctx.config.thumbUrlBase}${fieldData.value[0].id}`"
79
+ style="width: 48px; max-height: 48px;"
80
+ >
81
+ </q-img>
82
+ <q-uploader-add-trigger v-if="!Field.ReadOnly" />
83
+ </q-item-section>
84
+
85
+ <q-item-section v-if="fieldData.value?.length && fieldData.value[0].__img" thumbnail class="gt-xs">
86
+ <q-img :src="fieldData.value[0].__img.src">
87
+ </q-img>
88
+ <q-uploader-add-trigger v-if="!Field.ReadOnly" />
89
+ </q-item-section>
90
+
91
+ <q-item-section v-if="!fieldData.value?.length">
92
+ <q-btn
93
+ v-if="!scope.isUploading"
94
+ type="a"
95
+ icon="cloud_upload"
96
+ class="upload-btn"
97
+ dense
98
+ flat
99
+ :disabled="Field.ReadOnly"
100
+ >
101
+ <q-uploader-add-trigger v-if="!Field.ReadOnly" />
102
+ </q-btn>
103
+ </q-item-section>
104
+ </q-item>
105
+
106
+ <div v-else-if="fieldData.value?.length" class="file-list row items-start justify-start">
107
+ <q-card
108
+ flat
109
+ class="file-list-item"
110
+ v-for="(file, index) in fieldData.value" :key="index">
111
+ <e-icon class="file-image" :name="fileThumb(file)" thumb
112
+ :relative="filePreviewType(file) !== 'image'"
113
+ @click="preview(file)">
114
+ <div class="view-btn-wrapper absolute-full justify-center text-center">
115
+ <q-btn
116
+ flat
117
+ class="view-btn full-height full-width"
118
+ @click="preview(file)"
119
+ >查看</q-btn>
120
+ </div>
121
+ </e-icon>
122
+ <span class="file-name full-width ellipsis">
123
+ <a
124
+ v-if="file && file.id"
125
+ target="_blank"
126
+ :href="$filter('serverPath', file.id)"
127
+ :download="file.name">
128
+ {{ file.name }}
129
+ </a>
130
+ <span v-else-if="file && file.name">
131
+ {{file.name}}
132
+ </span>
133
+ <q-tooltip>{{ file.name }}</q-tooltip>
134
+ </span>
135
+
136
+ <span class="file-size full-width ellipsis">
137
+ Size: {{ file.sizeLabel || file.__sizeLabel }}
138
+ </span>
139
+
140
+ <q-btn
141
+ flat
142
+ dense
143
+ round
144
+ class="delete-btn"
145
+ icon="close"
146
+ @click="scope.removeFile(file)"
147
+ :disabled="Field.ReadOnly"
148
+ />
149
+ </q-card>
150
+ </div>
151
+
152
+ <div class="free-field--error-tag" v-if="hasError">
153
+ <e-icon name="error"></e-icon>
154
+ </div>
155
+ </template>
156
+ </q-uploader>
157
+ <q-dialog class="image-preview-dialog"
158
+ flat
159
+ full-width full-height v-model="showPreview"
160
+ style="background: rgba(0,0,0,0)">
161
+ <div class="image-preview">
162
+ <q-icon name="close"
163
+ class="absolute cursor-pointer bg-white text-primary"
164
+ style="border-radius: 6px;border: 1px solid primary;right: 0;"
165
+ round size="20px"
166
+ @click="showPreview=false"></q-icon>
167
+ <q-img
168
+ v-if="previewType === 'image'"
169
+ contain :src="previewFile"
170
+ @click="showPreview=false"
171
+ style="max-height: 100%; max-width: 100%;">
172
+ </q-img>
173
+
174
+ <q-pdfviewer
175
+ v-if="previewType === 'pdf'"
176
+ v-model="showPreview"
177
+ @click="showPreview=false"
178
+ :src="previewFile"
179
+ type="pdfjs"
180
+ style="height: 100%; max-width: 100%;"
181
+ />
182
+ </div>
183
+ </q-dialog>
184
+ </div>
185
+ </template>
186
+
187
+ <script>
188
+ import { defineComponent, getCurrentInstance, ref } from 'vue';
189
+ import { useFreeField, freeFieldProps } from '../composible/useFreeField';
190
+ import { useFormValidator} from '../../composible/useFormValidator';
191
+ import { useUploader } from '../composible/useUploader';
192
+
193
+ export default defineComponent({
194
+ name: 'InputFieldImage',
195
+ fieldInfo: {
196
+ Category: 'Upload',
197
+ Label: '图片',
198
+ Value: 'Image',
199
+ Extra: [
200
+ {
201
+ Type: 'String',
202
+ Label: '支持的文件类型',
203
+ Name: 'Options.Ext',
204
+ Default: 'jpg,png,bmp',
205
+ },
206
+ {
207
+ Type: 'String',
208
+ Label: '最大文件大小',
209
+ Name: 'MaxValue',
210
+ Default: '10m',
211
+ },
212
+ ],
213
+ Description: '',
214
+ },
215
+ props: {
216
+ ...freeFieldProps,
217
+ dense: { type: Boolean, default: false },
218
+ onlyIcon: { type: Boolean, default: false },
219
+ },
220
+ emits: ['input'],
221
+ setup(props, { expose, emit }) {
222
+ const { proxy:vm } = getCurrentInstance();
223
+ const { fieldData, setFieldData } = useFreeField(props);
224
+ const {
225
+ showPreview,
226
+ previewType,
227
+ previewFile,
228
+ maxFileSize,
229
+ maxTotalSize,
230
+ acceptedFileTypes,
231
+ fileThumb,
232
+ filePreviewType,
233
+ filePreview,
234
+ filesRejected,
235
+ preview,
236
+ } = useUploader(props);
237
+
238
+ const uploader = ref(null);
239
+ const hasError = ref(false);
240
+
241
+ const selfValidate = () => {
242
+ if (props.Field?.Required) {
243
+ hasError.value = fieldData.value?.length <= 0;
244
+ return fieldData.value?.length > 0;
245
+ }
246
+
247
+ const rules = Array.isArray(typeof props.Field.Rules) ? props.Field.Rules : [props.Field.Rules];
248
+
249
+ let isValid = true;
250
+ for (let i = 0; i < rules.length; i += 1) {
251
+ const r = rules[i];
252
+
253
+ if (typeof r === 'function') {
254
+ isValid = isValid && r(fieldData.value);
255
+ }
256
+ }
257
+
258
+ hasError.value = !isValid;
259
+ return isValid;
260
+ };
261
+
262
+ const factoryFn = () => {
263
+ return {
264
+ url: props.Field.url || `${vm.ctx.config.baseUrl}/upload`,
265
+ fieldName: 'file',
266
+ };
267
+ };
268
+
269
+ const uploaded = (info) => {
270
+ const uploadedFiles = [];
271
+ for (let i = 0; i < info.files.length; i += 1) {
272
+ const file = info.files[i];
273
+
274
+ const { xhr } = file;
275
+ let res;
276
+ if (xhr && xhr.response) {
277
+ if (typeof xhr.response === 'string') {
278
+ //
279
+ res = JSON.parse(xhr.response);
280
+ } else if (typeof xhr.response === 'object') {
281
+ //
282
+ res = xhr.response;
283
+ } else {
284
+ //
285
+ return;
286
+ }
287
+
288
+ if (res && res.msg === 'OK') {
289
+ uploadedFiles.push({
290
+ id: res.data.id,
291
+ // eslint-disable-next-line no-underscore-dangle
292
+ sizeLabel: file.__sizeLabel,
293
+ name: file.name,
294
+ size: file.size,
295
+ type: file.type,
296
+ });
297
+ }
298
+ } else if (file.id) {
299
+ // old files
300
+ uploadedFiles.push(file);
301
+ }
302
+ }
303
+
304
+ setFieldData(uploadedFiles, emit);
305
+ selfValidate();
306
+ }
307
+
308
+ const { validate } = useFormValidator();
309
+ expose({
310
+ validate,
311
+ });
312
+
313
+ return {
314
+ fieldData,
315
+ setFieldData,
316
+
317
+ maxFileSize,
318
+ maxTotalSize,
319
+ acceptedFileTypes,
320
+ fileThumb,
321
+ filePreviewType,
322
+ filePreview,
323
+ filesRejected,
324
+ preview,
325
+ showPreview,
326
+ previewType,
327
+ previewFile,
328
+ uploader,
329
+
330
+ hasError,
331
+ selfValidate,
332
+ factoryFn,
333
+ uploaded,
334
+
335
+ removeFile: () => {
336
+ setFieldData([], emit);
337
+ selfValidate();
338
+ },
339
+ };
340
+ },
341
+ // methods: {
342
+ // fileAdded(files) {
343
+ // this.$refs.uploader.files = files;
344
+ // },
345
+ // },
346
+ });
347
+ </script>
348
+
349
+ <style lang="sass">
350
+ .free-field-image
351
+ .only-icon
352
+ text-align: center
353
+ width: 32px
354
+ .q-btn
355
+ max-width: 32px
356
+ .q-uploader__header
357
+ display: none
358
+ .q-uploader__list
359
+ min-height: 32px
360
+ padding: 0
361
+ </style>