waibu-db 2.16.0 → 2.16.2
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/waibuBootstrap/theme/component/wdb-base.js +0 -45
- package/extend/waibuBootstrap/theme/component/widget/btn-export.js +1 -9
- package/extend/waibuBootstrap/theme/component/widget/form.js +17 -51
- package/extend/waibuBootstrap/theme/component/widget/table.js +6 -28
- package/lib/method/create-record.js +3 -3
- package/lib/method/update-record.js +2 -2
- package/lib/util.js +2 -4
- package/package.json +1 -1
- package/wiki/CHANGES.md +15 -0
|
@@ -1,50 +1,5 @@
|
|
|
1
1
|
async function wdbBase () {
|
|
2
2
|
return class WdbBase extends this.app.baseClass.MpaWidget {
|
|
3
|
-
constructor (options) {
|
|
4
|
-
super(options)
|
|
5
|
-
const { getModel } = this.app.dobo
|
|
6
|
-
const { get } = this.app.lib._
|
|
7
|
-
this.schema = get(this, 'component.locals.schema', {})
|
|
8
|
-
this.formData = get(this, 'component.locals.form', {})
|
|
9
|
-
this.oldData = get(this, 'component.locals.oldData', {})
|
|
10
|
-
this.model = getModel(this.schema.name, true)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
getRef = ({ field, refName, returning } = {}) => {
|
|
14
|
-
const { get } = this.app.lib._
|
|
15
|
-
if (!this.model) return {}
|
|
16
|
-
const prop = this.model.getProperty(field)
|
|
17
|
-
if (!prop) return {}
|
|
18
|
-
if (!refName && field.endsWith('Id')) refName = field.slice(0, -2)
|
|
19
|
-
const key = this.params.attr.refName ?? refName
|
|
20
|
-
const ref = get(prop, `ref.${key}`, {})
|
|
21
|
-
if (returning === 'all') return { ref, key }
|
|
22
|
-
else if (returning === 'key') return key
|
|
23
|
-
return ref
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
getRefValue = ({ field, data, labelField, refName } = {}) => {
|
|
27
|
-
const { get, isEmpty } = this.app.lib._
|
|
28
|
-
const { ref, key } = this.getRef({ field, refName, returning: 'all' })
|
|
29
|
-
if (isEmpty(ref)) return undefined
|
|
30
|
-
labelField = labelField ?? ref.labelField ?? 'id'
|
|
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)
|
|
47
|
-
}
|
|
48
3
|
}
|
|
49
4
|
}
|
|
50
5
|
|
|
@@ -67,16 +67,13 @@ async function btnExport () {
|
|
|
67
67
|
let items = []
|
|
68
68
|
let checker = false
|
|
69
69
|
const keys = []
|
|
70
|
-
const types = []
|
|
71
70
|
let els = document.querySelectorAll(selector + ' thead th')
|
|
72
71
|
for (const el of els) {
|
|
73
72
|
keys.push(this.options.includes('fkey') ? el.innerText : el.dataset.key)
|
|
74
|
-
types.push(el.dataset.type)
|
|
75
73
|
}
|
|
76
74
|
if (_.isEmpty(keys[0])) {
|
|
77
75
|
checker = true
|
|
78
76
|
keys.shift()
|
|
79
|
-
types.shift()
|
|
80
77
|
}
|
|
81
78
|
els = document.querySelectorAll(selector + ' tbody tr')
|
|
82
79
|
for (const el of els) {
|
|
@@ -85,12 +82,7 @@ async function btnExport () {
|
|
|
85
82
|
i = i + ''
|
|
86
83
|
if (i === '0' && checker) return undefined
|
|
87
84
|
if (this.options.includes('fvalue')) data.push(v.innerText)
|
|
88
|
-
else
|
|
89
|
-
const type = types[parseInt(i)]
|
|
90
|
-
let val = wmpa.parseValue(v.dataset.value, type)
|
|
91
|
-
if (['datetime', 'date', 'time'].includes(type)) val = val.toISOString()
|
|
92
|
-
data.push(val)
|
|
93
|
-
}
|
|
85
|
+
else data.push(wmpa.parseValue(v.dataset.value, v.dataset.value))
|
|
94
86
|
})
|
|
95
87
|
const item = {}
|
|
96
88
|
for (const i in keys) {
|
|
@@ -1,58 +1,24 @@
|
|
|
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
|
-
|
|
52
3
|
async function form () {
|
|
53
4
|
const WdbBase = await wdbBase.call(this)
|
|
54
5
|
|
|
55
6
|
return class WdbForm extends WdbBase {
|
|
7
|
+
static async handleRo ({ attr = {}, prop = {} } = {}) {
|
|
8
|
+
return await this.component.buildTag({ tag: 'formPlaintext', attr, selfCosing: true, noEscape: true })
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static async handleRw ({ attr = {}, prop = {}, widget = {} } = {}) {
|
|
12
|
+
const { get, has } = this.app.lib._
|
|
13
|
+
const { stringifyAttribs } = this.app.waibuMpa
|
|
14
|
+
if (has(attr, 'name') && !has(attr, 'value')) {
|
|
15
|
+
attr.value = widget.component === 'form-plaintext' ? get(this, `oldData.${attr.name}`, attr.dataValue) : attr.dataValue
|
|
16
|
+
}
|
|
17
|
+
attr.dataType = prop.type
|
|
18
|
+
const cmp = prop.ref ? 'wdb-lookup-select' : widget.component
|
|
19
|
+
return `<c:${cmp} ${stringifyAttribs(attr)} />`
|
|
20
|
+
}
|
|
21
|
+
|
|
56
22
|
build = async () => {
|
|
57
23
|
const { get, find, filter, forOwn, isEmpty } = this.app.lib._
|
|
58
24
|
const { base64JsonEncode } = this.app.waibu
|
|
@@ -85,9 +51,9 @@ async function form () {
|
|
|
85
51
|
}
|
|
86
52
|
if (widget.componentOpts) attr['c-opts'] = base64JsonEncode(widget.componentOpts)
|
|
87
53
|
if (widget.component === 'form-plaintext' || this.params.attr.method !== 'POST') {
|
|
88
|
-
body.push(await handleRo.call(this, attr, prop, widget))
|
|
54
|
+
body.push(await WdbForm.handleRo.call(this, { attr, prop, widget }))
|
|
89
55
|
} else {
|
|
90
|
-
body.push(await handleRw.call(this, attr, prop, widget))
|
|
56
|
+
body.push(await WdbForm.handleRw.call(this, { attr, prop, widget }))
|
|
91
57
|
}
|
|
92
58
|
}
|
|
93
59
|
body.push('</c:fieldset>')
|
|
@@ -4,8 +4,6 @@ async function table () {
|
|
|
4
4
|
const WdbBase = await wdbBase.call(this)
|
|
5
5
|
|
|
6
6
|
return class WdbTable extends WdbBase {
|
|
7
|
-
propValues = {}
|
|
8
|
-
|
|
9
7
|
isRightAligned = (field, schema) => {
|
|
10
8
|
const { get, find } = this.app.lib._
|
|
11
9
|
const prop = find(schema.properties, { name: field })
|
|
@@ -22,11 +20,10 @@ async function table () {
|
|
|
22
20
|
}
|
|
23
21
|
|
|
24
22
|
build = async () => {
|
|
25
|
-
const { callHandler } = this.app.bajo
|
|
26
23
|
const { req } = this.component
|
|
27
24
|
const { escape, attrToArray } = this.app.waibu
|
|
28
25
|
const { groupAttrs } = this.app.waibuMpa
|
|
29
|
-
const { get, omit, set, find, isEmpty, without, merge
|
|
26
|
+
const { get, omit, set, find, isEmpty, without, merge } = this.app.lib._
|
|
30
27
|
const group = groupAttrs(this.params.attr, ['body', 'head', 'foot'])
|
|
31
28
|
this.params.attr = group._
|
|
32
29
|
const prettyUrl = this.params.attr.prettyUrl
|
|
@@ -35,11 +32,6 @@ async function table () {
|
|
|
35
32
|
const data = get(this, 'component.locals.list.data', [])
|
|
36
33
|
const filter = get(this, 'component.locals.list.filter', {})
|
|
37
34
|
const count = get(this, 'component.locals.list.count', 0)
|
|
38
|
-
// collect prop.values for later use
|
|
39
|
-
for (const prop of schema.properties) {
|
|
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
|
|
42
|
-
}
|
|
43
35
|
if (count === 0 || data.length === 0) {
|
|
44
36
|
const alert = '<c:alert color="warning" t:content="noRecordFound" margin="top-4"/>'
|
|
45
37
|
this.params.noTag = true
|
|
@@ -134,12 +126,10 @@ async function table () {
|
|
|
134
126
|
let prop = find(schema.properties, { name: f })
|
|
135
127
|
if (!prop) prop = find(schema.view.calcFields, { name: f })
|
|
136
128
|
if (!prop) continue
|
|
137
|
-
let dataValue = d._orig[f]
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (['array', 'object'].includes(prop.type)) dataValue = escape(JSON.stringify(d[f]))
|
|
142
|
-
}
|
|
129
|
+
let dataValue = d._orig[f]
|
|
130
|
+
if (['datetime'].includes(prop.type)) dataValue = escape(dataValue.toISOString())
|
|
131
|
+
else if (['string', 'text'].includes(prop.type)) dataValue = escape(dataValue)
|
|
132
|
+
else if (['array', 'object'].includes(prop.type)) dataValue = escape(JSON.stringify(d[f]))
|
|
143
133
|
const refName = get(schema, `view.widget.${f}.attr.refName`)
|
|
144
134
|
let value = this.getRefValue({ field: f, data: d, refName }) ?? d[f]
|
|
145
135
|
const formatValue = get(schema, `view.formatValue.${f}`)
|
|
@@ -154,21 +144,9 @@ async function table () {
|
|
|
154
144
|
if (formatCell) merge(attr, await formatCell.call(this, value, d, { params: this.params, req }))
|
|
155
145
|
const noWrap = this.isNoWrap(f, schema, group.body.nowrap) ? 'nowrap' : ''
|
|
156
146
|
if (this.isRightAligned(f, schema)) attr.text = `align:end ${noWrap}`
|
|
157
|
-
else attr.text = noWrap
|
|
158
|
-
const lookup = get(schema, `view.lookup.${f}`)
|
|
159
|
-
if (lookup) {
|
|
160
|
-
const item = find(lookup.values, set({}, lookup.id ?? 'id', dataValue))
|
|
161
|
-
if (item) value = req.t(item[lookup.field ?? 'name'])
|
|
162
|
-
}
|
|
147
|
+
else attr.text = `${noWrap}`
|
|
163
148
|
const format = get(schema, `view.format.${f}`)
|
|
164
149
|
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
|
-
}
|
|
172
150
|
const line = await this.component.buildTag({ tag: 'td', attr, html: value })
|
|
173
151
|
lines.push(line)
|
|
174
152
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { prepCrud, processHandler } from '../util.js'
|
|
2
2
|
|
|
3
|
-
async function createRecord ({ model, req, reply,
|
|
4
|
-
const { model: mdl,
|
|
3
|
+
async function createRecord ({ model, req, reply, options = {}, transaction } = {}) {
|
|
4
|
+
const { model: mdl, opts, attachment, stats, mimeType } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], transaction })
|
|
5
5
|
|
|
6
6
|
async function handler (trx) {
|
|
7
7
|
if (opts.trx === true) opts.trx = trx
|
|
8
|
-
const ret = await mdl.createRecord(
|
|
8
|
+
const ret = await mdl.createRecord(req.body, { ...opts, partial: true, strict: true })
|
|
9
9
|
if (attachment) ret.data._attachment = await mdl.findAttachment(ret.data.id, { stats, mimeType })
|
|
10
10
|
return ret
|
|
11
11
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { prepCrud, getOneRecord, processHandler } from '../util.js'
|
|
2
2
|
|
|
3
3
|
async function updateRecord ({ model, req, reply, id, body, options = {}, transaction } = {}) {
|
|
4
|
-
const { model: mdl, filter,
|
|
4
|
+
const { model: mdl, filter, opts, recId, attachment, stats, mimeType } = await prepCrud.call(this, { model, req, reply, id, options, args: ['model', 'id'], transaction })
|
|
5
5
|
const me = this
|
|
6
6
|
|
|
7
7
|
async function handler (trx) {
|
|
@@ -10,7 +10,7 @@ async function updateRecord ({ model, req, reply, id, body, options = {}, transa
|
|
|
10
10
|
const resp = await getOneRecord.call(me, mdl, recId, filter, opts)
|
|
11
11
|
opts._data = resp.data
|
|
12
12
|
}
|
|
13
|
-
const ret = await mdl.updateRecord(recId,
|
|
13
|
+
const ret = await mdl.updateRecord(recId, body ?? req.body, { ...opts, partial: true, strict: true })
|
|
14
14
|
if (attachment) ret.data._attachment = await mdl.findAttachment(id, { stats, mimeType })
|
|
15
15
|
return ret
|
|
16
16
|
}
|
package/lib/util.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const actions = ['countRecord', 'createAggregate', 'createHistogram', 'createRecord', 'findAllRecord', 'findOneRecord', 'findRecord', 'getRecord', 'removeRecord', 'updateRecord']
|
|
2
2
|
|
|
3
|
-
export async function prepCrud ({ model,
|
|
3
|
+
export async function prepCrud ({ model, id, req, reply, transaction, options = {}, args } = {}) {
|
|
4
4
|
const { isSet } = this.app.lib.aneka
|
|
5
5
|
const { importModule } = this.app.bajo
|
|
6
6
|
const { parseFilter } = this.app.waibu
|
|
@@ -42,8 +42,6 @@ export async function prepCrud ({ model, body, id, req, reply, transaction, opti
|
|
|
42
42
|
model = model ?? pascalCase(params.model)
|
|
43
43
|
mdl = this.app.dobo.getModel(model)
|
|
44
44
|
}
|
|
45
|
-
const input = await mdl.sanitizeBody({ body: body ?? params.body, partial: true, strict: true })
|
|
46
|
-
|
|
47
45
|
opts.bboxLatField = req.query[cfgWeb.qsKey.bboxLatField]
|
|
48
46
|
opts.bboxLngField = req.query[cfgWeb.qsKey.bboxLngField]
|
|
49
47
|
const filter = parseFilter(req)
|
|
@@ -54,7 +52,7 @@ export async function prepCrud ({ model, body, id, req, reply, transaction, opti
|
|
|
54
52
|
if (options.limit) filter.limit = options.limit
|
|
55
53
|
if (options.sort) filter.sort = options.sort
|
|
56
54
|
if (options.page) filter.page = options.page
|
|
57
|
-
return { model: mdl, recId,
|
|
55
|
+
return { model: mdl, recId, opts, filter, attachment, stats, mimeType }
|
|
58
56
|
}
|
|
59
57
|
|
|
60
58
|
export async function getOneRecord (model, id, filter, options) {
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-04-17
|
|
4
|
+
|
|
5
|
+
- [2.16.2] Bug fix in ```WdbTable``` widget
|
|
6
|
+
- [2.16.2] Bug fix in ```WdbForm``` widget
|
|
7
|
+
- [2.16.2] Remove reduncat codes in ```WdbBase``` widget
|
|
8
|
+
|
|
9
|
+
## 2026-04-16
|
|
10
|
+
|
|
11
|
+
- [2.16.1] Bug fix in ```WdbBtnExport``` widget
|
|
12
|
+
- [2.16.1] Bug fix in ```WdbForm``` widget
|
|
13
|
+
- [2.16.1] Bug fix in ```WdbTable``` widget
|
|
14
|
+
- [2.16.1] Bug fix in ```prepCrud()```
|
|
15
|
+
- [2.16.1] Bug fix in ```createRecord()```
|
|
16
|
+
- [2.16.1] Bug fix in ```updateRecord()```
|
|
17
|
+
|
|
3
18
|
## 2026-04-13
|
|
4
19
|
|
|
5
20
|
- [2.16.0] Add ```oldData``` propety to ```WdbBase``` widget
|