saas-dynamic-component
====
saas平台动态引擎组件库，目前包括查询引擎、表格引擎，后续待增加...

## 安装

```sh
npm install saas-dynamic-component --save-dev
```

## 试用范围

`NornJ`+`React`+`Mobx-state-tree`项目模板

## 使用说明

1. store文件注入

```javascript
import { SearchEngineStore, TableEngineStore, TableListSettingStore } from 'saas-dynamic-component';

const RootStore = types.model("RootStore", {

  searchEngine: types.optional(SearchEngineStore, {
    hostDomain: `${__HOST_DYNAMIC}`
  }),
  tableEngine: types.optional(TableEngineStore, {
    hostDomain: `${__HOST_DYNAMIC}`,
    isBuild: `${__IS_BUILD}` == true ? true : false,
  }),
  tableListSetting: types.optional(TableListSettingStore, {
    hostDomain: `${__HOST_DYNAMIC}`,
  }),
  
})
```

2. 修改webpack配置文件`webpack.config.js`，具体关注注释部分

```javascript
new webpack.DefinePlugin({
    __ENV: (isProd || isTest) ? "'pro'" : "'dev'",
    __HOST: (isProd || isTest) ? "''" : "'http://localhost:8089/'",
    __HOST_DYNAMIC: (isProd || isTest) ? `'//raas.jd.com/'` : "'http://localhost:8080/'", // 同域跨应用调用数据
    __IS_BUILD: (isProd || isTest) ? true : false, //标识是否构建
    'process.env': {  
      'NODE_ENV': JSON.stringify(isProd ? 'production' : 'development')
    },
    __JSPATH: JSON.stringify((isProd || isTest) ? '/' + process.env.Project + '/js/' : `/dist/${process.env.Project}/js/`),
    __COMMONHOST: (isProd || isTest) ? `'${commonDomain}'` : "'http://localhost:8089/'",
  }),
```

3. 页面使用（举例）

  1)  js文件，注意：flarej里面的组件引入不可缺少，否则影响正常使用

```js
import 'flarej/lib/components/antd/button';
import 'flarej/lib/components/antd/cascader';
import 'flarej/lib/components/antd/input';
import 'flarej/lib/components/antd/select';
import 'flarej/lib/components/antd/datePicker';
import 'flarej/lib/components/antd/table';
import 'flarej/lib/components/antd/spin';

import 'saas-dynamic-component/dist/saas-dynamic-component.min.css'; 
import { SearchEngine, TableEngine, TableListSetting } from 'saas-dynamic-component';
```

2)  html文件，举例如下

```html
<SearchEngine
  searchText="查询"
  appCode="pbs"
  menuUrl="ReplenishmentDesk"
  onBuried={buriedClick}
/>

<TableEngine 
  rowSelection={rowSelection}
  tableAction={tableAction} 
  rowKey=id
  scroll={tableScroll}
  onChangeEdit={onChangeEditRecord}
  onRef={refTableEngine}
  showQuickJumper={true}
  showSizeChanger={true}
  appCode="pbs"
  menuUrl="ReplenishmentDesk"
/>

<TableListSetting appCode="pbs" menuUrl="ReplenishmentDesk" tableEngineFunc={reloadTable} />
```

4. 组件API

1)  查询引擎

| 参数       | 说明                                                         | 类型           |
| ---------- | ------------------------------------------------------------ | -------------- |
| appCode    | 应用code（应用标识）                                         | string         |
| menuUrl    | 菜单url（菜单标识）                                          | string         |
| searchText | 按钮文本（不设置则按钮区域不显示，可支持自定义按钮，自定义请求），如使用国际化 | string         |
| onChange   | 获取查询列表form数据，通常在不设置searchText时使用           | Function(form) |
| onBuried   | 点击查询按钮回调                                             | Function()     |

2)  表格引擎

| 参数            | 说明                                                         | 类型                                  |
| --------------- | ------------------------------------------------------------ | ------------------------------------- |
| appCode         | 应用code（应用标识）                                         | string                                |
| menuUrl         | 菜单url（菜单标识）                                          | string                                |
| tableAction     | 表格操作属性（不设置则无操作列）                             | object                                |
| scroll          | 参考ant-design（talbe）                                      | { x: number \| true, y: number }      |
| showQuickJumper | 参考ant-design（pagination）                                 | boolean                               |
| showSizeChanger | 参考ant-design（pagination）                                 | boolean                               |
| rowSelection    | 参考ant-design（table）                                      | object                                |
| onRef           | 获取表格组件this，用于调用组件方法                           | Function(ref)                         |
| onChangeEdit    | 可编辑项，编辑时回调，通常用于存储修改后的值，或增加一些校验信息。 | Function                              |
| onChangePage    | 点击分页回调                                                 | Function(pagination, filters, sorter) |
| bordered        | 参考ant-design（talbe）bordered                              | boolean                               |

3)  表格引擎内置方法

| 方法名            | 说明                                             | 参数                                                 |
| ----------------- | ------------------------------------------------ | ---------------------------------------------------- |
| loadData          | 刷新当前页                                       | Page：不传默认当前页                                 |
| changeGroupMember | 改变分组显示，只试用于表格分组模板               | Boolean：true展示所有组内成员，false展示默认显示成员 |
| reloadTableResult | 重新获取表格模板信息，用于表格字段配置后刷新模板 |                                                      |

4）表格-字段配置

| 方法名          | 说明                 | 参数     |
| --------------- | -------------------- | -------- |
| appCode         | 应用code（应用标识） | string   |
| menuUrl         | 菜单url（菜单标识）  | string   |
| tableEngineFunc | 保存回调             | function |

## 本地开发-调用测试环境接口方案

1. 配置测试环境host

   ```javascript
   // 模板引擎举例
   192.168.153.99 passport.jd.com
   10.182.48.16 raas.jd.com
   ```

2. Chrome 浏览器安装插件 `EditThisCookie`

可自行去chrome应用商店搜索，并添加至浏览器，用于管理chookie信息，添加完后，会在浏览器地址栏右侧出现插件图标-小饼干。

1. `webpack.config.js`配置文件中增加反向代理

```javascript
// 将ypub-pass-web(用于模板引擎的服务)的接口全部代理到raas.jd.com
devServer: {
    port: 8080,
    proxy: {
      '/mockjs': {
        target: 'http://rap.jd.com',
        secure: false,
        changeOrigin: true
      },
      '/ypub-paas-web': {
        target: 'http://raas.jd.com/',
        secure: false,
        changeOrigin: true,
      }
    },
  },
```

```javascript
new webpack.DefinePlugin({
    __ENV: (isProd || isTest) ? "'pro'" : "'dev'",
    __HOST: (isProd || isTest) ? "''" : "'http://localhost:8089/'",
    __HOST_DYNAMIC: (isProd || isTest) ? `'//raas.jd.com/'` : "'http://localhost:8080/'", // 将需要代理的接口配置为监听端口
    __IS_BUILD: (isProd || isTest) ? true : false,
    'process.env': {  
      'NODE_ENV': JSON.stringify(isProd ? 'production' : 'development')
    },
    __JSPATH: JSON.stringify((isProd || isTest) ? '/' + process.env.Project + '/js/' : `/dist/${process.env.Project}/js/`),
    __COMMONHOST: (isProd || isTest) ? `'${commonDomain}'` : "'http://localhost:8089/'",
  }),
```

> 注意：上方代码只需关注含注释的部分。

1. 具体操作流程（模板引擎服务举例，其他同理）

   1) 登录租户端应用 [raas.jd.com/tenant/](http://raas.jd.com/tenant/) (使用配置的host环境账号进行登录)。

   2) 点击浏览器地址栏右侧`EditThisChookie`（小饼干）图标，粘贴出登录租户标识`saassessionid`的值。

   3) 通过`npm run dev-web`启动你的本地项目，浏览器访问，点击`EditThisChookie`图标，点击`+`新增cookie，名称为`saassessionid`，值为上一步粘贴的值，点击保存。

   4) 重新刷新页面，即可看到本地服务调取测试环境接口成功后的返回值。

> 说明：若一段时间后接口数据报错，可能为登陆过期，需要按具体操作流程重新操作一遍。

## API具体使用

#### 查询模板

1. `appCode`和`menuUrl`

appCode为应用标识——即在SaaS运营端系统维护的标识编码

menuUrl为菜单标识——即在SaaS运营端系统维护的菜单编码

> 作用：这两个参数为后台接口提供，用于唯一确定在PaaS平台配置的模板信息，获取到模板信息完成组件渲染

2. `searchText`按钮文字

如果使用react-intl-universal作为国际化工具，系统需要支持国际化，则使用如下：

```html
<SearchEngine
  searchText="{intl('btnCode')}"
  appCode="pbs"
  menuUrl="ReplenishmentDesk"
  onBuried={buriedClick}
  onChange={changeForm}/>
```

语言变量key`btnCode`需要在locales文件加下语言文件里添加，key可自定义。

3. `scroll`使用样例

```javascript
// 根据配置列动态适应总宽度，可根据实际需求改动
@computed get tableScroll() {
  const { store: { tableEngine } } = this.props;
  let length = tableEngine.tableColumns.length;
  tableEngine.tableColumns.map(item => {
    if (item.children) {
      length += item.children.length;
    }
  })
  return {
    x: length * 120 + 300,
  }
}
```

4. `onChange`返回查询列表form，举例

```javascript
@observable formEngine = null;
@autobind
changeSearch(form) {
  this.formEngine = form;
}

/* 
* 自定义按钮需要获取form数据时，如需进行虚拟字段（品牌、品类、地址）/时间格式翻译
* 可调用searchEngine.translateForm方法，返回翻译后的form信息，如下
*/
@autobind
searchButtonFunc() {
  const { store: { searchEngine } } = this.props;
  let params = searchEngine.translateForm(this.formEngine);
}
```

5. `onBuried`点击查询按钮的回调，举例（增加操作埋点）

```javascript
import AnalysisGather from '../../../utils/analysis';

@autobind
buriedClick() {
  AnalysisGather('test_search'); // test_search为埋点标识，根据业务自行修改
}
```

#### 表格模板

1. `appCode`和`menuUrl`同查询模板，不再赘述。
2. `onRef`获取表格模板组件this

```javascript
@observable refTable = null; //用于存储表格组件this

@autobind
refTableEngine(ref) {
  this.refTable = ref;
}
```

3. `tableAction`为表格操作栏

```javascript
@computed get tableAction() {
  return {
    width: 150,
    fixed: 'right',
    title: '操作',
    dataIndex: 'handler',
    render: (text, record, index) => nj `
      <#if ${record.editable}}>
        <span>
          <a href="javascript:;" onClick=${()=>this.onSaveTable(record, index)} className="btn-link">保存</a>
          <a href="javascript:;" onClick=${()=>this.onCancelTable(record, index)} className="btn-link">取消</a>
        </span>
      <#else>
        <span>
          <a href="javascript:;" onClick=${()=>this.onEditTable(record, index)} className="btn-link">编辑</a>
        </span>
      </#else>
      </#if>
  ` (),
  }
}

@observable editLine = null;
@autobind
onEditTable(record, index) {
  const { store: { tableEngine } } = this.props;
  this.editLine = record;
  tableEngine.setTableEdit(true, record);
}

@autobind
onCancelTable(record, index) {
  const { store: { tableEngine } } = this.props;
  tableEngine.setTableEdit(false, record);
  this.editLine = null
}

@autobind
onSaveTable(record, index) {
  const { store: { tableEngine } } = this.props;
  tableEngine.setTableEdit(false, record);
  // 调取保存接口，保存数据
  this.refTable.loadData(); // 保存成功后，刷新表格数据，this.refTable参考上一条说明
}
```

4. `onChangeEdit`编辑项在编辑态时，改变值回调函数

```javascript
@autobind
onChangeEditRecord(e, code, text, record, index, callback, oldRecord) {
  /*
  * e 为ant design控件onChange的返回
  * code 为 修改列的字段编码
  * text，record, index 同表格操作render
  * callback 为表格模板分组 目标字段根据分组成员动态计算扩展回调，具体使用如下
  * oldRecord 改变前的record值
  */
  // 此处例子说明：只能输入数字，否则出现错误提示，判定为数字，则分组内汇总字段（目标字段）根据填入的数字做动态求和。
  if (/^(0|[1-9][0-9]*)$/.test(e.target.value)) {
      callback('sum'); // 参数说明：sum 为求和，avg 为求平均
  } else {
      Message.error('请输入数字');
  }
  e.target.value = e.target.value.replace(/\D/g,'')
}
```

5. `onChangePage`为点击分页回调

```javascript
// 如分页埋点
@autobind
changePage(){
  AnalysisGather('test_table_paging');
}
```

#### 表格内置方法

1. `loadData`刷新表格，使用可参考表格模板`tableAction`说明。

2. `changeGroupMember`改变分组表格的显示效果，即切换表格分组成员的默认显示/显示全部。

```html
<ant-Button onClick={changeShow}>{show ? ('折叠分组', '展开分组')}</ant-Button>
```

```javascript
@observable show = false;
@autobind
changeShow() {
  this.show = !this.show;
  this.refTable.changeGroupMember(this.show);
}
```

#### 表格-字段配置

```javascript
@autobind
reloadTable() {
	this.refTable.reloadTableResult();
}
```

