import * as YvanUI from './YvanUIExtend' import * as YvanMessage from './YvanUIMessage' import { Ajax } from './YvanUIAjax' import { GridDataSource, GridDataSourceSql, GridDataSourceServer, GridDataSourceAjax, GridDataSourceStaticFunction, WatchParam, GetParam, GridOnBeforeArgs, GridOnCUDBeforeArgs } from './YvanDataSourceGrid' import { brokerInvoke } from './Service' import { Db } from './YvanUIDb' import { YvEventDispatch } from './YvanEvent' import _ from 'lodash' export class YvanDataSourceGrid { private option: GridDataSource private ctl: any private readonly module: any private watches: Function[] = [] private isFirstAutoLoad: boolean = true //是否为第一次自动读取 private reload: undefined | (() => void) private rowCount: number | undefined public lastFilterModel: any public lastSortModel: any serverQuery = _.debounce((option: GridDataSourceSql | GridDataSourceServer | GridDataSourceAjax, paramFunction: undefined | (() => any), params: any) => { const that = this let needCount = false if (typeof that.rowCount === 'undefined') { //从来没有统计过 rowCount(记录数) needCount = true that.lastFilterModel = _.cloneDeep(params.filterModel) that.lastSortModel = _.cloneDeep(params.sortModel) } else { if (!_.isEqual(that.lastFilterModel, params.filterModel)) { //深度对比,如果 filter 模型更改了,需要重新统计 rowCount(记录数) needCount = true that.lastFilterModel = _.cloneDeep(params.filterModel) that.lastSortModel = _.cloneDeep(params.sortModel) } } // 获取所有参数 const queryParams = { ...(typeof paramFunction === 'function' ? paramFunction() : undefined) } let ajaxPromise: Promise; if (option.type === 'SQL') { const ajaxParam = { params: queryParams, limit: params.endRow - params.startRow, limitOffset: params.startRow, needCount, sortModel: params.sortModel, filterModel: params.filterModel, sqlId: option.sqlId }; const beforeArgs: GridOnBeforeArgs = { requestParam: ajaxParam, handle: false, successCallback: params.successCallback, failCallback: params.failCallback } YvEventDispatch(option.onBefore, that.ctl, beforeArgs) if (beforeArgs.handle === true) { // 不允许请求 return; } ajaxPromise = YvanUI.dbs[option.db].query(ajaxParam) } else if (option.type === 'Server') { const [serverUrl, method] = _.split(option.method, '@'); const ajaxParam = { params: queryParams, limit: params.endRow - params.startRow, limitOffset: params.startRow, needCount, sortModel: params.sortModel, filterModel: params.filterModel, } const beforeArgs: GridOnBeforeArgs = { requestParam: ajaxParam, handle: false, successCallback: params.successCallback, failCallback: params.failCallback } YvEventDispatch(option.onBefore, that.ctl, beforeArgs) if (beforeArgs.handle === true) { // 不允许请求 return; } ajaxPromise = >brokerInvoke(YvanUI.getServerPrefix(serverUrl), method, ajaxParam) } else if (option.type === 'Ajax') { const ajax: Ajax.Function = _.get(window, 'YvanUI.ajax'); const ajaxParam: Ajax.Option = { url: option.url, method: 'POST-JSON', data: { params: queryParams, limit: params.endRow - params.startRow, limitOffset: params.startRow, needCount, sortModel: params.sortModel, filterModel: params.filterModel, } } const beforeArgs: GridOnBeforeArgs = { requestParam: ajaxParam, handle: false, successCallback: params.successCallback, failCallback: params.failCallback } YvEventDispatch(option.onBefore, that.ctl, beforeArgs) if (beforeArgs.handle === true) { // 不允许请求 return; } ajaxPromise = >ajax(ajaxParam); } else { console.error('unSupport dataSource mode:', option); params.failCallback(); return; } if (that.ctl.loading_data == true) { return; } //异步请求数据内容 that.ctl.loading = true that.ctl.loading_data = true ajaxPromise.then(res => { YvEventDispatch(option.onAfter, that.ctl, res) const { data: resultData, pagination, params: resParams } = res if (needCount) { if (_.has(res, 'totalCount')) { // 兼容老模式 that.rowCount = _.get(res, 'totalCount'); } else { that.rowCount = pagination.total } } params.successCallback(resultData, that.rowCount) /** 如果不分页就在这里设置总条目数量,避免多次刷新分页栏 **/ if (!that.ctl.pagination) { that.ctl.gridPage.itemCount = that.rowCount } that.ctl._bindingComplete() if (that.ctl.entityName) { _.set(that.module, that.ctl.entityName + '.selectedRow', that.ctl.getSelectedRow()) } }).catch(r => { params.failCallback() }).finally(() => { that.ctl.loading_data = false this.ctl.loading = false }) }) /** * SQL取值 */ setSqlMode(option: GridDataSourceSql | GridDataSourceServer | GridDataSourceAjax, paramFunction: undefined | (() => any)) { const that = this if (that.ctl._module.isDesignMode === true) { return; } this.reload = () => { this.ctl.loading = true that.clearRowCount() if (that.ctl.entityName) { _.set(that.module, that.ctl.entityName + '.selectedRow', undefined) } that.ctl.gridApi.hasDataSource = true if (that.ctl.pagination) { /** 分页模式 **/ that.ctl.gridPage.getPageData = (currentPage: number, pageSize: number) => { let params: any = {} params.successCallback = (data: [], rowCount: number) => { // if (needClearRefresh) { // that.ctl.setData(data) // } else { // 不能直接用 setData, 会造成 filter 被置空 // 使用 _transactionUpdate 也有 bug ,如果查询条件被改变,也不会分页回顶端 that.ctl._transactionUpdate(data) // } // that.ctl.setData(data) that.ctl.gridPage.itemCount = rowCount that.ctl.gridPage.currentPage = currentPage that.ctl._bindingComplete(); } params.failCallback = () => { console.error('error') } params.startRow = (currentPage - 1) * pageSize params.endRow = currentPage * pageSize params.filterModel = that.ctl.gridApi.getFilterModel() params.sortModel = that.ctl.gridApi.getSortModel() if (that.isFirstAutoLoad && that.ctl.autoLoad === false) { that.rowCount = 0 params.successCallback([], that.rowCount) that.ctl.loading = false that.isFirstAutoLoad = false } else { that.serverQuery(option, paramFunction, params) } } that.ctl.gridPage.getPageData(1, that.ctl.gridPage.pageSize); } else { /** 无限滚动模式 **/ that.ctl.gridApi.setDatasource({ getRows: (params: any) => { if (that.isFirstAutoLoad && that.ctl.autoLoad === false) { that.rowCount = 0 params.successCallback([], that.rowCount) that.ctl.loading = false that.isFirstAutoLoad = false return } that.serverQuery(option, paramFunction, params) } }) } } this.reload() } /** * 自定义函数式取值 */ setCustomFunctionMode(option: GridDataSourceStaticFunction, paramFunction: undefined | (() => any)) { const that = this this.reload = () => { that.clearRowCount() if (that.ctl.entityName) { _.set(that.module, that.ctl.entityName + '.selectedRow', undefined) } that.ctl.loading = true if (that.ctl.pagination) { that.ctl.gridPage.getPageData = (currentPage: number, pageSize: number) => { let params: any = {} params.successCallback = (data: [], rowCount: number) => { // if (needClearRefresh) { // that.ctl.setData(data) // } else { // 不能直接用 setData, 会造成 filter 被置空 // 使用 _transactionUpdate 也有 bug ,如果查询条件被改变,也不会分页回顶端 that.ctl._transactionUpdate(data) // } // that.ctl.setData(data) that.ctl.gridPage.itemCount = rowCount that.ctl.gridPage.currentPage = currentPage } params.failCallback = () => { console.error('error') } params.startRow = (currentPage - 1) * pageSize params.endRow = currentPage * pageSize params.filterModel = that.ctl.gridApi.getFilterModel() params.sortModel = that.ctl.gridApi.getSortModel() if (that.isFirstAutoLoad && that.ctl.autoLoad === false) { that.rowCount = 0 params.successCallback([], that.rowCount) that.ctl.loading = false that.isFirstAutoLoad = false } else { option.call(that.module, that.ctl, { param: typeof paramFunction === 'function' ? paramFunction() : undefined, failCallback: () => { params.failCallback() }, successCallback: (data: any[], dataLength: number | undefined) => { if (!dataLength) { dataLength = data.length } if (params.startRow === 0 && dataLength <= params.endRow) { params.successCallback(data, dataLength) } else { params.successCallback(_.slice(data, params.startRow, dataLength), dataLength) } that.ctl.loading = false that.ctl.gridPage.itemCount = dataLength that.ctl._bindingComplete() if (that.ctl.entityName) { _.set( that.module, that.ctl.entityName + '.selectedRow', that.ctl.getSelectedRow() ) } } }) } } that.ctl.gridPage.getPageData(1, that.ctl.gridPage.pageSize); } else { // rowModelType = infinite that.ctl.gridApi.setDatasource({ getRows: (params: any) => { that.ctl.loading = true if (that.isFirstAutoLoad && that.ctl.autoLoad === false) { that.rowCount = 0 params.successCallback([], that.rowCount) that.ctl.loading = false that.isFirstAutoLoad = false return } option.call(that.module, that.ctl, { param: typeof paramFunction === 'function' ? paramFunction() : undefined, failCallback: () => { params.failCallback() }, successCallback: (data: any[], dataLength: number | undefined) => { if (!dataLength) { dataLength = data.length } if (params.startRow === 0 && dataLength <= params.endRow) { params.successCallback(data, dataLength) } else { params.successCallback(_.slice(data, params.startRow, dataLength), dataLength) } that.ctl.loading = false that.ctl.gridPage.itemCount = dataLength that.ctl._bindingComplete() if (that.ctl.entityName) { _.set( that.module, that.ctl.entityName + '.selectedRow', that.ctl.getSelectedRow() ) } } }) } }) } } this.reload() } setCodeArrayMode(option: Array) { const that = this const rowCount = option.length this.reload = () => { this.ctl.loading = true that.clearRowCount() if (that.ctl.entityName) { _.set(that.module, that.ctl.entityName + '.selectedRow', undefined) } that.ctl.gridApi.hasDataSource = true if (that.ctl.pagination) { /** 分页模式 **/ that.ctl.gridPage.getPageData = (currentPage: number, pageSize: number) => { let d: any[] = [] const startRow = (currentPage - 1) * pageSize let endRow = currentPage * pageSize endRow = endRow > rowCount ? rowCount : endRow for (let i = startRow; i < endRow; i++) { d.push(option[i]) } that.ctl.setData(d) that.ctl.gridPage.itemCount = rowCount that.ctl.gridPage.currentPage = currentPage } that.ctl.gridPage.getPageData(1, that.ctl.gridPage.pageSize) } else { /** 不分页模式 **/ that.ctl.setData(option) that.ctl.gridPage.itemCount = rowCount } } this.reload() } constructor(ctl: any, option: GridDataSource) { if (ctl._webix.$scope.isDesignMode === true) { return } this.ctl = ctl this.option = option this.module = ctl._webix.$scope if (!option) { //没有设值,退出 this.reload = undefined return } if (_.isArray(option)) { this.setCodeArrayMode(option) return } if (typeof option === 'function') { //以 function 方式运行 this.setCustomFunctionMode(option, undefined) return } // 使 watch 生效 _.forOwn(option.params, value => { if (!_.has(value, '$watch')) { return } const watchOption: WatchParam = value this.module.$watch(watchOption.$watch, () => { if (this.reload) { ctl.reload(2); } }) }) // params 函数 let paramFunction = () => { const result: any = {} _.forOwn(option.params, (value, key) => { if (_.has(value, '$get')) { const getOption: GetParam = value result[key] = _.get(this.module, getOption.$get) } else if (_.has(value, '$watch')) { const watchOption: WatchParam = value result[key] = _.get(this.module, watchOption.$watch) } else { result[key] = value } }) return result } if (option.type === 'function') { if (typeof option.bind === 'function') { this.setCustomFunctionMode(option.bind, paramFunction) } else { // 取 bind 函数 const bindFunction: GridDataSourceStaticFunction = _.get(this.module, option.bind) as GridDataSourceStaticFunction if (!bindFunction) { console.error(`没有找到名称为 ${option.bind} 的方法`) return } this.setCustomFunctionMode(bindFunction, paramFunction) } return } if (option.type === 'SQL' || option.type === 'Server' || option.type === 'Ajax') { this.setSqlMode(option, paramFunction) return } console.error(`其他方式没有实现`) } /** * 释放与 YvGrid 的绑定 */ destory() { // 解除全部 watch _.each(this.watches, unwatch => { unwatch() }) this.reload = undefined } /** * 清空 rowCount, 下次重新统计总行数 */ clearRowCount() { delete this.rowCount } updateSupport(): boolean { return false } _updateRow(param: any) { const option: any = this.option if (!option.updatePath) { param.node.cstate = 'same' return; // throw new Error('no updatePath') } const [serverUrl, method] = _.split(option.updatePath, '@'); const wheres = {} _.set(wheres, this.ctl.idField, this.ctl._getIdByRow(param.data)) const sets = {} _.forEach(this.ctl.vjson.columns, column => { if (column.field != this.ctl.idField) { const newValue = _.get(param.data, column.field) const oldValue = _.get(param.node.origin, column.field) if (newValue != oldValue) { if (column.editMode === "date" || column.editMode === 'datetime') { let formatter = column.formatter if (!YvanUI.formatter.hasOwnProperty(column.formatter)) { console.error('没有发现全局函数 YvanUI.formatter.' + column.formatter) _.set(sets, column.field, newValue) } else { formatter = YvanUI.formatter[column.formatter] const dateValue = formatter(newValue) if (dateValue) { _.set(sets, column.field, dateValue) } else { _.set(sets, column.field, newValue) } } } else { if (newValue) { _.set(sets, column.field, newValue) } } } } }) if (_.size(sets) <= 0) { param.node.cstate = 'same' return } const ajaxParam = { args: [wheres, sets] } const beforeArgs: GridOnCUDBeforeArgs = { requestParam: ajaxParam, handle: false } YvEventDispatch(option.onUpdateBefore, this.ctl, beforeArgs) if (beforeArgs.handle === true) { // 不允许请求 param.node.cstate = 'same' return; } let ajaxPromise = >brokerInvoke(YvanUI.getServerPrefix(serverUrl), method, ajaxParam) //异步处理数据内容 this.ctl.loading = true const that = this ajaxPromise.then(res => { YvEventDispatch(option.onUpdateAfter, that.ctl, res) if (res.success) { that.ctl._acceptChanges(param.node) } }).catch(r => { YvanMessage.msg('修改失败') }).finally(() => { this.ctl.loading = false }) } _addRow(param: any) { const option: any = this.option if (!option.createPath) { param.node.cstate = 'same' return; // throw new Error('no createPath') } const [serverUrl, method] = _.split(option.createPath, '@'); const newData = {} _.forEach(this.ctl.vjson.columns, column => { if (column.field != this.ctl.idField) { const newValue = _.get(param.data, column.field) if (!newValue) { return } if (column.editMode === "date" || column.editMode === 'datetime') { let formatter = column.formatter if (!YvanUI.formatter.hasOwnProperty(column.formatter)) { console.error('没有发现全局函数 YvanUI.formatter.' + column.formatter) _.set(newData, column.field, newValue) } else { formatter = YvanUI.formatter[column.formatter] const dateValue = formatter(newValue) if (dateValue) { _.set(newData, column.field, dateValue) } else { _.set(newData, column.field, newValue) } } } else { if (newValue) { _.set(newData, column.field, newValue) } } } }) if (_.size(newData) <= 0) { param.node.cstate = 'same' return } const ajaxParam = { args: [newData] } const beforeArgs: GridOnCUDBeforeArgs = { requestParam: ajaxParam, handle: false } YvEventDispatch(option.onCreateBefore, this.ctl, beforeArgs) if (beforeArgs.handle === true) { // 不允许请求 param.node.cstate = 'same' return; } let ajaxPromise = >brokerInvoke(YvanUI.getServerPrefix(serverUrl), method, ajaxParam) //异步处理数据内容 this.ctl.loading = true const that = this ajaxPromise.then(res => { YvEventDispatch(option.onCreateAfter, that.ctl, res) if (res.success) { _.set(param.data, that.ctl.idField, res.data) delete param.data.__grid_row_new that.ctl._acceptChanges(param.node) that.ctl._transactionUpdate(that.ctl._gridData) } }).catch(r => { YvanMessage.msg('新增失败') }).finally(() => { this.ctl.loading = false }) } _deleteRow(param: any) { const option: any = this.option if (!option.deletePath) { param.node.cstate = 'same' return; // throw new Error('no deletePath') } const deleteId = this.ctl._getIdByRow(param.data) const [serverUrl, method] = _.split(option.deletePath, '@') const ajaxParam = { args: [deleteId] } const beforeArgs: GridOnCUDBeforeArgs = { requestParam: ajaxParam, handle: false } YvEventDispatch(option.onDeleteBefore, this.ctl, beforeArgs) if (beforeArgs.handle === true) { // 不允许请求 param.node.cstate = 'same' return; } let ajaxPromise = >brokerInvoke(YvanUI.getServerPrefix(serverUrl), method, ajaxParam) //异步处理数据内容 this.ctl.loading = true const that = this ajaxPromise.then(res => { YvEventDispatch(option.onDeleteAfter, that.ctl, res) if (res.success) { that.ctl.reload() } }).catch(r => { YvanMessage.msg('删除失败') }).finally(() => { this.ctl.loading = false }) } }