Skip to content

Commit acd69d6

Browse files
committed
#29 destructurize item details field
1 parent a6a21bb commit acd69d6

File tree

3 files changed

+97
-346
lines changed

3 files changed

+97
-346
lines changed
Lines changed: 52 additions & 291 deletions
Original file line numberDiff line numberDiff line change
@@ -1,157 +1,22 @@
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
14818
export 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

Comments
 (0)