Compare commits

...

10 Commits

Author SHA1 Message Date
luk
95c5416959 u 2025-12-09 20:41:30 +08:00
Luk
f9ffbb4989 u 2025-09-24 11:46:52 +08:00
Luk
0c05bfd464 u 2025-08-21 10:00:33 +08:00
luk
612f8ed85e u 2025-08-18 14:47:24 +08:00
luk
27cc887045 u 2025-08-18 14:41:27 +08:00
Luk
5f3cc1fb12 u 2025-06-21 20:27:44 +08:00
Luk
fc146dc10d u 2025-06-09 21:18:38 +08:00
Luk
3e52b5a9f4 bugfix in 'summarize_story' 2025-05-08 15:14:14 +08:00
Luk
280cba80d6 rename 'hash_easy' to 'hash_stable' and use 'stringify_by_keyorder' to make sure a stable outcome 2025-05-03 15:12:05 +08:00
Luk
df386d8266 u 2025-05-02 09:27:05 +08:00
2 changed files with 87 additions and 29 deletions

View File

@@ -58,7 +58,6 @@ module.exports = {
stringify_by_keyorder (obj, { cmp, cycles = false, space = '', replacer, schemaColumns, excludeKeys = [] } = {}) {
/* 这个解决方法不考虑缺省值,不能把嵌套对象也按顺序展开。*/
// return JSON.stringify(obj, Object.keys(schemaColumns || entity).sort().filter(key => ! excludeKeys.includes(key))) // JSON.stringify 可根据第二个数组参数的顺序排序,但这导致了嵌套对象不能按顺序展开。
let newObj = {}
if (schemaColumns) {
for (let key in schemaColumns) {
@@ -191,8 +190,8 @@ module.exports = {
return num
},
hash_easy (data, { hasher = 'sha256', salt, input = 'utf8', output = 'hex' } = {}) {
if (typeof data !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView)) data = JSON.stringify(data)
hash_stable (data, { hasher = 'sha256', salt, input = 'utf8', output = 'hex' } = {}) {
if (typeof data !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView)) data = this.stringify_by_keyorder(data)
if (salt && typeof salt === 'string') data = data + salt
const inputEncoding = input // my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView.
const outputEncoding = output === 'buf' ? undefined : output // (my.OUTPUT_LIST.indexOf(output)>=0?output:my.OUTPUT) // option.output: 留空=》默认输出hex格式或者手动指定 'buf', hex', 'latin1' or 'base64'
@@ -286,7 +285,10 @@ module.exports = {
let parent = root || globalThis || global || window || {}
let keychain = path.split('.')
for (let key of keychain) {
if (typeof parent === 'object' && /^\w+\(.*\)$/.test(key)) {
if (!parent) {
// 如果 parent 是 null 或 undefined直接返回空值。
return emptyValue
} else if (typeof parent === 'object' && /^\w+\(.*\)$/.test(key)) {
// 支持 myfunc(param) 作为一个路径节点。
let [all, func, param] = key.match(/^(\w+)\((.*)\)$/)
parent = parent[func](param)
@@ -330,19 +332,28 @@ module.exports = {
// 返回新的数组
filter_story (story) {
if (Array.isArray(story) && story.length) {
return story.filter((section) => Object.values(section || {}).some((val) => !this.is_empty(val))) // (section.text || section.image || section.video)?.trim?.()
return story.filter((section) => {
if (!section) return false
return Object.values(section).some((val) => !this.is_empty(val))
//return Object.entries(section).some(([key, val]) => !key.startsWith('_') && !this.is_empty(val)) // don't check properties '_xxx'
})
} else {
return []
}
},
extract_story_title (story) {
// 20250621 todo 如果 text 是 {enUS,zhCN} 怎么处理
extract_story_title ({ story, lang = 'enUS' } = {}) {
if (Array.isArray(story) && story.length) {
return (
story
.map(({ text = '' } = {}) => text.trim().replace(/\n/g, ' '))
.join(' ')
.substring(0, 140) || ''
.map(({ text = '' } = {}) => {
if (typeof text === 'string') return text.trim?.()?.replace?.(/\n+/g, ' ')
else if (typeof text === 'object') return (text[lang] || text['enUS'])?.trim?.()?.replace?.(/\n+/g, ' ')
})
?.join(' ')
?.trim?.()
?.substring?.(0, 140) || ''
)
} else {
return ''
@@ -352,7 +363,7 @@ module.exports = {
summarize_story (story = []) {
// story is an array of objects, each object could either be {text:'some string'}, {image: url} or {video:url}. Please construct a summary object as result: { textLength, imageCount, VideoCount }
return story.reduce(
(summary, { text, image, video, file } = {}) => {
(summary, { text, image, video, audio, file, linkTarget, _autocontent } = {}) => {
if (text) {
summary.textLength += text.length
summary.wordCount += text.split(/\s+/).length
@@ -360,12 +371,20 @@ module.exports = {
summary.imageCount++
} else if (video) {
summary.videoCount++
} else if (audio) {
summary.audioCount++
} else if (file) {
summary.fileCount++
} else if (linkTarget) {
summary.linkCount++
} else if (_autocontent) {
summary.emptyContent++
} else {
summary.untypeCount++
}
return summary
},
{ textLength: 0, wordCount: 0, imageCount: 0, videoCount: 0 }
{ textLength: 0, wordCount: 0, imageCount: 0, videoCount: 0, audioCount: 0, fileCount: 0, linkCount: 0, emptyContent: 0, untypeCount: 0 }
)
},
@@ -462,6 +481,7 @@ module.exports = {
})
},
// 洗牌算法,随机打乱数组顺序
shuffle_array (array = []) {
if (Array.isArray(array)) {
for (let i = array.length - 1; i > 0; i--) {
@@ -473,6 +493,6 @@ module.exports = {
},
is_uuid (uuid) {
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(unid)
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid)
},
}

View File

@@ -39,15 +39,19 @@ module.exports = {
is_text_file (fileName = '') {
const ext = /\./.test(fileName) ? fileName.split('.').pop().toLowerCase() : ''
return (wo.envar.textExtensionList || ['txt', 'text']).includes(ext)
return wo.envar.textExtensionList?.includes?.(ext)
},
is_image_file (fileName = '') {
const ext = /\./.test(fileName) ? fileName.split('.').pop().toLowerCase() : ''
return (wo.envar.imageExtensionList || ['jpg', 'jpeg', 'png', 'gif', 'webp', 'image']).includes(ext)
return wo.envar.imageExtensionList?.includes?.(ext)
},
is_video_file (fileName = '') {
const ext = /\./.test(fileName) ? fileName.split('.').pop().toLowerCase() : ''
return (wo.envar.videoExtensionList || ['avi', 'mp4', 'mov', 'wmv', 'video']).includes(ext)
return wo.envar.videoExtensionList?.includes?.(ext)
},
is_audio_file (fileName = '') {
const ext = /\./.test(fileName) ? fileName.split('.').pop().toLowerCase() : ''
return wo.envar.audioExtensionList?.includes?.(ext)
},
thisPage () {
@@ -60,15 +64,14 @@ module.exports = {
i18nText =
i18nText || // 如果传入i18n参数 ({zhCN:'...', enUS:'...'})
this.i18nText || // 1) 如果挂载到具体页面的 computed { lote: wo?.localizeText } 那么 this 就是当前页面,直接取用 this.i18nText 即可。2) 对于组件内定义的 i18nText要使用 this 来获得组件内的 i18nText
getCurrentPages()?.pop()?.i18nText // 如果不是挂载到 Vue.prototype 而是 挂载到 wo 下调用,那么 this.i18nText 就不存在了。因此通过 pageNow.i18nText 访问。
getCurrentPages?.()?.pop?.()?.i18nText // 如果不是挂载到 Vue.prototype 而是 挂载到 wo 下调用,那么 this.i18nText 就不存在了。因此通过 pageNow.i18nText 访问。
if (['string', 'number', 'boolean'].includes(typeof i18nText)) {
// 必须先检测是否标量值,如果直接返回 i18nText 可能返回{}等,导致依赖于返回空值的前端出错
return i18nText
} else if (typeof i18nText === 'object' && i18nText) {
return (
i18nText?.[langCode] ||
i18nText?.[my.get_mylang()] ||
(precise ? '' : i18nText?.earTH || i18nText?.defLAN || i18nText?.gloBAL || i18nText?.enUS || Object.values(i18nText)[0] || '')
i18nText[langCode || my.get_mylang()] ||
(precise ? '' : i18nText.earTH || i18nText.gloBAL || i18nText.defLAN || i18nText.enUS || Object.values(i18nText)[0] || '')
)
} else {
return ''
@@ -97,7 +100,11 @@ module.exports = {
)
windowTitle =
windowTitle || wo?.envar?.callnames?.[langNow] || wo?.pagesJson?.appInfo?.i18nText?.[langNow] || wo?.pagesJson?.globalStyle?.navigationBarTitleText || ''
windowTitle ||
this.localizeText(wo?.envar?.callnames, { langCode: langNow }) ||
this.localizeText(wo?.pagesJson?.appInfo?.i18nText, { langCode: langNow }) ||
wo?.pagesJson?.globalStyle?.navigationBarTitleText ||
''
if (wo.envar._clientInfo.deviceType === 'pc') {
uni.setNavigationBarTitle({ title: windowTitle + (navibarTitle ? ` - ${navibarTitle}` : '') })
@@ -139,7 +146,7 @@ module.exports = {
}
},
make_server_url (route, envar = globalThis.wo?.envar || {}) {
make_server_url (route, { envar = globalThis.wo?.envar, fullUrl = false } = {}) {
if (typeof route === 'string') route = route.replace('\\', '/')
else if (route?.apiWho && route?.apiTodo) {
const { apiVersion = 'api', apiWho, apiTodo } = route
@@ -156,22 +163,26 @@ module.exports = {
if (/^\/static\//.test(route)) {
return route
}
route = route.replace(/^\//, '')
// 对 route='abc.com' 这种,应当有 fullUrl===true就直接使用而不是组装成 pexserver.tic.cc/abc.com
// 纯数字和字母的cid
if (/^[\da-zA-Z]+$/.test(route) && envar.ipfsLens) {
return `${envar.ipfsLens.replace(/\/$/, '')}/${route.replace(/^\//, '')}`
if (/^[0-9a-zA-Z]+$/.test(route) && envar?.ipfsLens) {
return `${envar.ipfsLens.replace(/\/$/, '')}/${route}`
}
//// base url / 后台服务器url 需要组装。包括了 route === '_filestore/xxx' 的情况
route = route.replace(/^\//, '')
// 已有现成后端服务域名
if (envar.servUrl) {
if (fullUrl) {
return `http://${route}`
} else if (envar?.servUrl) {
return `${envar.servUrl.replace(/\/$/, '')}/${route}`
} else {
} else if (envar?.servHostname) {
// 需要组装后端服务域名
const hostname = envar.servHostname /*|| globalThis.window?.location?.hostname*/ || 'localhost'
const port = envar.servPort /*|| globalThis.window?.location?.port*/ || ''
const protocol = hostname === 'localhost' ? 'http' : envar.servProtocol || (process.env.NODE_ENV === 'production' ? 'https' : 'http')
return `${protocol}://${hostname}${port ? ':' : ''}${port}/${route}`
}
return route
},
make_bgurl (image) {
@@ -340,7 +351,13 @@ module.exports = {
filePath = fileDragged.filePath
filePicked = fileDragged
if (!mediaType) {
mediaType = this.is_image_file(fileDragged.name) ? 'image' : this.is_video_file(fileDragged.name) ? 'video' : 'file'
mediaType = this.is_image_file(fileDragged.name)
? 'image'
: this.is_video_file(fileDragged.name)
? 'video'
: this.is_audio_file(fileDragged.name)
? 'audio'
: 'file'
}
} else if (mediaType === 'image') {
let [errorChoose, { tempFilePaths, tempFiles } = {}] = await uni.chooseImage({ count, sizeType, sourceType })
@@ -364,6 +381,27 @@ module.exports = {
fileSize = size
filePicked = tempFile
filePath = tempFilePath
} else if (mediaType === 'audio') {
// #ifdef WEB
// https://uniapp.dcloud.net.cn/api/media/file.html
let [errorChoose, { tempFilePaths, tempFiles } = {}] = await uni.chooseFile({
count,
extension: wo.envar.audioExtensionList,
type: undefined,
}) // 20240429 但是测试下来 extension 参数无效
if (errorChoose) {
return {
_state: 'CER_FAIL_CHOOSE',
_msg: '', // { zhCN: '文件选择失败。请稍后再试,或向客服投诉。', enUS: 'File choose failed. Please try again later, or report to customer service.' },
}
}
fileSize = tempFiles?.[0]?.size
filePicked = tempFiles?.[0]
filePath = tempFilePaths?.[0]
// #endif
// #ifndef WEB
return { _state: 'UNSUPPORTED_FILETYPE', _msg: { zhCN: '请切换到网页端上传文件!', enUS: 'Please switch to Web App to upload files.' } }
// #endif
} else {
// #ifdef WEB
// https://uniapp.dcloud.net.cn/api/media/file.html