waibu-db 2.17.2 → 2.18.1
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/widget/data-table.js +8 -14
- package/extend/waibuBootstrap/theme/component/widget/form.js +2 -3
- package/extend/waibuBootstrap/theme/component/widget/lookup-select.js +5 -5
- package/extend/waibuRestApi/route/@model/@id/get.js +2 -2
- package/extend/waibuRestApi/route/@model/@id/remove.js +2 -2
- package/extend/waibuRestApi/route/@model/@id/update.js +2 -2
- package/extend/waibuRestApi/route/@model/create.js +2 -2
- package/extend/waibuRestApi/route/@model/find.js +2 -2
- package/extend/waibuRestApi/route/@model/stat/@stat/find.js +2 -2
- package/index.js +0 -47
- package/lib/crud/add-handler.js +1 -1
- package/lib/crud/all-handler.js +1 -2
- package/lib/crud/edit-handler.js +1 -1
- package/lib/method/get-schema-ext.js +13 -14
- package/lib/util.js +2 -2
- package/package.json +1 -1
- package/wiki/CHANGES.md +6 -0
|
@@ -116,27 +116,21 @@ async function table () {
|
|
|
116
116
|
const lines = []
|
|
117
117
|
if (selection) {
|
|
118
118
|
const tag = selection === 'single' ? 'formRadio' : 'formCheck'
|
|
119
|
-
const attr = { 'x-model': 'selected', name: '_rt', value: d.
|
|
119
|
+
const attr = { 'x-model': 'selected', name: '_rt', value: d.id, noLabel: true, noWrapper: true }
|
|
120
120
|
const type = find(schema.properties, { name: 'id' }).type
|
|
121
|
-
const prepend = `<td data-value="${d.
|
|
121
|
+
const prepend = `<td data-value="${d.id}" data-key="id" data-type="${type}">`
|
|
122
122
|
lines.push(await this.component.buildTag({ tag, attr, prepend, append: '</td>' }))
|
|
123
123
|
}
|
|
124
124
|
for (const f of schema.view.fields) {
|
|
125
125
|
if (!fields.includes(f)) continue
|
|
126
|
-
|
|
127
|
-
if (!prop) prop = find(schema.view.calcFields, { name: f })
|
|
126
|
+
const prop = find(schema.properties, { name: f })
|
|
128
127
|
if (!prop) continue
|
|
129
|
-
let dataValue = d
|
|
128
|
+
let dataValue = d[f]
|
|
130
129
|
if (['datetime'].includes(prop.type) && dataValue instanceof Date && !isNaN(dataValue)) dataValue = escape(dataValue.toISOString())
|
|
131
130
|
else if (['string', 'text'].includes(prop.type)) dataValue = escape(dataValue)
|
|
132
131
|
else if (['array', 'object'].includes(prop.type)) dataValue = escape(JSON.stringify(dataValue))
|
|
133
132
|
const refName = get(schema, `view.widget.${f}.attr.refName`)
|
|
134
|
-
let value = this.getRefValue({ field: f, data: d, refName }) ?? d[f]
|
|
135
|
-
const formatValue = get(schema, `view.formatValue.${f}`)
|
|
136
|
-
if (formatValue) {
|
|
137
|
-
value = await formatValue.call(this, value, d, { params: this.params, req })
|
|
138
|
-
dataValue = value
|
|
139
|
-
}
|
|
133
|
+
let value = this.getRefValue({ field: f, data: d, refName }) ?? get(d, `_fmt.${f}`, d[f])
|
|
140
134
|
if (!get(schema, 'view.noEscape', []).includes(f)) value = escape(value)
|
|
141
135
|
const attr = { dataValue, dataKey: prop.name, dataType: prop.type }
|
|
142
136
|
if (!disableds.includes('get')) attr.style = { cursor: 'pointer' }
|
|
@@ -150,9 +144,9 @@ async function table () {
|
|
|
150
144
|
const line = await this.component.buildTag({ tag: 'td', attr, html: value })
|
|
151
145
|
lines.push(line)
|
|
152
146
|
}
|
|
153
|
-
const attr = { id: `rec-${d.
|
|
154
|
-
if (!disableds.includes('update') || !disableds.includes('remove') || !disableds.includes('get')) attr['@click'] = `toggle('${d.
|
|
155
|
-
if (!disableds.includes('get')) attr['@dblclick'] = `goDetails('${d.
|
|
147
|
+
const attr = { id: `rec-${d.id}` }
|
|
148
|
+
if (!disableds.includes('update') || !disableds.includes('remove') || !disableds.includes('get')) attr['@click'] = `toggle('${d.id}')`
|
|
149
|
+
if (!disableds.includes('get')) attr['@dblclick'] = `goDetails('${d.id}')`
|
|
156
150
|
items.push(await this.component.buildTag({ tag: 'tr', attr, html: lines.join('\n') }))
|
|
157
151
|
}
|
|
158
152
|
html.push(await this.component.buildTag({ tag: 'tbody', attr: group.body, html: items.join('\n') }))
|
|
@@ -12,11 +12,10 @@ async function form () {
|
|
|
12
12
|
const { get, has } = this.app.lib._
|
|
13
13
|
const { stringifyAttribs } = this.app.waibuMpa
|
|
14
14
|
attr.dataType = prop.type
|
|
15
|
-
const cmp = prop.ref ? 'wdb-lookup-select' : widget.component
|
|
16
15
|
if (has(attr, 'name') && !has(attr, 'value')) {
|
|
17
|
-
attr.value =
|
|
16
|
+
attr.value = widget.component === 'form-plaintext' ? get(this, `oldData.${attr.name}`, attr.dataValue) : attr.dataValue
|
|
18
17
|
}
|
|
19
|
-
return `<c:${
|
|
18
|
+
return `<c:${widget.component} ${stringifyAttribs(attr)} />`
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
build = async () => {
|
|
@@ -21,10 +21,10 @@ async function lookupSelect () {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
this.params.attr.url = this.params.attr.url ?? `waibuDb.restapi:/lookup/${kebabCase(ref.model)}`
|
|
24
|
-
const
|
|
25
|
-
const attr = omit(this.params.attr,
|
|
26
|
-
for (const k of
|
|
27
|
-
attr[camelCase(`remote ${k}`)] = this.params.attr[k] ?? ref[k]
|
|
24
|
+
const keys = ['url', 'searchField', 'labelField', 'valueField']
|
|
25
|
+
const attr = omit(this.params.attr, keys)
|
|
26
|
+
for (const k of keys) {
|
|
27
|
+
attr[camelCase(`remote ${k}`)] = this.params.attr[k] ?? ref[k] ?? ref.field
|
|
28
28
|
}
|
|
29
29
|
const q = set({}, this.params.attr.searchField ?? get(ref, 'searchField', 'id'), ['__REGEXP__', '{searchItem}', 'i'])
|
|
30
30
|
attr.remoteQuery = base64JsonEncode({ $and: [parseQuery(get(ref, 'query', {})), q] })
|
|
@@ -32,7 +32,7 @@ async function lookupSelect () {
|
|
|
32
32
|
attr.clearBtn = true
|
|
33
33
|
const sentence = `<c:form-select-ext ${Object.entries(attr).map(([k, v]) => `${kebabCase(k)}="${v}"`).join(' ')} />`
|
|
34
34
|
this.params.html = await this.component.buildSentence(sentence, this.component.locals)
|
|
35
|
-
this.params.attr = omit(this.params.attr, ['model', 'field', ...
|
|
35
|
+
this.params.attr = omit(this.params.attr, ['model', 'field', ...keys])
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
async function stat (req, reply) {
|
|
1
|
+
async function stat (req, reply, options) {
|
|
2
2
|
const { camelCase } = this.app.lib._
|
|
3
3
|
const method = camelCase(`create ${req.params.stat}`)
|
|
4
4
|
if (!this[method]) throw this.error('_notFound')
|
|
5
|
-
return await this[method]({ req, reply })
|
|
5
|
+
return await this[method]({ req, reply, options })
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export default stat
|
package/index.js
CHANGED
|
@@ -167,53 +167,6 @@ async function factory (pkgName) {
|
|
|
167
167
|
return await this.findAllRecord({ model, req, options })
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
formatRecord = async ({ data, req, schema, options = {} }) => {
|
|
171
|
-
const { isArray } = this.app.lib._
|
|
172
|
-
if (!isArray(data)) return await this.formatRow({ data, req, schema, options })
|
|
173
|
-
const items = []
|
|
174
|
-
for (const d of data) {
|
|
175
|
-
const item = await this.formatRow({ data: d, req, schema, options })
|
|
176
|
-
items.push(item)
|
|
177
|
-
}
|
|
178
|
-
return items
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
formatRow = async ({ data, req, schema, options = {} }) => {
|
|
182
|
-
const { get, find, isFunction, cloneDeep } = this.app.lib._
|
|
183
|
-
const { format, callHandler } = this.app.bajo
|
|
184
|
-
const { escape } = this.app.waibu
|
|
185
|
-
const fields = get(schema, 'view.fields', Object.keys(schema.properties))
|
|
186
|
-
const rec = cloneDeep(data)
|
|
187
|
-
const lang = get(req, 'lang')
|
|
188
|
-
const unitSys = get(req, 'site.setting.sumba.unitSys')
|
|
189
|
-
const timeZone = get(req, 'site.setting.sumba.timeZone', this.app.bajo.config.intl.format.datetime.timeZone)
|
|
190
|
-
for (const f of fields) {
|
|
191
|
-
if (f === '_ref') continue
|
|
192
|
-
let prop = find(schema.properties, { name: f })
|
|
193
|
-
if (!prop) prop = find(schema.view.calcFields, { name: f })
|
|
194
|
-
if (!prop) continue
|
|
195
|
-
const opts = {
|
|
196
|
-
lang: options.lang ?? lang,
|
|
197
|
-
longitude: ['lng', 'longitude'].includes(f),
|
|
198
|
-
latitude: ['lat', 'latitude'].includes(f),
|
|
199
|
-
speed: ['speed'].includes(f),
|
|
200
|
-
degree: ['course', 'heading'].includes(f),
|
|
201
|
-
distance: ['distance'].includes(f),
|
|
202
|
-
unitSys: options.unitSys ?? unitSys,
|
|
203
|
-
datetime: options.datetime ?? { timeZone },
|
|
204
|
-
date: options.date ?? { timeZone },
|
|
205
|
-
time: options.time ?? { timeZone }
|
|
206
|
-
}
|
|
207
|
-
rec[f] = format(data[f], prop.type, opts)
|
|
208
|
-
const vf = get(schema, `view.formatValue.${f}`)
|
|
209
|
-
if (vf) {
|
|
210
|
-
if (isFunction(vf)) rec[f] = await vf.call(this, data[f], data, { req })
|
|
211
|
-
else rec[f] = await callHandler(vf, { req, value: data[f], data })
|
|
212
|
-
} else if (['string', 'text'].includes(prop.type)) rec[f] = escape(rec[f])
|
|
213
|
-
}
|
|
214
|
-
return rec
|
|
215
|
-
}
|
|
216
|
-
|
|
217
170
|
countRecord = countRecord
|
|
218
171
|
createAggregate = createAggregate
|
|
219
172
|
createHistogram = createHistogram
|
package/lib/crud/add-handler.js
CHANGED
|
@@ -17,7 +17,7 @@ async function addHandler ({ req, reply, model, params = {}, template, addOnsHan
|
|
|
17
17
|
const omitted = ['id', 'createdAt', 'updatedAt']
|
|
18
18
|
if (req.query.mode === 'clone' && req.query.id) {
|
|
19
19
|
const resp = await getRecord({ model, req, id: req.query.id, options: { fields: map(schema.properties, 'name'), ...opts } })
|
|
20
|
-
def = omit(resp.data
|
|
20
|
+
def = omit(resp.data, omitted)
|
|
21
21
|
} else {
|
|
22
22
|
const picked = map(schema.properties, 'name')
|
|
23
23
|
def = omit(pick(req.query, picked), omitted)
|
package/lib/crud/all-handler.js
CHANGED
|
@@ -20,8 +20,7 @@ async function allHandler ({ model, action, req, reply, template, params = {}, o
|
|
|
20
20
|
const { upperFirst, merge, keys } = this.app.lib._
|
|
21
21
|
if (!keys(handler).includes(action)) throw this.error('_notFound')
|
|
22
22
|
options.modelOpts = options.modelOpts ?? {}
|
|
23
|
-
options.modelOpts.
|
|
24
|
-
options.modelOpts.retainOriginalValue = true
|
|
23
|
+
options.modelOpts.fmt = true
|
|
25
24
|
if (['delete', 'export'].includes(action)) {
|
|
26
25
|
if (req.method === 'GET') throw this.error('_notFound')
|
|
27
26
|
return await handler[action].call(this, { model, req, reply, options })
|
package/lib/crud/edit-handler.js
CHANGED
|
@@ -23,7 +23,7 @@ async function editHandler ({ req, reply, model, id, params = {}, template, addO
|
|
|
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
|
|
26
|
+
const def = cloneDeep(old.data)
|
|
27
27
|
for (const k in def) {
|
|
28
28
|
if (isArray(def[k]) || isPlainObject(def[k])) def[k] = JSON.stringify(def[k])
|
|
29
29
|
}
|
|
@@ -7,12 +7,10 @@ const defFormatter = {}
|
|
|
7
7
|
function getCommons (action, schema, ext, options = {}) {
|
|
8
8
|
const { defaultsDeep } = this.app.lib.aneka
|
|
9
9
|
const { merge, map, get, set, without, uniq, pull } = this.app.lib._
|
|
10
|
-
const calcFields = get(ext, `view.${action}.calcFields`, get(ext, 'common.calcFields', []))
|
|
11
10
|
const forceVisible = get(ext, `view.${action}.forceVisible`, get(ext, 'common.forceVisible', []))
|
|
12
11
|
const widget = defaultsDeep(get(ext, `view.${action}.widget`), get(ext, 'common.widget', {}))
|
|
13
12
|
const noEscape = get(ext, `view.${action}.noEscape`, get(ext, 'common.noEscape', []))
|
|
14
13
|
const control = defaultsDeep(get(ext, `view.${action}.control`), get(ext, 'common.control', {}))
|
|
15
|
-
const formatValue = defaultsDeep(get(ext, `view.${action}.formatValue`), get(ext, 'common.formatValue', {}))
|
|
16
14
|
const formatCell = defaultsDeep(get(ext, `view.${action}.formatCell`), get(ext, 'common.formatCell', {}))
|
|
17
15
|
const format = defaultsDeep(get(ext, `view.${action}.format`), get(ext, 'common.format', {}))
|
|
18
16
|
const card = get(ext, `view.${action}.card`, get(ext, 'common.card', true))
|
|
@@ -20,7 +18,7 @@ function getCommons (action, schema, ext, options = {}) {
|
|
|
20
18
|
const disabled = get(ext, `view.${action}.disabled`, get(ext, 'common.disabled', []))
|
|
21
19
|
const x = defaultsDeep(get(ext, `view.${action}.x`), get(ext, 'common.x', {}))
|
|
22
20
|
const aggregate = get(ext, `view.${action}.stat.aggregate`, get(ext, 'common.stat.aggregate', []))
|
|
23
|
-
let attachment = get(ext, `view.${action}.attachment`, get(ext, 'common.attachment',
|
|
21
|
+
let attachment = get(ext, `view.${action}.attachment`, get(ext, 'common.attachment', schema.attachment))
|
|
24
22
|
if (!schema.attachment || action === 'list') attachment = false
|
|
25
23
|
hidden.push('siteId', ...schema.hidden, ...(options.hidden ?? []))
|
|
26
24
|
hidden = uniq(hidden)
|
|
@@ -28,10 +26,8 @@ function getCommons (action, schema, ext, options = {}) {
|
|
|
28
26
|
const allFields = without(map(schema.properties, 'name'), ...hidden)
|
|
29
27
|
const forFields = get(ext, `view.${action}.fields`, get(ext, 'common.fields', allFields))
|
|
30
28
|
set(schema, 'view.attachment', attachment)
|
|
31
|
-
set(schema, 'view.calcFields', calcFields)
|
|
32
29
|
set(schema, 'view.noEscape', noEscape)
|
|
33
30
|
set(schema, 'view.widget', widget)
|
|
34
|
-
set(schema, 'view.formatValue', formatValue)
|
|
35
31
|
set(schema, 'view.formatCell', formatCell)
|
|
36
32
|
set(schema, 'view.format', merge({}, defFormatter, format))
|
|
37
33
|
set(schema, 'view.stat.aggregate', aggregate)
|
|
@@ -40,9 +36,8 @@ function getCommons (action, schema, ext, options = {}) {
|
|
|
40
36
|
set(schema, 'view.x', x)
|
|
41
37
|
if (schema.disabled.length > 0) schema.view.disabled.push(...schema.disabled)
|
|
42
38
|
let fields = []
|
|
43
|
-
const calcFieldNames = map(calcFields, 'name')
|
|
44
39
|
for (const f of forFields) {
|
|
45
|
-
if (
|
|
40
|
+
if (allFields.includes(f)) fields.push(f)
|
|
46
41
|
}
|
|
47
42
|
fields = uniq(without(fields, ...hidden))
|
|
48
43
|
|
|
@@ -52,7 +47,7 @@ function getCommons (action, schema, ext, options = {}) {
|
|
|
52
47
|
if (noWrap === true) noWrap = fields
|
|
53
48
|
else if (noWrap === false) noWrap = []
|
|
54
49
|
set(schema, 'view.noWrap', noWrap)
|
|
55
|
-
return { fields, allFields, card
|
|
50
|
+
return { fields, allFields, card }
|
|
56
51
|
}
|
|
57
52
|
|
|
58
53
|
function autoLayout ({ action, schema, ext, layout }) {
|
|
@@ -91,14 +86,13 @@ function customLayout ({ action, schema, ext, layout, readonly }) {
|
|
|
91
86
|
|
|
92
87
|
function applyLayout (action, schema, ext) {
|
|
93
88
|
const { defaultsDeep } = this.app.lib.aneka
|
|
94
|
-
const { set, get, isEmpty, find
|
|
95
|
-
const { fields, card
|
|
89
|
+
const { set, get, isEmpty, find } = this.app.lib._
|
|
90
|
+
const { fields, card } = getCommons.call(this, action, schema, ext)
|
|
96
91
|
const layout = get(ext, `view.${action}.layout`, get(ext, 'common.layout', []))
|
|
97
92
|
const readonly = get(ext, `view.${action}.readonly`, get(ext, 'common.readonly', defReadonly))
|
|
98
93
|
const widget = {}
|
|
99
94
|
for (const f of fields) {
|
|
100
|
-
|
|
101
|
-
if (!prop) prop = find(calcFields, { name: f })
|
|
95
|
+
const prop = find(schema.properties, { name: f })
|
|
102
96
|
if (!prop) continue
|
|
103
97
|
const result = schema.view.widget[f] ?? {}
|
|
104
98
|
result.name = result.name ?? f
|
|
@@ -111,8 +105,11 @@ function applyLayout (action, schema, ext) {
|
|
|
111
105
|
if (action === 'details') {
|
|
112
106
|
result.component = 'form-plaintext'
|
|
113
107
|
} else {
|
|
108
|
+
if (prop.ref) {
|
|
109
|
+
result.component = result.component ?? 'wdb-lookup-select'
|
|
110
|
+
}
|
|
114
111
|
if (prop.type === 'boolean') {
|
|
115
|
-
result.component = 'form-select'
|
|
112
|
+
result.component = result.component ?? 'form-select'
|
|
116
113
|
result.attr.options = 'false:no;true:yes'
|
|
117
114
|
}
|
|
118
115
|
if (prop.values) {
|
|
@@ -126,6 +123,7 @@ function applyLayout (action, schema, ext) {
|
|
|
126
123
|
if (readonly.includes(f)) result.component = 'form-plaintext'
|
|
127
124
|
if (!result.component) result.component = 'form-input'
|
|
128
125
|
}
|
|
126
|
+
/*
|
|
129
127
|
for (const k in result.attr ?? {}) {
|
|
130
128
|
const newKey = kebabCase(k)
|
|
131
129
|
if (k !== newKey) {
|
|
@@ -133,6 +131,7 @@ function applyLayout (action, schema, ext) {
|
|
|
133
131
|
delete result.attr[k]
|
|
134
132
|
}
|
|
135
133
|
}
|
|
134
|
+
*/
|
|
136
135
|
widget[f] = result
|
|
137
136
|
}
|
|
138
137
|
set(schema, 'view.widget', widget)
|
|
@@ -182,7 +181,7 @@ async function getSchemaExt (modelName, view, options = {}) {
|
|
|
182
181
|
|
|
183
182
|
const model = isString(modelName) ? this.app.dobo.getModel(modelName) : modelName
|
|
184
183
|
const ns = model.plugin.ns
|
|
185
|
-
const schema = pick(model, ['name', 'properties', 'indexes', 'disabled', 'attachment', 'sortables', 'scanables', 'view', 'hidden'])
|
|
184
|
+
const schema = pick(model, ['name', 'properties', 'indexes', 'disabled', 'attachment', 'sortables', 'scanables', 'view', 'hidden', 'attachment'])
|
|
186
185
|
const base = options.base ?? path.basename(model.file, path.extname(model.file))
|
|
187
186
|
const parserOpts = { args: options.args }
|
|
188
187
|
let ext = await readConfig(`${ns}:/extend/waibuDb/schema/${base}.*`, { ns, baseNs: 'waibuDb', parserOpts })
|
package/lib/util.js
CHANGED
|
@@ -38,7 +38,7 @@ export async function prepCrud ({ model, id, req, reply, transaction, options =
|
|
|
38
38
|
|
|
39
39
|
const recId = id ?? params.id ?? req.query.id
|
|
40
40
|
let mdl = model
|
|
41
|
-
if (isString(model)) {
|
|
41
|
+
if (!model || isString(model)) {
|
|
42
42
|
model = model ?? pascalCase(params.model)
|
|
43
43
|
mdl = this.app.dobo.getModel(model)
|
|
44
44
|
}
|
|
@@ -59,7 +59,7 @@ export async function getOneRecord (model, id, filter, options) {
|
|
|
59
59
|
const { cloneDeep, pick, isEmpty } = this.app.lib._
|
|
60
60
|
let query = cloneDeep(filter.query || {})
|
|
61
61
|
query = { $and: [query, { id }] }
|
|
62
|
-
const opts = pick(options, ['forceNoHidden', 'trx', 'req', 'refs', '
|
|
62
|
+
const opts = pick(options, ['forceNoHidden', 'trx', 'req', 'refs', 'fmt'])
|
|
63
63
|
opts.dataOnly = false
|
|
64
64
|
const data = await model.findOneRecord({ query }, opts)
|
|
65
65
|
if (isEmpty(data.data) && options.throwNotFound) throw this.error('_notFound')
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-04-25
|
|
4
|
+
|
|
5
|
+
- [2.18.0] Change options to format value using the new key set by dobo
|
|
6
|
+
- [2.18.0] Remove ```options.retainOriginalValue``` since it is not needed anymore
|
|
7
|
+
- [2.18.0] Add all necessary ```options``` for all auto generated api endpoints
|
|
8
|
+
|
|
3
9
|
## 2026-04-23
|
|
4
10
|
|
|
5
11
|
- [2.17.2] Bug fix in ```getSchemaExt()```
|