luoyifan 4 سال پیش
والد
کامیت
d909814474
4فایلهای تغییر یافته به همراه314 افزوده شده و 2 حذف شده
  1. 240 0
      src/DataSourceHelper.ts
  2. 2 0
      src/Defaults.ts
  3. 45 2
      src/controls/combo.js
  4. 27 0
      src/lib/lib.ts

+ 240 - 0
src/DataSourceHelper.ts

@@ -0,0 +1,240 @@
+import {ajax} from './lib/config'
+import _ from 'lodash'
+import {lookupFn, lookupScope} from "./lib/lib";
+
+/**
+ * 通用 dataSource 解析,
+ * Tree/Combo/Grid/PageList ... 都会从这里解析
+ */
+export function dataSourceReload(ctl: any, extraParam?: any, _successCb?: Function,
+                                 successCallback?: (data: any[]) => void,
+                                 failCallback?: (msg) => void): Promise<any> {
+    const {config} = ctl
+
+    return new Promise<any>((resolve, reject) => {
+        if (!config.dataSource) {
+            // 没有设置数据源,直接置空
+            return Promise.resolve()
+        }
+        const option: DataSourceParam = {
+            extraParam,
+            _successCb,
+            successCallback: (value) => {
+                if (typeof successCallback === "function") {
+                    successCallback(value)
+                } else {
+                    ctl.setDataReal(value)
+                }
+                ctl.fireEvent('dataLoadComplete', ctl, true, value);
+                ctl.setLoading(false)
+                resolve(value)
+            },
+            failCallback: (error) => {
+                if (typeof failCallback === "function") {
+                    failCallback(error)
+                }
+                ctl.fireEvent('dataLoadComplete', ctl, false);
+                ctl.setLoading(false)
+                reject(error)
+            }
+        }
+
+        // 请求前要求确认一下
+        let isCanceled: boolean = false
+        ctl.fireEvent('dataBeforeLoad', ctl, {
+            cancel: () => {
+                isCanceled = true
+            },
+            ...option
+        });
+
+        if (!isCanceled) {
+            // 请求没有被取消
+            // 调用异步接口返回 Promise
+            return _innerReload(ctl, option)
+        }
+        return reject()
+    })
+}
+
+function _innerReload(ctl: any, option: DataSourceParam) {
+    const {config} = ctl
+    ctl.setLoading(true)
+
+    const scope = lookupScope(ctl)
+    let dataSource = config.dataSource
+    if (typeof dataSource == 'string') {
+        dataSource = lookupFn(scope, config.dataSource)
+    }
+
+    if (typeof dataSource === 'function') {
+        // @ts-ignore
+        dataSource.call(scope, ctl, option)
+        return
+
+    } else if (typeof dataSource?.url === 'string' || typeof dataSource?.sqlId === 'string') {
+
+        if (!ajax.func) {
+            console.error("没有配置 ajax")
+            return
+        }
+
+        const params: any = {}
+        let sortModel: any = null;
+        let filterModel: any = null;
+        if (option.extraParam && option.extraParam.filterModel) {
+            filterModel = option.extraParam.filterModel;
+        }
+        if (option.extraParam && option.extraParam.sortModel) {
+            sortModel = option.extraParam.sortModel;
+        }
+
+        _.forOwn(option.extraParam, (v: any, key) => {
+            if (key != "filterModel" && key != "sortModel") {
+                if (typeof v === 'function') {
+                    params[key] = v()
+                } else {
+                    params[key] = v
+                }
+            }
+        })
+        const ds: DataSourceAjax<any> = dataSource
+
+        _.forOwn(ds.params, (v: any, key) => {
+            if (typeof v === 'function') {
+                params[key] = v()
+            } else {
+                params[key] = v
+            }
+        })
+
+        if (typeof dataSource?.url === 'string') {
+            // @ts-ignore
+            ajax.func({
+                method: dataSource.method,
+                url: ds.url,
+                data: params,
+                filterModel,
+                sortModel,
+            }).then(res => {
+                if (res.success) {
+                    option.successCallback(res.data)
+
+                } else {
+                    option.failCallback(res.msg)
+                }
+
+            }).catch(e => {
+                option.failCallback(e)
+
+            })
+
+        } else if (typeof dataSource?.sqlId === 'string') {
+
+            const ds: DataSourceSql<any> = ctl.dataSource
+
+            // @ts-ignore
+            Extend.ajax.func({
+                url: ds.sqlId,
+                data: params,
+                db: ds.db,
+                method: 'sql',
+                filterModel,
+                sortModel,
+            }).then(res => {
+                if (res.success) {
+                    option.successCallback(res.data)
+
+                } else {
+                    option.failCallback(res.msg)
+                }
+
+            }).catch(e => {
+                option.failCallback(e)
+
+            }).finally(() => {
+                ctl.loading = false
+            })
+
+            return
+        } else {
+
+            console.error("非法的 dataSource", ctl.dataSource)
+            debugger
+            throw new Error("非法的 dataSource")
+        }
+    }
+}
+
+export interface DataSourceParam {
+    /**
+     * 额外附加的参数
+     */
+    extraParam?: any
+
+    /**
+     * 成功后额外的调用
+     */
+    _successCb?: Function
+
+    /**
+     * 设置响应成功的数据
+     */
+    successCallback: (data: any[]) => void
+
+    /**
+     * 设置响应失败
+     * @param msg 错误消息(可以为空)
+     */
+    failCallback: (msg?: string) => void
+}
+
+export interface DataSourceBefore extends DataSourceParam {
+
+    /**
+     * 取消下一步的请求
+     */
+    cancel: () => void
+}
+
+/**
+ * 方法作为数据源
+ */
+export type DataSourceFunction<T> = (sender: T, option: DataSourceParam) => void
+
+/**
+ * Ajax 数据源
+ */
+export interface DataSourceAjax<T> {
+    url: string,
+    method: string,
+    params?: {
+        [key: string]: (() => any) | any
+    }
+}
+
+/**
+ * Sql 数据源
+ */
+export interface DataSourceSql<T> {
+    sqlId: string,
+    db: string,
+    params?: {
+        [key: string]: (() => any) | any
+    }
+}
+
+/**
+ * 数据源配置
+ */
+export type DataSourceHelper<T> = DataSourceFunction<T> | DataSourceAjax<T> | DataSourceSql<T>
+
+/**
+ * 数据抽取之前的设置
+ */
+export type OnBeforeDataLoad<T> = (sender: T, option: DataSourceBefore) => void
+
+/**
+ * 数据加载完成之后的回调
+ */
+export type OnDataLoadComplete<T> = (sender: T) => void

+ 2 - 0
src/Defaults.ts

@@ -8,6 +8,8 @@ export const combo = {
     labelAlign: 'right',
     labelWidth: 70,
     selectOnFocus: true,
+    valueField: 'id',
+    displayField: 'text',
 }
 
 export const text = {

+ 45 - 2
src/controls/combo.js

@@ -1,21 +1,64 @@
 import _ from 'lodash'
 import {combo} from '../Defaults'
 import {baseConfig} from "./base";
+import {lookupScope, lookupFn} from "../lib/lib";
+import {dataSourceReload} from "../DataSourceHelper";
 
 export default function () {
     const cc = Ext.form.field.ComboBox.prototype.constructor
-    const {initComponent} = Ext.form.field.ComboBox.prototype
+    const {initComponent, setData} = Ext.form.field.ComboBox.prototype
     Ext.form.field.ComboBox.override({
         constructor(config) {
             const newConfig = _.defaultsDeep({
-                // 强制属性
+                // 强制性属性
+
             }, baseConfig(config, 'col-item'), config, combo)
 
+            if (typeof newConfig.store == 'undefined') {
+                if (_.isArray(newConfig.data)) {
+                    // ExtJS 无法直接接受数组模式
+                    newConfig.store = {
+                        fields: [newConfig.valueField, newConfig.displayField],
+                        data: newConfig.data
+                    }
+                }
+
+                if (typeof newConfig.data !== 'object') {
+                    newConfig.store = {
+                        fields: [newConfig.valueField, newConfig.displayField],
+                        data: []
+                    }
+                }
+            }
+
             cc.call(this, newConfig)
         },
 
+        setDataReal(value) {
+            const {config} = value
+            if (!this.store) {
+                this.store = new Ext.data.Store({
+                    fields: [config.valueField, config.displayField],
+                    data: value
+                })
+            } else {
+                this.store.getProxy().setData(value)
+                this.store.load()
+            }
+        },
+
         initComponent() {
             this.on({
+                afterrender() {
+                    const {config} = this
+                    const scope = lookupScope(this)
+
+                    if (config.dataSource) {
+                        _.defer(() => {
+                            dataSourceReload(this)
+                        })
+                    }
+                },
                 focus: {
                     // 获得焦点后自动下拉
                     fn(sender) {

+ 27 - 0
src/lib/lib.ts

@@ -1,5 +1,6 @@
 import _ from 'lodash'
 import {FunctionRegiste} from "../types";
+import {Scope} from "../Scope";
 
 export const LibList: FunctionRegiste[] = []
 
@@ -20,4 +21,30 @@ export function Lib(registe: FunctionRegiste) {
 
         return target
     }
+}
+
+/**
+ * 解析事件
+ *   'scope.私有方法名'
+ *   'system.系统函数名'
+ *   'format.格式化名'
+ */
+export function lookupFn(scope: Scope, event: string): Function {
+    // 为 eval 准备环境
+    const func: Function = eval(event)
+
+    if (typeof func !== 'function') {
+        console.error('无法识别的事件响应类型', event, func)
+        throw new TypeError('无法识别的事件响应类型')
+    }
+
+    return func
+}
+
+/**
+ * 从 ext.element 获取 scope 对象
+ */
+export function lookupScope(extHandle: any): Scope {
+    const vm = extHandle.lookupViewModel()
+    return vm.yvanScope
 }