export type PaginationType = { current : number, size : number, count : number } export type LoadMoreType = { contentdown : string, contentrefresh : string, contentnomore : string } export type SelectedItemType = { name : string, value : string, } export type GetCommandOptions = { collection ?: UTSJSONObject, field ?: string, orderby ?: string, where ?: any, pageData ?: string, pageCurrent ?: number, pageSize ?: number, getCount ?: boolean, getTree ?: any, getTreePath ?: UTSJSONObject, startwith ?: string, limitlevel ?: number, groupby ?: string, groupField ?: string, distinct ?: boolean, pageIndistinct ?: boolean, foreignKey ?: string, loadtime ?: string, manual ?: boolean } const DefaultSelectedNode = { text: '请选择', value: '' } export const dataPicker = defineMixin({ props: { localdata: { type: Array as PropType>, default: [] as Array }, collection: { type: Object, default: '' }, field: { type: String, default: '' }, orderby: { type: String, default: '' }, where: { type: Object, default: '' }, pageData: { type: String, default: 'add' }, pageCurrent: { type: Number, default: 1 }, pageSize: { type: Number, default: 20 }, getcount: { type: Boolean, default: false }, gettree: { type: Object, default: '' }, gettreepath: { type: Object, default: '' }, startwith: { type: String, default: '' }, limitlevel: { type: Number, default: 10 }, groupby: { type: String, default: '' }, groupField: { type: String, default: '' }, distinct: { type: Boolean, default: false }, pageIndistinct: { type: Boolean, default: false }, foreignKey: { type: String, default: '' }, loadtime: { type: String, default: 'auto' }, manual: { type: Boolean, default: false }, preload: { type: Boolean, default: false }, stepSearh: { type: Boolean, default: true }, selfField: { type: String, default: '' }, parentField: { type: String, default: '' }, multiple: { type: Boolean, default: false }, value: { type: Object, default: '' }, modelValue: { type: Object, default: '' }, defaultProps: { type: Object as PropType, } }, data() { return { loading: false, error: null as UniCloudError | null, treeData: [] as Array, selectedIndex: 0, selectedNodes: [] as Array, selectedPages: [] as Array[], selectedValue: '', selectedPaths: [] as Array, pagination: { current: 1, size: 20, count: 0 } as PaginationType } }, computed: { mappingTextName() : string { // TODO return (this.defaultProps != null) ? this.defaultProps!.getString('text', 'text') : 'text' }, mappingValueName() : string { // TODO return (this.defaultProps != null) ? this.defaultProps!.getString('value', 'value') : 'value' }, currentDataList() : Array { if (this.selectedIndex > this.selectedPages.length - 1) { return [] as Array } return this.selectedPages[this.selectedIndex] }, isLocalData() : boolean { return this.localdata.length > 0 }, isCloudData() : boolean { return this._checkIsNotNull(this.collection) }, isCloudDataList() : boolean { return (this.isCloudData && (this.parentField.length == 0 && this.selfField.length == 0)) }, isCloudDataTree() : boolean { return (this.isCloudData && this.parentField.length > 0 && this.selfField.length > 0) }, dataValue() : any { return this.hasModelValue ? this.modelValue : this.value }, hasCloudTreeData() : boolean { return this.treeData.length > 0 }, hasModelValue() : boolean { if (typeof this.modelValue == 'string') { const valueString = this.modelValue as string return (valueString.length > 0) } else if (Array.isArray(this.modelValue)) { const valueArray = this.modelValue as Array return (valueArray.length > 0) } return false }, hasCloudDataValue() : boolean { if (typeof this.dataValue == 'string') { const valueString = this.dataValue as string return (valueString.length > 0) } return false } }, created() { this.pagination.current = this.pageCurrent this.pagination.size = this.pageSize this.$watch( () : any => [ this.pageCurrent, this.pageSize, this.localdata, this.value, this.collection, this.field, this.getcount, this.orderby, this.where, this.groupby, this.groupField, this.distinct ], (newValue : Array, oldValue : Array) => { this.pagination.size = this.pageSize if (newValue[0] !== oldValue[0]) { this.pagination.current = this.pageCurrent } this.onPropsChange() } ) }, methods: { onPropsChange() { this.selectedIndex = 0 this.selectedNodes.length = 0 this.selectedPages.length = 0 this.selectedPaths.length = 0 // 加载数据 this.$nextTick(() => { this.loadData() }) }, onTabSelect(index : number) { this.selectedIndex = index }, onNodeClick(nodeData : UTSJSONObject) { if (nodeData.getBoolean('disable', false)) { return } const isLeaf = this._checkIsLeafNode(nodeData) this._trimSelectedNodes(nodeData) this.$emit('nodeclick', nodeData) if (this.isLocalData) { if (isLeaf || !this._checkHasChildren(nodeData)) { this.onFinish() } } else if (this.isCloudDataList) { this.onFinish() } else if (this.isCloudDataTree) { if (isLeaf) { this.onFinish() } else if (!this._checkHasChildren(nodeData)) { // 尝试请求一次,如果没有返回数据标记为叶子节点 this.loadCloudDataNode(nodeData) } } }, getChangeNodes(): Array { const nodes: Array = [] this.selectedNodes.forEach((node : UTSJSONObject) => { const newNode: UTSJSONObject = {} newNode[this.mappingTextName] = node.getString(this.mappingTextName) newNode[this.mappingValueName] = node.getString(this.mappingValueName) nodes.push(newNode) }) return nodes }, onFinish() { }, // 加载数据(自动判定环境) loadData() { if (this.isLocalData) { this.loadLocalData() } else if (this.isCloudDataList) { this.loadCloudDataList() } else if (this.isCloudDataTree) { this.loadCloudDataTree() } }, // 加载本地数据 loadLocalData() { this.treeData = this.localdata if (Array.isArray(this.dataValue)) { const value = this.dataValue as Array this.selectedPaths = value.slice(0) this._pushSelectedTreeNodes(value, this.localdata) } else { this._pushSelectedNodes(this.localdata) } }, // 加载 Cloud 数据 (单列) loadCloudDataList() { this._loadCloudData(null, (data : Array) => { this.treeData = data this._pushSelectedNodes(data) }) }, // 加载 Cloud 数据 (树形) loadCloudDataTree() { let commandOptions = { field: this._cloudDataPostField(), where: this._cloudDataTreeWhere(), getTree: true } as GetCommandOptions if (this._checkIsNotNull(this.gettree)) { commandOptions.startwith = `${this.selfField}=='${this.dataValue as string}'` } this._loadCloudData(commandOptions, (data : Array) => { this.treeData = data if (this.selectedPaths.length > 0) { this._pushSelectedTreeNodes(this.selectedPaths, data) } else { this._pushSelectedNodes(data) } }) }, // 加载 Cloud 数据 (节点) loadCloudDataNode(nodeData : UTSJSONObject) { const commandOptions = { field: this._cloudDataPostField(), where: this._cloudDataNodeWhere() } as GetCommandOptions this._loadCloudData(commandOptions, (data : Array) => { nodeData['children'] = data if (data.length == 0) { nodeData['isleaf'] = true this.onFinish() } else { this._pushSelectedNodes(data) } }) }, // 回显 Cloud Tree Path loadCloudDataPath() { if (!this.hasCloudDataValue) { return } const command : GetCommandOptions = {} // 单列 if (this.isCloudDataList) { // 根据 field's as value标识匹配 where 条件 let where : Array = []; let whereField = this._getForeignKeyByField(); if (whereField.length > 0) { where.push(`${whereField} == '${this.dataValue as string}'`) } let whereString = where.join(' || ') if (this._checkIsNotNull(this.where)) { whereString = `(${this.where}) && (${whereString})` } command.field = this._cloudDataPostField() command.where = whereString } // 树形 if (this.isCloudDataTree) { command.field = this._cloudDataPostField() command.getTreePath = { startWith: `${this.selfField}=='${this.dataValue as string}'` } } this._loadCloudData(command, (data : Array) => { this._extractTreePath(data, this.selectedPaths) }) }, _loadCloudData(options ?: GetCommandOptions, callback ?: ((data : Array) => void)) { if (this.loading) { return } this.loading = true this.error = null this._getCommand(options).then((response : UniCloudDBGetResult) => { callback?.(response.data) }).catch((err : any | null) => { this.error = err as UniCloudError }).finally(() => { this.loading = false }) }, _cloudDataPostField() : string { let fields = [this.field]; if (this.parentField.length > 0) { fields.push(`${this.parentField} as parent_value`) } return fields.join(',') }, _cloudDataTreeWhere() : string { let result : Array = [] let selectedNodes = this.selectedNodes.length > 0 ? this.selectedNodes : this.selectedPaths let parentField = this.parentField if (parentField.length > 0) { result.push(`${parentField} == null || ${parentField} == ""`) } if (selectedNodes.length > 0) { for (var i = 0; i < selectedNodes.length - 1; i++) { const parentFieldValue = selectedNodes[i].getString('value', '') result.push(`${parentField} == '${parentFieldValue}'`) } } let where : Array = [] if (this._checkIsNotNull(this.where)) { where.push(`(${this.where as string})`) } if (result.length > 0) { where.push(`(${result.join(' || ')})`) } return where.join(' && ') }, _cloudDataNodeWhere() : string { const where : Array = [] if (this.selectedNodes.length > 0) { const value = this.selectedNodes[this.selectedNodes.length - 1].getString('value', '') where.push(`${this.parentField} == '${value}'`) } let whereString = where.join(' || ') if (this._checkIsNotNull(this.where)) { return `(${this.where as string}) && (${whereString})` } return whereString }, _getWhereByForeignKey() : string { let result : Array = [] let whereField = this._getForeignKeyByField(); if (whereField.length > 0) { result.push(`${whereField} == '${this.dataValue as string}'`) } if (this._checkIsNotNull(this.where)) { return `(${this.where}) && (${result.join(' || ')})` } return result.join(' || ') }, _getForeignKeyByField() : string { const fields = this.field.split(',') let whereField = '' for (let i = 0; i < fields.length; i++) { const items = fields[i].split('as') if (items.length < 2) { continue } if (items[1].trim() === 'value') { whereField = items[0].trim() break } } return whereField }, _getCommand(options ?: GetCommandOptions) : Promise { let db = uniCloud.databaseForJQL() let collection = Array.isArray(this.collection) ? db.collection(...(this.collection as Array)) : db.collection(this.collection) let filter : UniCloudDBFilter | null = null if (this.foreignKey.length > 0) { filter = collection.foreignKey(this.foreignKey) } const where : any = options?.where ?? this.where if (typeof where == 'string') { const whereString = where as string if (whereString.length > 0) { filter = (filter != null) ? filter.where(where) : collection.where(where) } } else { filter = (filter != null) ? filter.where(where) : collection.where(where) } let query : UniCloudDBQuery | null = null if (this.field.length > 0) { query = (filter != null) ? filter.field(this.field) : collection.field(this.field) } if (this.groupby.length > 0) { if (query != null) { query = query.groupBy(this.groupby) } else if (filter != null) { query = filter.groupBy(this.groupby) } } if (this.groupField.length > 0) { if (query != null) { query = query.groupField(this.groupField) } else if (filter != null) { query = filter.groupField(this.groupField) } } if (this.distinct == true) { if (query != null) { query = query.distinct(this.field) } else if (filter != null) { query = filter.distinct(this.field) } } if (this.orderby.length > 0) { if (query != null) { query = query.orderBy(this.orderby) } else if (filter != null) { query = filter.orderBy(this.orderby) } } const size = this.pagination.size const current = this.pagination.current if (query != null) { query = query.skip(size * (current - 1)).limit(size) } else if (filter != null) { query = filter.skip(size * (current - 1)).limit(size) } else { query = collection.skip(size * (current - 1)).limit(size) } const getOptions = {} const treeOptions = { limitLevel: this.limitlevel, startWith: this.startwith } if (this.getcount == true) { getOptions['getCount'] = this.getcount } const getTree : any = options?.getTree ?? this.gettree if (typeof getTree == 'string') { const getTreeString = getTree as string if (getTreeString.length > 0) { getOptions['getTree'] = treeOptions } } else if (typeof getTree == 'object') { getOptions['getTree'] = treeOptions } else { getOptions['getTree'] = getTree } const getTreePath = options?.getTreePath ?? this.gettreepath if (typeof getTreePath == 'string') { const getTreePathString = getTreePath as string if (getTreePathString.length > 0) { getOptions['getTreePath'] = getTreePath } } else { getOptions['getTreePath'] = getTreePath } return query.get(getOptions) }, _checkIsNotNull(value : any) : boolean { if (typeof value == 'string') { const valueString = value as string return (valueString.length > 0) } else if (value instanceof UTSJSONObject) { return true } return false }, _checkIsLeafNode(nodeData : UTSJSONObject) : boolean { if (this.selectedIndex >= this.limitlevel) { return true } if (nodeData.getBoolean('isleaf', false)) { return true } return false }, _checkHasChildren(nodeData : UTSJSONObject) : boolean { const children = nodeData.getArray('children') ?? ([] as Array) return children.length > 0 }, _pushSelectedNodes(nodes : Array) { this.selectedNodes.push(DefaultSelectedNode) this.selectedPages.push(nodes) this.selectedIndex = this.selectedPages.length - 1 }, _trimSelectedNodes(nodeData : UTSJSONObject) { this.selectedNodes.splice(this.selectedIndex) this.selectedNodes.push(nodeData) if (this.selectedPages.length > 0) { this.selectedPages.splice(this.selectedIndex + 1) } const children = nodeData.getArray('children') ?? ([] as Array) if (children.length > 0) { this.selectedNodes.push(DefaultSelectedNode) this.selectedPages.push(children) } this.selectedIndex = this.selectedPages.length - 1 }, _pushSelectedTreeNodes(paths : Array, nodes : Array) { let children : Array = nodes paths.forEach((node : UTSJSONObject) => { const findNode = children.find((item : UTSJSONObject) : boolean => { return (item.getString(this.mappingValueName) == node.getString(this.mappingValueName)) }) if (findNode != null) { this.selectedPages.push(children) this.selectedNodes.push(node) children = findNode.getArray('children') ?? ([] as Array) } }) this.selectedIndex = this.selectedPages.length - 1 }, _extractTreePath(nodes : Array, result : Array) { if (nodes.length == 0) { return } const node = nodes[0] result.push(node) const children = node.getArray('children') if (Array.isArray(children) && children!.length > 0) { this._extractTreePath(children, result) } } } })