waibu-db 2.14.0 → 2.16.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.
- package/extend/bajo/intl/en-US.json +2 -0
- package/extend/bajo/intl/id.json +2 -0
- package/extend/bajoTemplate/partial/crud/_details-btns.html +10 -10
- package/extend/bajoTemplate/partial/crud/_edit-btns.html +11 -11
- package/extend/bajoTemplate/partial/crud/_list-attachment.html +14 -8
- package/extend/bajoTemplate/partial/crud/_list-btns.html +2 -2
- package/extend/waibuBootstrap/theme/component/wdb-base.js +21 -5
- package/extend/waibuBootstrap/theme/component/widget/btn-columns.js +16 -6
- package/extend/waibuBootstrap/theme/component/widget/btn-export.js +2 -2
- package/extend/waibuBootstrap/theme/component/widget/form.js +75 -30
- package/extend/waibuBootstrap/theme/component/widget/lookup-select.js +6 -8
- package/extend/waibuBootstrap/theme/component/widget/query.js +5 -1
- package/extend/waibuBootstrap/theme/component/widget/table.js +32 -39
- package/index.js +6 -1
- package/lib/crud/add-handler.js +2 -2
- package/lib/crud/all-handler.js +3 -0
- package/lib/crud/edit-handler.js +8 -3
- package/lib/method/get-schema-ext.js +6 -4
- package/lib/method/update-record.js +4 -2
- package/lib/util.js +3 -2
- package/package.json +1 -1
- package/wiki/CHANGES.md +19 -2
|
@@ -38,6 +38,8 @@
|
|
|
38
38
|
"ndjson": "NDJSON",
|
|
39
39
|
"dataValue": "Data value",
|
|
40
40
|
"suppressedError": "Error occured and suppressed. Please check error log for details",
|
|
41
|
+
"queryHint%s": "Search by %s",
|
|
42
|
+
"noAttachmentFound": "No attachment found",
|
|
41
43
|
"op": {
|
|
42
44
|
"eq": "=",
|
|
43
45
|
"neq": "≠",
|
package/extend/bajo/intl/id.json
CHANGED
|
@@ -38,6 +38,8 @@
|
|
|
38
38
|
"ndjson": "NDJSON",
|
|
39
39
|
"dataValue": "Nilai data",
|
|
40
40
|
"suppressedError": "Kesalahan terjadi dan tidak ditampilkan. Silahkan cek log kesalahan untuk detilnya",
|
|
41
|
+
"queryHint%s": "Cari berdasarkan %s",
|
|
42
|
+
"noAttachmentFound": "Tidak ada lampiran ditemukan",
|
|
41
43
|
"op": {
|
|
42
44
|
"eq": "=",
|
|
43
45
|
"neq": "≠",
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
<c:grid-row gutter="2">
|
|
2
2
|
<c:grid-col col="6-lg">
|
|
3
|
-
<% if (!
|
|
4
|
-
<c:wdb-btn-back href="<%=
|
|
3
|
+
<% if (!_getSetting('waibuDb:/control/wdbBtnBack/disabled')) { %>
|
|
4
|
+
<c:wdb-btn-back href="<%= _getSetting('waibuDb:/control/wdbBtnBack/href', 'undefined') %>" />
|
|
5
5
|
<% } %>
|
|
6
6
|
<% if (schema.disabled.includes('remove') && schema.disabled.includes('update')) { %>
|
|
7
|
-
<% if (!
|
|
7
|
+
<% if (!_getSetting('waibuDb:/control/wdbBtnExport/disabled')) { %>
|
|
8
8
|
<c:wdb-btn-export selector="#main-form" handler="details" launch-margin="start-1" />
|
|
9
9
|
<% } %>
|
|
10
10
|
<% } else { %>
|
|
11
11
|
<c:btn-group margin="start-1">
|
|
12
|
-
<% if (!
|
|
13
|
-
<c:wdb-btn-edit href="<%=
|
|
12
|
+
<% if (!_getSetting('waibuDb:/control/wdbBtnEdit/disabled')) { %>
|
|
13
|
+
<c:wdb-btn-edit href="<%= _getSetting('waibuDb:/control/wdbBtnEdit/href', 'undefined') %>" />
|
|
14
14
|
<% } %>
|
|
15
|
-
<% if (!
|
|
16
|
-
<c:wdb-btn-clone href="<%=
|
|
15
|
+
<% if (!_getSetting('waibuDb:/control/wdbBtnClone/disabled')) { %>
|
|
16
|
+
<c:wdb-btn-clone href="<%= _getSetting('waibuDb:/control/wdbBtnClone/href', 'undefined') %>" />
|
|
17
17
|
<% } %>
|
|
18
|
-
<% if (!
|
|
18
|
+
<% if (!_getSetting('waibuDb:/control/wdbBtnExport/disabled')) { %>
|
|
19
19
|
<c:wdb-btn-export selector="#main-form" handler="details" launch-on-end no-save/>
|
|
20
20
|
<% } %>
|
|
21
21
|
</c:btn-group>
|
|
22
22
|
<% } %>
|
|
23
23
|
</c:grid-col>
|
|
24
24
|
<c:grid-col col="6-lg" flex="justify-content:end-lg align-items:center">
|
|
25
|
-
<% if (!
|
|
26
|
-
<c:wdb-btn-delete href="<%=
|
|
25
|
+
<% if (!_getSetting('waibuDb:/control/wdbBtnDelete/disabled')) { %>
|
|
26
|
+
<c:wdb-btn-delete href="<%= _getSetting('waibuDb:/control/wdbBtnDelete/href', 'undefined') %>" />
|
|
27
27
|
<% } %>
|
|
28
28
|
</c:grid-col>
|
|
29
29
|
</c:grid-row>
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
<c:grid-row gutter="2">
|
|
2
2
|
<c:grid-col col="6-lg">
|
|
3
|
-
<% if (!
|
|
4
|
-
<c:wdb-btn-back href="<%=
|
|
3
|
+
<% if (!_getSetting('waibuDb:/control/wdbBtnBack/disabled')) { %>
|
|
4
|
+
<c:wdb-btn-back href="<%= _getSetting('waibuDb:/control/wdbBtnBack/href', 'undefined') %>"/>
|
|
5
5
|
<% } %>
|
|
6
6
|
<c:btn-group margin="start-1">
|
|
7
|
-
<% if (!
|
|
8
|
-
<c:wdb-btn-details href="<%=
|
|
7
|
+
<% if (!_getSetting('waibuDb:/control/wdbBtnDetails/disabled')) { %>
|
|
8
|
+
<c:wdb-btn-details href="<%= _getSetting('waibuDb:/control/wdbBtnDetails/href', 'undefined') %>" />
|
|
9
9
|
<% } %>
|
|
10
|
-
<% if (!
|
|
11
|
-
<c:wdb-btn-clone href="<%=
|
|
10
|
+
<% if (!_getSetting('waibuDb:/control/wdbBtnClone/disabled')) { %>
|
|
11
|
+
<c:wdb-btn-clone href="<%= _getSetting('waibuDb:/control/wdbBtnClone/href', 'undefined') %>" />
|
|
12
12
|
<% } %>
|
|
13
|
-
<% if (!
|
|
13
|
+
<% if (!_getSetting('waibuDb:/control/wdbBtnExport/disabled')) { %>
|
|
14
14
|
<c:wdb-btn-export selector="#main-form" handler="edit" launch-on-end no-save />
|
|
15
15
|
<% } %>
|
|
16
16
|
</c:btn-group>
|
|
17
17
|
</c:grid-col>
|
|
18
18
|
<c:grid-col col="6-lg" flex="justify-content:end-lg align-items:center">
|
|
19
|
-
<% if (!
|
|
20
|
-
<c:wdb-btn-delete href="<%=
|
|
19
|
+
<% if (!_getSetting('waibuDb:/control/wdbBtnDelete/disabled')) { %>
|
|
20
|
+
<c:wdb-btn-delete href="<%= _getSetting('waibuDb:/control/wdbBtnDelete/href', 'undefined') %>"/>
|
|
21
21
|
<% } %>
|
|
22
|
-
<% if (!
|
|
22
|
+
<% if (!_getSetting('waibuDb:/control/wdbBtnReset/disabled')) { %>
|
|
23
23
|
<c:btn type="reset" color="secondary" t:content="reset" margin="start-2"/>
|
|
24
24
|
<% } %>
|
|
25
|
-
<% if (!
|
|
25
|
+
<% if (!_getSetting('waibuDb:/control/wdbBtnSubmit/disabled')) { %>
|
|
26
26
|
<c:btn type="submit" color="primary" t:content="submit" margin="start-2"/>
|
|
27
27
|
<% } %>
|
|
28
28
|
</c:grid-col>
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
<%
|
|
2
|
-
<c:grid-col col="
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
<% if (attachments.length === 0) { %>
|
|
2
|
+
<c:grid-col col="12">
|
|
3
|
+
<c:alert color="warning" t:content="noAttachmentFound" margin="bottom-1" />
|
|
4
|
+
</c:grid-col>
|
|
5
|
+
<% } else { %>
|
|
6
|
+
<% for (const att of attachments) { %>
|
|
7
|
+
<c:grid-col col="4-lg 6-md">
|
|
8
|
+
<% if (attr.readonly) { %>
|
|
9
|
+
<a href="<%= decodeURI(att.url) %>" target="_blank"><%= att.fileName %></a>
|
|
10
|
+
<% } else { %>
|
|
11
|
+
<c:form-check x-model="selected" value="<%= att.fullPath %>" label="<a href='<%= att.url %>' target='_blank'><%= att.fileName %></a>" />
|
|
12
|
+
<% } %>
|
|
13
|
+
</c:grid-col>
|
|
7
14
|
<% } %>
|
|
8
|
-
|
|
9
|
-
<% } %>
|
|
15
|
+
<% } %>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<c:grid-row gutter="3">
|
|
2
|
-
<c:grid-col col="
|
|
2
|
+
<c:grid-col col="5-lg">
|
|
3
3
|
<c:wdb-query />
|
|
4
4
|
</c:grid-col>
|
|
5
|
-
<c:grid-col col="
|
|
5
|
+
<c:grid-col col="7-lg" flex="justify-content:end-lg">
|
|
6
6
|
<c:btn-group margin="end-2">
|
|
7
7
|
<c:wdb-btn-add text="nowrap"/>
|
|
8
8
|
<c:wdb-btn-details on-list text="nowrap"/>
|
|
@@ -6,15 +6,16 @@ async function wdbBase () {
|
|
|
6
6
|
const { get } = this.app.lib._
|
|
7
7
|
this.schema = get(this, 'component.locals.schema', {})
|
|
8
8
|
this.formData = get(this, 'component.locals.form', {})
|
|
9
|
-
this.
|
|
9
|
+
this.oldData = get(this, 'component.locals.oldData', {})
|
|
10
|
+
this.model = getModel(this.schema.name, true)
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
getRef = ({ field, refName, returning } = {}) => {
|
|
13
14
|
const { get } = this.app.lib._
|
|
14
|
-
if (!this.
|
|
15
|
+
if (!this.model) return {}
|
|
15
16
|
const prop = this.model.getProperty(field)
|
|
16
17
|
if (!prop) return {}
|
|
17
|
-
|
|
18
|
+
if (!refName && field.endsWith('Id')) refName = field.slice(0, -2)
|
|
18
19
|
const key = this.params.attr.refName ?? refName
|
|
19
20
|
const ref = get(prop, `ref.${key}`, {})
|
|
20
21
|
if (returning === 'all') return { ref, key }
|
|
@@ -23,11 +24,26 @@ async function wdbBase () {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
getRefValue = ({ field, data, labelField, refName } = {}) => {
|
|
26
|
-
const { isEmpty } = this.app.lib._
|
|
27
|
+
const { get, isEmpty } = this.app.lib._
|
|
27
28
|
const { ref, key } = this.getRef({ field, refName, returning: 'all' })
|
|
28
29
|
if (isEmpty(ref)) return undefined
|
|
29
30
|
labelField = labelField ?? ref.labelField ?? 'id'
|
|
30
|
-
return (data ?? this.formData
|
|
31
|
+
return (get(data ?? this.formData, `_ref.${key}.${labelField}`))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getRefName = (field) => {
|
|
35
|
+
const { get } = this.app.lib._
|
|
36
|
+
let refName = get(this.schema, `view.widget.${field}.attr.ref-name`, this.params.attr.refName)
|
|
37
|
+
if (!refName && this.params.attr[field] && this.params.attr[field].endsWith('Id')) refName = this.params.attr[field].slice(0, -2)
|
|
38
|
+
return refName
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getSetting = (key, defValue) => {
|
|
42
|
+
const { get, camelCase } = this.app.lib._
|
|
43
|
+
const widgetName = camelCase(this.constructor.name)
|
|
44
|
+
key = key.replaceAll('{self}', widgetName)
|
|
45
|
+
const cfg = this.app.waibu.getSetting(`${this.plugin.ns}:${key}`, { defValue, req: this.component.req })
|
|
46
|
+
return get(this.schema, `view.${key}`, cfg)
|
|
31
47
|
}
|
|
32
48
|
}
|
|
33
49
|
}
|
|
@@ -25,7 +25,7 @@ async function btnColumns () {
|
|
|
25
25
|
let prop = find(schema.properties, { name: f })
|
|
26
26
|
if (!prop) prop = find(schema.view.calcFields, { name: f })
|
|
27
27
|
if (!prop) continue
|
|
28
|
-
const attr = { 'x-model': 'selected', label: req.t(get(schema, `view.label.${f}`, `field.${f}`)), value: f }
|
|
28
|
+
const attr = { 'x-model': 'selected', label: req.t(get(schema, `view.label.${f}`, `field.${f}`)), value: f, labelText: 'nowrap' }
|
|
29
29
|
if (fields.includes(f)) attr.checked = true
|
|
30
30
|
items.push(await this.component.buildTag({ tag: 'formCheck', attr }))
|
|
31
31
|
}
|
|
@@ -39,17 +39,27 @@ async function btnColumns () {
|
|
|
39
39
|
$refs.apply.href = '${href}&${qsKey.fields}=' + selected.join(',')
|
|
40
40
|
$watch('selected', v => {
|
|
41
41
|
$refs.apply.href = '${href}&${qsKey.fields}=' + v.join(',')
|
|
42
|
+
if (v.length === 0) $refs.apply.classList.add('disabled')
|
|
43
|
+
else $refs.apply.classList.remove('disabled')
|
|
42
44
|
})
|
|
43
45
|
">`)
|
|
44
46
|
this.params.attr.menuPrepend = Buffer.from(menuPrepend.join('\n')).toString('base64')
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
const btnColor = this.params.attr.applyColor ?? 'primary'
|
|
48
|
+
const sentences = [
|
|
49
|
+
'<c:div flex="justify-content:between" margin="top-2" >',
|
|
50
|
+
` <c:btn size="sm" x-ref="apply" color="${btnColor}" href="${href}">${req.t('apply')}</c:btn>`,
|
|
51
|
+
' <c:btn-group>',
|
|
52
|
+
` <c:btn size="sm" color="${btnColor}-outline" icon="checkAll" @click="selected = all" />`,
|
|
53
|
+
` <c:btn size="sm" color="${btnColor}-outline" icon="remove" @click="selected = []" />`,
|
|
54
|
+
' </c:btn-group>',
|
|
55
|
+
'</c:div>'
|
|
56
|
+
]
|
|
57
|
+
const menuAppend = await this.component.buildSentence(sentences, this.component.locals)
|
|
58
|
+
this.params.attr.menuAppend = Buffer.from(menuAppend + '\n</form>').toString('base64')
|
|
49
59
|
this.params.attr.autoClose = 'outside'
|
|
50
60
|
this.params.attr.triggerColor = this.params.attr.color
|
|
51
61
|
this.params.attr.menuDir = this.params.attr.menuDir ?? 'end'
|
|
52
|
-
this.params.attr.menuMax = this.params.attr.menuMax ?? '
|
|
62
|
+
this.params.attr.menuMax = this.params.attr.menuMax ?? this.getSetting('control.{self}.menuMax')
|
|
53
63
|
const html = [...items]
|
|
54
64
|
this.params.html = await this.component.buildTag({ tag: 'dropdown', attr: this.params.attr, html: html.join('\n') })
|
|
55
65
|
this.params.noTag = true
|
|
@@ -61,7 +61,7 @@ async function btnExport () {
|
|
|
61
61
|
}
|
|
62
62
|
item[key] = value
|
|
63
63
|
}
|
|
64
|
-
return this.ftype === 'csv' ? CSVJSON.json2csv(item) : JSON.stringify(item)
|
|
64
|
+
return this.ftype === 'csv' ? CSVJSON.json2csv(item) : JSON.stringify(item, null, 2)
|
|
65
65
|
},
|
|
66
66
|
extractTable (selector) {
|
|
67
67
|
let items = []
|
|
@@ -98,7 +98,7 @@ async function btnExport () {
|
|
|
98
98
|
}
|
|
99
99
|
items.push(item)
|
|
100
100
|
}
|
|
101
|
-
return this.ftype === 'csv' ? CSVJSON.json2csv(items) : JSON.stringify(items)
|
|
101
|
+
return this.ftype === 'csv' ? CSVJSON.json2csv(items) : JSON.stringify(items, null, 2)
|
|
102
102
|
},
|
|
103
103
|
async submit () {
|
|
104
104
|
const instance = wbs.getInstance('Modal', $refs.export)
|
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
import wdbBase from '../wdb-base.js'
|
|
2
2
|
|
|
3
|
+
async function handleRo (attr = {}, prop = {}, widget = {}) {
|
|
4
|
+
const { get, camelCase, isEmpty, isString } = this.app.lib._
|
|
5
|
+
const { callHandler } = this.app.bajo
|
|
6
|
+
const { escape } = this.app.waibu
|
|
7
|
+
const { req } = this.component
|
|
8
|
+
const dataValue = get(this.formData, `_orig.${prop.name}`, prop.dataValue ?? '')
|
|
9
|
+
let value = get(this.oldData, prop.name, get(this.formData, prop.name, prop.value ?? ''))
|
|
10
|
+
const format = get(this.schema, `view.format.${prop.name}`)
|
|
11
|
+
const formatValue = get(this.schema, `view.formatValue.${prop.name}`)
|
|
12
|
+
const labelField = get(this.schema, `view.widget.${prop.name}.attr.labelField`)
|
|
13
|
+
if (formatValue) value = await formatValue.call(this, value, this.formData, { req })
|
|
14
|
+
else if (prop.ref) {
|
|
15
|
+
value = this.getRefValue({ field: prop.name, labelField, refName: this.getRefName(prop.name) })
|
|
16
|
+
if (format && !isEmpty(value)) attr.href = await format.call(this, value, this.formData, { linkOnly: true })
|
|
17
|
+
} else if (prop.values) {
|
|
18
|
+
const values = isString(prop.values) ? (await callHandler(prop.values)) : prop.values
|
|
19
|
+
value = values.find(v => v.value === dataValue)
|
|
20
|
+
if (value) {
|
|
21
|
+
const key = camelCase(`${prop.name} ${value.text}`)
|
|
22
|
+
value = req.te(key) ? req.t(key) : value.text
|
|
23
|
+
}
|
|
24
|
+
} else if (format && !isEmpty(value)) value = await format.call(this, value, this.formData)
|
|
25
|
+
attr.dataValue = escape(dataValue)
|
|
26
|
+
attr.value = escape(value)
|
|
27
|
+
attr.dataType = prop.type
|
|
28
|
+
|
|
29
|
+
if (['object', 'array', 'text'].includes(prop.type)) {
|
|
30
|
+
attr.style = 'min-height: 100px'
|
|
31
|
+
return await this.component.buildTag({ tag: 'formTextarea', attr, html: value })
|
|
32
|
+
}
|
|
33
|
+
return await this.component.buildTag({ tag: 'formPlaintext', attr, selfCosing: true, noEscape: true })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function handleRw (attr = {}, prop = {}, widget = {}) {
|
|
37
|
+
const { get, has, isPlainObject, isArray } = this.app.lib._
|
|
38
|
+
const { escape } = this.app.waibu
|
|
39
|
+
const { stringifyAttribs } = this.app.waibuMpa
|
|
40
|
+
if (has(attr, 'name') && !has(attr, 'value')) {
|
|
41
|
+
attr.dataType = attr.dataType ?? prop.type
|
|
42
|
+
attr.dataValue = get(this, `formData.${attr.name}`)
|
|
43
|
+
if (isPlainObject(attr.dataValue) || isArray(attr.dataValue)) attr.dataValue = JSON.stringify(attr.dataValue)
|
|
44
|
+
attr.dataValue = escape(attr.dataValue)
|
|
45
|
+
attr.value = widget.component === 'form-plaintext' ? get(this, `oldData.${attr.name}`, attr.dataValue) : attr.dataValue
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const cmp = prop.ref ? 'wdb-lookup-select' : widget.component
|
|
49
|
+
return `<c:${cmp} ${stringifyAttribs(attr)} data-type="${prop.type}" />`
|
|
50
|
+
}
|
|
51
|
+
|
|
3
52
|
async function form () {
|
|
4
53
|
const WdbBase = await wdbBase.call(this)
|
|
5
54
|
|
|
@@ -7,51 +56,47 @@ async function form () {
|
|
|
7
56
|
build = async () => {
|
|
8
57
|
const { get, find, filter, forOwn, isEmpty } = this.app.lib._
|
|
9
58
|
const { base64JsonEncode } = this.app.waibu
|
|
10
|
-
const { req } = this.component
|
|
11
|
-
const schema = get(this, 'component.locals.schema', {})
|
|
12
|
-
const data = get(this, 'component.locals.form', {})
|
|
13
59
|
const body = []
|
|
14
|
-
const xModels = get(schema, 'view.x.model', [])
|
|
15
|
-
const xOns = get(schema, 'view.x.on', [])
|
|
16
|
-
for (const l of schema.view.layout) {
|
|
17
|
-
const fields = filter(l.fields, f => schema.view.fields.includes(f))
|
|
60
|
+
const xModels = get(this.schema, 'view.x.model', [])
|
|
61
|
+
const xOns = get(this.schema, 'view.x.on', [])
|
|
62
|
+
for (const l of this.schema.view.layout) {
|
|
63
|
+
const fields = filter(l.fields, f => this.schema.view.fields.includes(f))
|
|
18
64
|
if (fields.length === 0) continue
|
|
19
|
-
body.push(`<c:fieldset ${schema.view.card === false ? '' : 'card'} ${l.name[0] !== '_' ? ('t:legend="' + l.name + '"') : ''} grid-gutter="2">`)
|
|
65
|
+
body.push(`<c:fieldset ${this.schema.view.card === false ? '' : 'card'} ${l.name[0] !== '_' ? ('t:legend="' + l.name + '"') : ''} grid-gutter="2">`)
|
|
20
66
|
for (const f of fields) {
|
|
21
|
-
const
|
|
22
|
-
let prop = find(schema.properties, { name: f })
|
|
23
|
-
if (!prop) prop = find(schema.view.calcFields, { name: f })
|
|
67
|
+
const widget = this.schema.view.widget[f]
|
|
68
|
+
let prop = find(this.schema.properties, { name: f })
|
|
69
|
+
if (!prop) prop = find(this.schema.view.calcFields, { name: f })
|
|
24
70
|
if (!prop) continue
|
|
25
|
-
const attr =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
71
|
+
const attr = {
|
|
72
|
+
'x-ref': widget.name,
|
|
73
|
+
labelFloating: true,
|
|
74
|
+
name: widget.name
|
|
75
|
+
}
|
|
76
|
+
if (xModels.includes(widget.name)) attr['x-model'] = widget.name
|
|
77
|
+
forOwn(widget.attr, (v, k) => {
|
|
78
|
+
if (v === true) attr[k] = true
|
|
79
|
+
else attr[k] = v
|
|
30
80
|
})
|
|
31
|
-
|
|
81
|
+
attr.label = this.component.req.t(attr.label)
|
|
82
|
+
const xon = filter(xOns, { field: widget.name })
|
|
32
83
|
for (const o of xon) {
|
|
33
|
-
attr
|
|
84
|
+
attr[`@${o.bind}`] = o.handler
|
|
34
85
|
}
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
let value
|
|
39
|
-
if (schema.view.valueFormatter[f]) value = await schema.view.valueFormatter[f].call(this, data[f], data, { req })
|
|
40
|
-
else if (prop.ref) value = this.getRefValue({ field: f, labelField: w.attr.labelField })
|
|
41
|
-
body.push(`<c:${w.component} ${attributes} data-type="${prop.type}" ${value ? `value="${value}"` : ''} />`)
|
|
42
|
-
} else if (prop.ref) {
|
|
43
|
-
body.push(`<c:wdb-lookup-select ${attributes} />`)
|
|
86
|
+
if (widget.componentOpts) attr['c-opts'] = base64JsonEncode(widget.componentOpts)
|
|
87
|
+
if (widget.component === 'form-plaintext' || this.params.attr.method !== 'POST') {
|
|
88
|
+
body.push(await handleRo.call(this, attr, prop, widget))
|
|
44
89
|
} else {
|
|
45
|
-
body.push(
|
|
90
|
+
body.push(await handleRw.call(this, attr, prop, widget))
|
|
46
91
|
}
|
|
47
92
|
}
|
|
48
93
|
body.push('</c:fieldset>')
|
|
49
94
|
}
|
|
50
95
|
const html = await this.component.buildSentence(body, this.component.locals)
|
|
51
96
|
this.params.html = `${html}\n${this.params.html}`
|
|
52
|
-
const xData = get(schema, 'view.x.data', '')
|
|
97
|
+
const xData = get(this.schema, 'view.x.data', '')
|
|
53
98
|
this.params.attr['x-data'] = isEmpty(xData) ? '' : `{ ${xData} }`
|
|
54
|
-
this.params.attr['x-init'] = get(schema, 'view.x.init', '')
|
|
99
|
+
this.params.attr['x-init'] = get(this.schema, 'view.x.init', '')
|
|
55
100
|
|
|
56
101
|
this.params.tag = this.params.attr.tag ?? 'form'
|
|
57
102
|
}
|
|
@@ -9,18 +9,16 @@ async function lookupSelect () {
|
|
|
9
9
|
this.params.noTag = true
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
returnEmpty = () => {
|
|
13
|
-
this.params.html = ''
|
|
14
|
-
}
|
|
15
|
-
|
|
16
12
|
build = async () => {
|
|
17
13
|
const { isEmpty, get, omit, set, camelCase, kebabCase } = this.app.lib._
|
|
18
14
|
const { parseQuery } = this.app.dobo
|
|
19
15
|
const { base64JsonEncode } = this.app.waibu
|
|
20
|
-
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
const ref = this.getRef({ field: this.params.attr.name, refName: this.getRefName(this.params.attr.name) })
|
|
17
|
+
if (isEmpty(ref)) {
|
|
18
|
+
const sentence = `<c:form-input ${Object.entries(this.params.attr).map(([k, v]) => `${kebabCase(k)}="${v}"`).join(' ')} />`
|
|
19
|
+
this.params.html = this.component.buildSentence(sentence, this.component.locals)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
24
22
|
|
|
25
23
|
this.params.attr.url = this.params.attr.url ?? `waibuDb.restapi:/lookup/${kebabCase(ref.model)}`
|
|
26
24
|
const omitted = ['url', 'searchField', 'labelField', 'valueField']
|
|
@@ -7,6 +7,7 @@ async function query () {
|
|
|
7
7
|
build = async () => {
|
|
8
8
|
const { req } = this.component
|
|
9
9
|
const { generateId } = this.app.lib.aneka
|
|
10
|
+
const { join } = this.app.bajo
|
|
10
11
|
const { jsonStringify } = this.app.waibuMpa
|
|
11
12
|
const { find, get, without, isEmpty, filter, upperFirst } = this.app.lib._
|
|
12
13
|
const qsKey = this.app.waibu.config.qsKey
|
|
@@ -60,8 +61,11 @@ async function query () {
|
|
|
60
61
|
}
|
|
61
62
|
this.params.noTag = true
|
|
62
63
|
const container = this.params.attr.modal ? 'modal' : 'drawer'
|
|
64
|
+
const scanables = (this.model ? this.model.scanables : []).map(item => req.t(`field.${item}`))
|
|
65
|
+
let placeholder = this.params.attr.placeholder
|
|
66
|
+
if (!placeholder) placeholder = scanables.length > 0 ? req.t('queryHint%s', join(scanables, { separator: ', ', lastSeparator: 'or' })) : req.t('query')
|
|
63
67
|
this.params.html = await this.component.buildSentence(`
|
|
64
|
-
<c:form-input type="search"
|
|
68
|
+
<c:form-input type="search" placeholder="${placeholder}" id="${id}" x-data="{ query: '' }" x-init="
|
|
65
69
|
const url = new URL(window.location.href)
|
|
66
70
|
query = url.searchParams.get('${qsKey.query}') ?? ''
|
|
67
71
|
" x-model="query" @on-query.window="query = $event.detail ?? ''" @keyup.enter="$dispatch('on-submit')">
|
|
@@ -21,45 +21,24 @@ async function table () {
|
|
|
21
21
|
return get(schema, 'view.noWrap', []).includes(field)
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
_defFormatter = async ({ req, key, value, data, schema, params }) => {
|
|
25
|
-
const { get, find, camelCase, isEmpty } = this.app.lib._
|
|
26
|
-
const { escape } = this.app.waibu
|
|
27
|
-
const prop = find(schema.properties, { name: key })
|
|
28
|
-
if (!prop) return value
|
|
29
|
-
if (prop.type === 'boolean') {
|
|
30
|
-
value = (await this.component.buildTag({ tag: 'icon', attr: { name: `circle${data[key] ? 'Check' : 'Cross'}` } })) +
|
|
31
|
-
' ' + (req.t(data[key] ? 'Yes' : 'No'))
|
|
32
|
-
} else if (prop.values) {
|
|
33
|
-
const values = typeof prop.values === 'string' ? this.propValues[key] : prop.values
|
|
34
|
-
const item = find(values, { value }) ?? {}
|
|
35
|
-
const ttext = camelCase(`${prop.name} ${item.text}`)
|
|
36
|
-
value = escape(req.format(!isEmpty(item) ? (req.te(ttext) ? req.t(ttext) : item.text) : value, prop.type))
|
|
37
|
-
if (item && !params.attr.noDataValueRef && !isEmpty(data[key])) value += ` <sup><a href="#" title="${req.t('dataValue')}: ${data[key]}">*</a></sup>`
|
|
38
|
-
} else if (['string', 'text'].includes(prop.type)) {
|
|
39
|
-
if (!get(schema, 'view.noEscape', []).includes(key)) value = escape(value)
|
|
40
|
-
}
|
|
41
|
-
return value
|
|
42
|
-
}
|
|
43
|
-
|
|
44
24
|
build = async () => {
|
|
45
25
|
const { callHandler } = this.app.bajo
|
|
46
26
|
const { req } = this.component
|
|
47
27
|
const { escape, attrToArray } = this.app.waibu
|
|
48
|
-
const { formatRecord } = this.app.waibuDb
|
|
49
28
|
const { groupAttrs } = this.app.waibuMpa
|
|
50
|
-
const { get, omit, set, find, isEmpty, without, merge } = this.app.lib._
|
|
29
|
+
const { get, omit, set, find, isEmpty, without, merge, camelCase } = this.app.lib._
|
|
51
30
|
const group = groupAttrs(this.params.attr, ['body', 'head', 'foot'])
|
|
52
31
|
this.params.attr = group._
|
|
53
32
|
const prettyUrl = this.params.attr.prettyUrl
|
|
54
33
|
|
|
55
34
|
const schema = get(this, 'component.locals.schema', {})
|
|
56
35
|
const data = get(this, 'component.locals.list.data', [])
|
|
57
|
-
const fdata = await formatRecord.call(this.plugin, { data, req, schema })
|
|
58
36
|
const filter = get(this, 'component.locals.list.filter', {})
|
|
59
37
|
const count = get(this, 'component.locals.list.count', 0)
|
|
60
38
|
// collect prop.values for later use
|
|
61
39
|
for (const prop of schema.properties) {
|
|
62
40
|
if (typeof prop.values === 'string') this.propValues[prop.name] = await callHandler(prop.values)
|
|
41
|
+
else if (prop.values) this.propValues[prop.name] = prop.values
|
|
63
42
|
}
|
|
64
43
|
if (count === 0 || data.length === 0) {
|
|
65
44
|
const alert = '<c:alert color="warning" t:content="noRecordFound" margin="top-4"/>'
|
|
@@ -87,7 +66,8 @@ async function table () {
|
|
|
87
66
|
let selection
|
|
88
67
|
const canDelete = !disableds.includes('remove')
|
|
89
68
|
const canEdit = !disableds.includes('update')
|
|
90
|
-
|
|
69
|
+
const canDetails = !disableds.includes('get')
|
|
70
|
+
if (canEdit || canDetails) selection = 'single'
|
|
91
71
|
if (canDelete) selection = 'multi'
|
|
92
72
|
if (selection) this.params.attr.hover = true
|
|
93
73
|
|
|
@@ -141,13 +121,12 @@ async function table () {
|
|
|
141
121
|
items = []
|
|
142
122
|
for (const idx in data) {
|
|
143
123
|
const d = data[idx]
|
|
144
|
-
const fd = fdata[idx]
|
|
145
124
|
const lines = []
|
|
146
125
|
if (selection) {
|
|
147
126
|
const tag = selection === 'single' ? 'formRadio' : 'formCheck'
|
|
148
|
-
const attr = { 'x-model': 'selected', name: '_rt', value: d.id, noLabel: true, noWrapper: true }
|
|
127
|
+
const attr = { 'x-model': 'selected', name: '_rt', value: d._orig.id, noLabel: true, noWrapper: true }
|
|
149
128
|
const type = find(schema.properties, { name: 'id' }).type
|
|
150
|
-
const prepend = `<td data-value="${d.id}" data-key="id" data-type="${type}">`
|
|
129
|
+
const prepend = `<td data-value="${d._orig.id}" data-key="id" data-type="${type}">`
|
|
151
130
|
lines.push(await this.component.buildTag({ tag, attr, prepend, append: '</td>' }))
|
|
152
131
|
}
|
|
153
132
|
for (const f of schema.view.fields) {
|
|
@@ -155,34 +134,47 @@ async function table () {
|
|
|
155
134
|
let prop = find(schema.properties, { name: f })
|
|
156
135
|
if (!prop) prop = find(schema.view.calcFields, { name: f })
|
|
157
136
|
if (!prop) continue
|
|
158
|
-
let dataValue = d[f] ?? ''
|
|
137
|
+
let dataValue = d._orig[f] ?? ''
|
|
159
138
|
if (!isEmpty(dataValue)) {
|
|
160
139
|
if (['datetime'].includes(prop.type)) dataValue = escape(dataValue.toISOString())
|
|
161
140
|
if (['string', 'text'].includes(prop.type)) dataValue = escape(dataValue)
|
|
162
141
|
if (['array', 'object'].includes(prop.type)) dataValue = escape(JSON.stringify(d[f]))
|
|
163
142
|
}
|
|
164
|
-
|
|
143
|
+
const refName = get(schema, `view.widget.${f}.attr.refName`)
|
|
144
|
+
let value = this.getRefValue({ field: f, data: d, refName }) ?? d[f]
|
|
145
|
+
const formatValue = get(schema, `view.formatValue.${f}`)
|
|
146
|
+
if (formatValue) {
|
|
147
|
+
value = await formatValue.call(this, value, d, { params: this.params, req })
|
|
148
|
+
dataValue = value
|
|
149
|
+
}
|
|
150
|
+
if (!get(schema, 'view.noEscape', []).includes(f)) value = escape(value)
|
|
165
151
|
const attr = { dataValue, dataKey: prop.name, dataType: prop.type }
|
|
166
152
|
if (!disableds.includes('get')) attr.style = { cursor: 'pointer' }
|
|
167
|
-
const
|
|
168
|
-
if (
|
|
153
|
+
const formatCell = get(schema, `view.formatCell.${f}`)
|
|
154
|
+
if (formatCell) merge(attr, await formatCell.call(this, value, d, { params: this.params, req }))
|
|
169
155
|
const noWrap = this.isNoWrap(f, schema, group.body.nowrap) ? 'nowrap' : ''
|
|
170
156
|
if (this.isRightAligned(f, schema)) attr.text = `align:end ${noWrap}`
|
|
171
157
|
else attr.text = noWrap
|
|
172
158
|
const lookup = get(schema, `view.lookup.${f}`)
|
|
173
159
|
if (lookup) {
|
|
174
|
-
const item = find(lookup.values, set({}, lookup.id ?? 'id',
|
|
160
|
+
const item = find(lookup.values, set({}, lookup.id ?? 'id', dataValue))
|
|
175
161
|
if (item) value = req.t(item[lookup.field ?? 'name'])
|
|
176
162
|
}
|
|
177
|
-
const
|
|
178
|
-
if (
|
|
179
|
-
|
|
163
|
+
const format = get(schema, `view.format.${f}`)
|
|
164
|
+
if (format) value = await format.call(this, value, d, { params: this.params, req })
|
|
165
|
+
if (this.propValues[f]) {
|
|
166
|
+
const item = find(this.propValues[f], { value: dataValue })
|
|
167
|
+
if (item) {
|
|
168
|
+
const key = camelCase(`${f} ${item.text}`)
|
|
169
|
+
value = req.te(key) ? req.t(key) : item.text
|
|
170
|
+
}
|
|
171
|
+
}
|
|
180
172
|
const line = await this.component.buildTag({ tag: 'td', attr, html: value })
|
|
181
173
|
lines.push(line)
|
|
182
174
|
}
|
|
183
|
-
const attr = { id: `rec-${d.id}` }
|
|
184
|
-
if (!disableds.includes('update') || !disableds.includes('remove')) attr['@click'] = `toggle('${d.id}')`
|
|
185
|
-
if (!disableds.includes('get')) attr['@dblclick'] = `goDetails('${d.id}')`
|
|
175
|
+
const attr = { id: `rec-${d._orig.id}` }
|
|
176
|
+
if (!disableds.includes('update') || !disableds.includes('remove') || !disableds.includes('get')) attr['@click'] = `toggle('${d._orig.id}')`
|
|
177
|
+
if (!disableds.includes('get')) attr['@dblclick'] = `goDetails('${d._orig.id}')`
|
|
186
178
|
items.push(await this.component.buildTag({ tag: 'tr', attr, html: lines.join('\n') }))
|
|
187
179
|
}
|
|
188
180
|
html.push(await this.component.buildTag({ tag: 'tbody', attr: group.body, html: items.join('\n') }))
|
|
@@ -235,7 +227,7 @@ async function table () {
|
|
|
235
227
|
}`
|
|
236
228
|
xInit = `
|
|
237
229
|
${xInit}
|
|
238
|
-
$watch('selected', val => $dispatch('on-selection', [val]))
|
|
230
|
+
$watch('selected', val => $dispatch('on-selection', _.isEmpty(val) ? [] : [val]))
|
|
239
231
|
`
|
|
240
232
|
} else {
|
|
241
233
|
xData = `{
|
|
@@ -244,6 +236,7 @@ async function table () {
|
|
|
244
236
|
}
|
|
245
237
|
this.params.attr['x-data'] = xData
|
|
246
238
|
this.params.attr['x-init'] = xInit
|
|
239
|
+
this.params.attr.responsive = true
|
|
247
240
|
this.params.html = await this.component.buildTag({ tag: 'table', attr: this.params.attr, html: html.join('\n') })
|
|
248
241
|
}
|
|
249
242
|
}
|
package/index.js
CHANGED
|
@@ -43,6 +43,11 @@ async function factory (pkgName) {
|
|
|
43
43
|
count: false,
|
|
44
44
|
patchEnabled: false
|
|
45
45
|
},
|
|
46
|
+
control: {
|
|
47
|
+
wdbBtnColumns: {
|
|
48
|
+
menuMax: 10
|
|
49
|
+
}
|
|
50
|
+
},
|
|
46
51
|
enableRestApiForModel: false
|
|
47
52
|
}
|
|
48
53
|
this.methodMap = {
|
|
@@ -200,7 +205,7 @@ async function factory (pkgName) {
|
|
|
200
205
|
time: options.time ?? { timeZone }
|
|
201
206
|
}
|
|
202
207
|
rec[f] = format(data[f], prop.type, opts)
|
|
203
|
-
const vf = get(schema, `view.
|
|
208
|
+
const vf = get(schema, `view.formatValue.${f}`)
|
|
204
209
|
if (vf) {
|
|
205
210
|
if (isFunction(vf)) rec[f] = await vf.call(this, data[f], data, { req })
|
|
206
211
|
else rec[f] = await callHandler(vf, { req, value: data[f], data })
|
package/lib/crud/add-handler.js
CHANGED
|
@@ -14,8 +14,8 @@ async function addHandler ({ req, reply, model, params = {}, template, addOnsHan
|
|
|
14
14
|
delete req.query.query
|
|
15
15
|
let def = {}
|
|
16
16
|
if (req.method === 'GET' && req.query.mode === 'clone' && req.query.id) {
|
|
17
|
-
const resp = await getRecord({ model, req, id: req.query.id, options: { fields: map(schema.properties, 'name') } })
|
|
18
|
-
def = omit(resp.data, ['id', 'createdAt', 'updatedAt'])
|
|
17
|
+
const resp = await getRecord({ model, req, id: req.query.id, options: { fields: map(schema.properties, 'name'), ...opts } })
|
|
18
|
+
def = omit(resp.data._orig, ['id', 'createdAt', 'updatedAt'])
|
|
19
19
|
}
|
|
20
20
|
let form = defaultsDeep(req.body, def)
|
|
21
21
|
let error
|
package/lib/crud/all-handler.js
CHANGED
|
@@ -19,6 +19,9 @@ const handler = {
|
|
|
19
19
|
async function allHandler ({ model, action, req, reply, template, params = {}, options = {} }) {
|
|
20
20
|
const { upperFirst, merge, keys } = this.app.lib._
|
|
21
21
|
if (!keys(handler).includes(action)) throw this.error('_notFound')
|
|
22
|
+
options.modelOpts = options.modelOpts ?? {}
|
|
23
|
+
options.modelOpts.formatValue = true
|
|
24
|
+
options.modelOpts.retainOriginalValue = true
|
|
22
25
|
if (['delete', 'export'].includes(action)) {
|
|
23
26
|
if (req.method === 'GET') throw this.error('_notFound')
|
|
24
27
|
return await handler[action].call(this, { model, req, reply, options })
|
package/lib/crud/edit-handler.js
CHANGED
|
@@ -7,7 +7,7 @@ async function editHandler ({ req, reply, model, id, params = {}, template, addO
|
|
|
7
7
|
const { buildUrl } = this.app.waibuMpa
|
|
8
8
|
const { fs } = this.app.lib
|
|
9
9
|
const { defaultsDeep } = this.app.lib.aneka
|
|
10
|
-
const { merge, isEmpty, omit } = this.app.lib._
|
|
10
|
+
const { merge, isEmpty, omit, cloneDeep, isArray, isPlainObject } = this.app.lib._
|
|
11
11
|
const opts = merge({}, options.modelOpts)
|
|
12
12
|
let error
|
|
13
13
|
let resp
|
|
@@ -22,7 +22,12 @@ async function editHandler ({ req, reply, model, id, params = {}, template, addO
|
|
|
22
22
|
delete req.query.query
|
|
23
23
|
const old = await getRecord({ model, req, id, options: opts })
|
|
24
24
|
if (isEmpty(old.data)) return await reply.view(notFoundTpl, params)
|
|
25
|
-
|
|
25
|
+
opts._data = old
|
|
26
|
+
const def = cloneDeep(old.data._orig)
|
|
27
|
+
for (const k in def) {
|
|
28
|
+
if (isArray(def[k]) || isPlainObject(def[k])) def[k] = JSON.stringify(def[k])
|
|
29
|
+
}
|
|
30
|
+
form = defaultsDeep(req.body, def)
|
|
26
31
|
if (req.method !== 'GET') {
|
|
27
32
|
form = omit(form, ['_action', '_value'])
|
|
28
33
|
try {
|
|
@@ -49,7 +54,7 @@ async function editHandler ({ req, reply, model, id, params = {}, template, addO
|
|
|
49
54
|
}
|
|
50
55
|
const addOns = addOnsHandler ? await addOnsHandler.call(this.app[req.ns], { req, reply, params, data: resp, schema, error, options }) : undefined
|
|
51
56
|
const attachments = await attachmentHandler.call(this, { schema, id, options })
|
|
52
|
-
merge(params, { form, schema, error, addOns, attachments })
|
|
57
|
+
merge(params, { oldData: old.data, form, schema, error, addOns, attachments })
|
|
53
58
|
if (schema.template) template = schema.template
|
|
54
59
|
if (schema.layout) params.page.layout = schema.layout
|
|
55
60
|
return await reply.view(template, params)
|
|
@@ -12,8 +12,9 @@ function getCommons (action, schema, ext, options = {}) {
|
|
|
12
12
|
const widget = defaultsDeep(get(ext, `view.${action}.widget`), get(ext, 'common.widget', {}))
|
|
13
13
|
const noEscape = get(ext, `view.${action}.noEscape`, get(ext, 'common.noEscape', []))
|
|
14
14
|
const control = defaultsDeep(get(ext, `view.${action}.control`), get(ext, 'common.control', {}))
|
|
15
|
-
const
|
|
16
|
-
const
|
|
15
|
+
const formatValue = defaultsDeep(get(ext, `view.${action}.formatValue`), get(ext, 'common.formatValue', {}))
|
|
16
|
+
const formatCell = defaultsDeep(get(ext, `view.${action}.formatCell`), get(ext, 'common.formatCell', {}))
|
|
17
|
+
const format = defaultsDeep(get(ext, `view.${action}.format`), get(ext, 'common.format', {}))
|
|
17
18
|
const card = get(ext, `view.${action}.card`, get(ext, 'common.card', true))
|
|
18
19
|
let hidden = get(ext, `view.${action}.hidden`, get(ext, 'common.hidden', []))
|
|
19
20
|
const disabled = get(ext, `view.${action}.disabled`, get(ext, 'common.disabled', []))
|
|
@@ -30,8 +31,9 @@ function getCommons (action, schema, ext, options = {}) {
|
|
|
30
31
|
set(schema, 'view.calcFields', calcFields)
|
|
31
32
|
set(schema, 'view.noEscape', noEscape)
|
|
32
33
|
set(schema, 'view.widget', widget)
|
|
33
|
-
set(schema, 'view.
|
|
34
|
-
set(schema, 'view.
|
|
34
|
+
set(schema, 'view.formatValue', formatValue)
|
|
35
|
+
set(schema, 'view.formatCell', formatCell)
|
|
36
|
+
set(schema, 'view.format', merge({}, defFormatter, format))
|
|
35
37
|
set(schema, 'view.stat.aggregate', aggregate)
|
|
36
38
|
set(schema, 'view.disabled', disabled)
|
|
37
39
|
set(schema, 'view.control', control)
|
|
@@ -6,8 +6,10 @@ async function updateRecord ({ model, req, reply, id, body, options = {}, transa
|
|
|
6
6
|
|
|
7
7
|
async function handler (trx) {
|
|
8
8
|
if (opts.trx === true) opts.trx = trx
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
if (!opts._data) {
|
|
10
|
+
const resp = await getOneRecord.call(me, mdl, recId, filter, opts)
|
|
11
|
+
opts._data = resp.data
|
|
12
|
+
}
|
|
11
13
|
const ret = await mdl.updateRecord(recId, input, opts)
|
|
12
14
|
if (attachment) ret.data._attachment = await mdl.findAttachment(id, { stats, mimeType })
|
|
13
15
|
return ret
|
package/lib/util.js
CHANGED
|
@@ -61,7 +61,7 @@ export async function getOneRecord (model, id, filter, options) {
|
|
|
61
61
|
const { cloneDeep, pick, isEmpty } = this.app.lib._
|
|
62
62
|
let query = cloneDeep(filter.query || {})
|
|
63
63
|
query = { $and: [query, { id }] }
|
|
64
|
-
const opts = pick(options, ['forceNoHidden', 'trx', 'req', 'refs'])
|
|
64
|
+
const opts = pick(options, ['forceNoHidden', 'trx', 'req', 'refs', 'formatValue', 'retainOriginalValue'])
|
|
65
65
|
opts.dataOnly = false
|
|
66
66
|
const data = await model.findOneRecord({ query }, opts)
|
|
67
67
|
if (isEmpty(data.data) && options.throwNotFound) throw this.error('_notFound')
|
|
@@ -79,7 +79,8 @@ export async function processHandler ({ action, model, handler, options } = {})
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
try {
|
|
82
|
-
|
|
82
|
+
if (options.trx === true) return await model.transaction(handler)
|
|
83
|
+
return await handler()
|
|
83
84
|
} catch (err) {
|
|
84
85
|
if (options.suppressError.includes(action)) return suppressedReturn.call(this, err)
|
|
85
86
|
throw err
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-04-13
|
|
4
|
+
|
|
5
|
+
- [2.16.0] Add ```oldData``` propety to ```WdbBase``` widget
|
|
6
|
+
- [2.16.0] Change ```WdbBase.getSetting()``` to also respect setting from ```waibu.getSetting()```
|
|
7
|
+
- [2.16.0] Rewrite ```WdbForm``` widget entirely
|
|
8
|
+
- [2.16.0] Remove redundant call to get old record in ```updateRecord()```
|
|
9
|
+
|
|
10
|
+
## 2026-04-11
|
|
11
|
+
|
|
12
|
+
- [2.15.0] Add ```control``` key in config object
|
|
13
|
+
- [2.15.0] Bug fix in ```formatRow()```
|
|
14
|
+
- [2.15.0] Add ```WdbBase.getRefName()```
|
|
15
|
+
- [2.15.0] Add ```WdbBase.getSetting()```
|
|
16
|
+
- [2.15.0] Update placeholder in ```WdbQuery``` based on model's ```scanables``` values
|
|
17
|
+
- [2.15.0] ```getSchemaExt()``` now support ```format```, ```formatValue``` and ```formatCell```
|
|
18
|
+
- [2.15.0] All default handlers now support ```options.formatValue``` and ```options.retainOriginalValue````
|
|
19
|
+
|
|
3
20
|
## 2026-04-07
|
|
4
21
|
|
|
5
22
|
- [2.14.0] Add ```wdb-lookup-select``` widget
|
|
@@ -31,8 +48,8 @@
|
|
|
31
48
|
|
|
32
49
|
## 2026-03-27
|
|
33
50
|
|
|
34
|
-
- [2.12.1] Bug fix in all ```view.
|
|
35
|
-
- [2.12.2] Bug fix in ```wdb-form``` & ```wdb-table``` widgets, now correctly use value from
|
|
51
|
+
- [2.12.1] Bug fix in all ```view.format``` & ```view.formatValue```
|
|
52
|
+
- [2.12.2] Bug fix in ```wdb-form``` & ```wdb-table``` widgets, now correctly use value from format if provided
|
|
36
53
|
|
|
37
54
|
## 2026-03-26
|
|
38
55
|
|