11<template >
2- <span v-if =" field.show" >
3- <!-- text field: input / number / decimal / date / time / datetime -->
4- <v-text-field
5- v-if =" ['input', 'number', 'decimal', 'time', 'datetime'].includes(fieldType)"
2+ <div
3+ v-if =" field.show"
4+ :class =" {'field--limited-width': isWidthLimited}"
5+ >
6+ <component
67 v-model =" value"
7- :type =" ['number', 'decimal'].includes(fieldType) ? 'number' : 'text'"
8- :label =" field.text"
9- :disabled =" field.disabled"
8+ :is =" fieldComponent"
9+ :field-type =" fieldType"
10+ :field =" field"
11+ :reload =" reload"
1012 :rules =" fieldRules(field)"
11- :step =" fieldType == 'decimal' ? 0.01 : 1"
12- :mask =" ['date', 'time', 'datetime'].includes(fieldType) ? masks[fieldType] : undefined"
13- :return-masked-value =" ['date', 'time', 'datetime'].includes(fieldType) ? true : false"
14- min =" 0"
15- class =" field--limited-width"
16- hide-details
17- @blur =" valueChanged()"
18- ></v-text-field >
19- <!-- date -->
20- <v-menu
21- v-else-if =" fieldType == 'date'"
22- v-model =" datepicker"
23- :close-on-content-click =" false"
24- :nudge-right =" 40"
25- lazy
26- transition =" scale-transition"
27- offset-y
28- full-width
29- min-width =" 290px"
30- >
31- <template v-slot :activator =" { on } " >
32- <v-text-field
33- v-model =" value"
34- :label =" field.text"
35- prepend-icon =" event"
36- readonly
37- v-on =" on"
38- class =" field--limited-width"
39- @blur =" valueChanged()"
40- ></v-text-field >
41- </template >
42- <v-date-picker
43- v-model =" value"
44- @input =" datepicker = false"
45- :first-day-of-week =" firstDayOfWeek"
46- :locale =" locale"
47- scrollable
48- @change =" valueChanged()"
49- ></v-date-picker >
50- </v-menu >
51- <!-- text area -->
52- <v-textarea
53- hide-details
54- :rules =" fieldRules(field)"
55- v-else-if =" fieldType == 'textarea'"
56- :label =" field.text"
57- v-model =" value"
58- :disabled =" field.disabled"
59- @blur =" valueChanged()"
60- ></v-textarea >
61- <!-- file upload -->
62- <field-wrapper
63- v-else-if =" fieldType == 'file'"
64- class =" field--limited-width"
65- >
66- <v-row dense >
67- <v-col cols =" 12" sm =" 5" >
68- <v-btn
69- :loading =" uploadLoader"
70- :class =" fileUploadBtn(uploadStatus)"
71- class =" jbtn-file"
72- dark
73- >
74- {{ $t('global.details.files.upload') }}
75- <v-icon dark right >{{ fileUploadIcon(uploadStatus) }}</v-icon >
76- <input
77- :id =" field.name"
78- :multiple =" false"
79- :disabled =" field.disabled"
80- type =" file"
81- accept =" *"
82- ref =" fileInput"
83- @change =" fileSelected($event, field)"
84- />
85- </v-btn >
86- </v-col >
87- <v-col cols =" 12" sm =" 7" >
88- <v-text-field
89- hide-details
90- :rules =" fieldRules(field)"
91- :label =" field.text"
92- :value =" filename"
93- disabled
94- ></v-text-field >
95- </v-col >
96- </v-row >
97- </field-wrapper >
98- <!-- select -->
99- <template v-else-if =" fieldType == ' select' " >
100- <select-field
101- v-model =" value"
102- :rules =" fieldRules(field)"
103- :label =" field.text"
104- :disabled =" field.disabled"
105- :options =" field.list"
106- :async =" field.async"
107- :url =" field.url"
108- @change =" valueChanged()"
109- />
110- </template >
111- <!-- rich text editor -->
112- <field-wrapper
113- v-else-if =" fieldType == 'richTextBox'"
114- :label =" field.text"
115- >
116- <rich-text-box-field
117- v-model =" value"
118- :disabled =" field.disabled"
119- :available-extensions =" field.richTextBoxOperations"
120- @change =" valueChanged()"
121- />
122- </field-wrapper >
123- <!-- checkbox -->
124- <v-checkbox
125- v-else-if =" fieldType == 'checkbox'"
126- hide-details
127- color =" primary"
128- v-model =" value"
129- :label =" field.text"
130- :disabled =" field.disabled"
131- @change =" checkboxUpdated()"
132- ></v-checkbox >
133- </span >
13+ />
14+ </div >
13415</template >
13516<script >
136- import crud from ' @/config/crud'
137-
138- import FieldWrapper from ' ./ItemDetailsFieldWrapper.vue'
139-
140- import RichTextBoxField from ' ./field-types/RichTextBox.vue'
141- import SelectField from ' ./field-types/Select.vue'
142-
143- import {
144- mapState ,
145- mapGetters ,
146- } from ' vuex'
14717
14818export default {
14919 name: ' ItemDetailsField' ,
150- components: {
151- FieldWrapper,
152- RichTextBoxField,
153- SelectField,
154- },
15520 props: [
15621 ' field' ,
15722 ' fieldValue' ,
@@ -161,102 +26,46 @@ export default {
16126 data () {
16227 return {
16328 value: null ,
164- uploadStatus: ' ready' ,
165- uploadLoader: false ,
166- masks: {
167- date: ' ####-##-##' ,
168- time: ' ##:##' ,
169- datetime: ' ####-##-## ##:##:##' ,
29+ isEmitLocked: false ,
30+ componentsMap: {
31+ input: ' Text' ,
32+ number: ' Text' ,
33+ decimal: ' Text' ,
34+ time: ' Text' ,
35+ datetime: ' Text' ,
36+ date: ' Date' ,
37+ textarea: ' Textarea' ,
38+ file: ' File' ,
39+ richTextBox: ' RichTextBox' ,
40+ select: ' Select' ,
41+ checkbox: ' Checkbox' ,
17042 },
171- datepicker: false ,
17243 }
17344 },
174- watch: {
175- fieldValue: {
176- immediate: true ,
177- handler (val ) {
178- this .value = val
179- },
180- },
181- reload : function (val ) {
182- if (val) {
183- if (this .fieldType === ' file' ) {
184- this .uploadLoader = false
185- this .uploadStatus = ' ready'
186- } else if (this .fieldType === ' select' && this .field .async ) {
187- this .listOldSearch = ' '
188- let val = this .value || ' '
189- const url = ` ${ this .field .url } /id/${ val} `
190- this .refreshList (url)
191- }
192- }
193- },
194- uploadStatus : function (val ) {
195- if (val !== ' ready' ) {
196- setTimeout (() => {
197- this .uploadStatus = ' ready'
198- }, 1000 )
199- }
200- },
201- },
20245 computed: {
203- ... mapState (' crud' , [
204- ' details' ,
205- ' path' ,
206- ' prefix' ,
207- ]),
208- ... mapGetters (' crud' , [' uploadPath' ]),
20946 fieldType () {
21047 return this .field .type === ' dynamic' ? this .dynamicFieldType : this .field .type
21148 },
49+ componentName () {
50+ return this .componentsMap [this .fieldType ]
51+ },
52+ fieldComponent () {
53+ return this .componentName ? () => import (` ./field-types/${ this .componentName } .vue` ) : undefined
54+ },
55+ isWidthLimited () {
56+ return ! [
57+ ' richTextBox' ,
58+ ' textarea' ,
59+ ].includes (this .fieldType )
60+ },
21261 rules () {
21362 const self = this
21463 return {
21564 required : (v ) => !! v || self .$t (' global.details.rules.required' ),
21665 }
21766 },
218- filename () {
219- let filename
220- if (this .fieldType === ' file' && this .value !== undefined && this .value !== null ) {
221- try {
222- filename = JSON .parse (this .value ).filename
223- } catch (e) {
224- return ' '
225- }
226- }
227- return filename
228- },
229- locale () {
230- return crud .locale || ' en-us'
231- },
232- firstDayOfWeek () {
233- return crud .firstDayOfWeek || 0
234- },
23567 },
23668 methods: {
237- checkboxUpdated () {
238- this .value = this .value ? 1 : 0
239- this .valueChanged ()
240- },
241- valueChanged () {
242- this .$emit (' valueChanged' , this .value , this .field .column )
243- },
244- fileUploadBtn (status ) {
245- const btnClasses = {
246- ready: ' primary' ,
247- success: ' success' ,
248- error: ' error' ,
249- }
250- return btnClasses[status]
251- },
252- fileUploadIcon (status ) {
253- const icons = {
254- ready: ' save_alt' ,
255- success: ' check' ,
256- error: ' close' ,
257- }
258- return icons[status]
259- },
26069 fieldRules (field ) {
26170 const rules = []
26271 const required = field .required !== undefined ? field .required : true
@@ -265,49 +74,22 @@ export default {
26574 }
26675 return rules
26776 },
268- fileSelected (e , field ) {
269- const file = e .target .files [0 ]
270- if (file) {
271- this .uploadLoader = true
272- const formData = new FormData ()
273- formData .append (' file' , file)
274- formData .append (' module' , this .prefix )
275- formData .append (' table' , this .path )
276- formData .append (' field' , field .column )
277- this .$http .post (this .uploadPath , formData, {}).then (
278- (response ) => {
279- if (response .body .status === 0 ) {
280- this .value = JSON .stringify ({
281- filename: file .name ,
282- mime: file .type ,
283- size: file .size ,
284- path: response .body .path ,
285- uploaded: 1 ,
286- })
287- this .valueChanged ()
288- this .uploadStatus = ' success'
289- } else {
290- this .uploadStatus = ' error'
291- if (response .body .status === - 1 ) {
292- this .openAlertBox ([
293- ' alertError' ,
294- response .body .msg ,
295- ])
296- } else if (response .body .status === - 2 ) {
297- this .openAlertBox ([
298- ' alertValidationError' ,
299- response .body .msg ,
300- ])
301- }
302- }
303- this .uploadLoader = false
304- },
305- () => {
306- this .uploadLoader = false
307- this .uploadStatus = ' error'
308- },
309- )
310- }
77+ },
78+ watch: {
79+ fieldValue: {
80+ immediate: true ,
81+ handler (val ) {
82+ this .isEmitLocked = true
83+ this .value = val
84+ this .isEmitLocked = false
85+ },
86+ },
87+ value: {
88+ handler (val ) {
89+ if (! this .isEmitLocked ) {
90+ this .$emit (' valueChanged' , val, this .field .column )
91+ }
92+ },
31193 },
31294 },
31395}
@@ -319,25 +101,4 @@ export default {
319101 max-width : 600px ;
320102 }
321103}
322- .jbtn-file {
323- cursor : pointer ;
324- position : relative ;
325- overflow : hidden ;
326- margin : 0px ;
327- width : 100% ;
328- }
329- .jbtn-file input [type = ' file' ] {
330- position : absolute ;
331- top : 0 ;
332- right : 0 ;
333- min-width : 100% ;
334- min-height : 100% ;
335- font-size : 100px ;
336- text-align : right ;
337- filter : alpha (opacity =0 );
338- opacity : 0 ;
339- outline : none ;
340- cursor : inherit ;
341- display : block ;
342- }
343104 </style >
0 commit comments