# AdminLTE 模板的 react 化

[AdminLTE](https://github.com/almasaeed2010/AdminLTE) 是后台管理模板！

本项目就是将他常用的 html 进行了 react 化，项目本身并未包含 AdminLTE 的任何内容，css，js 等需要你从你的项目中引入。

使用方法

```
npm i react-admin-lte --save-dev
```

UI 组件的导入方式是：

```javascript
import DataTable from 'react-admin-lte/lib/DataTable';
```

## 目录

* [正确的打开方式](#right-way)

* [在 webpack 的 config 中使用 Make](#make)

* [热开发] (#hot-dev)

* [Tools 工具集](#tools)

---------

* [LayoutMain 主模板](#layout-main)

* [PanelList 列表框](#panel-list)

* PanelEdit 编辑框

* [DataTable 数据表](#datatable)

* Pager 分页器

* [LoginBox/RegisterBox 登录框/注册框](#login-box)

* Dialog

* [UserAvatarMenu 用户头像菜单](#user-avatar-menu)

---------------


* [InputRadio](#input-radio)

* [InputSelect](#input-select)

* [InputText](#input-text)

* [InputDateRange](#input-daterange)

以上组件都带有 label 属性，可以输入类似这样的 html

```html
<div class="form-group">
  <label for="goods_info" class="col-sm-2 control-label">设备信息</label>
  <div class="col-sm-10">
    <textarea class="form-control" id="goods_info" rows="4" name="goods_info" placeholder="设备信息"></textarea>
  </div>
</div>

```

并且提供了一个方便的事件属性：

onValueChange(key, value)  

key 是控件的 name 属性， value 是值，string 类型。

双向绑定的时候，建议使用此方法。


没有特殊写明的还会有一个 onChange 的属性，这个是直接返回基础控件的 onChange 事件。

onChange(e)


---------------

<a name="right-way" />
## 正确的打开方式 

在开发目录下建立一个 layout.html 模板， 因为在配置中使用了 HtmlWebpackPlugin 的 template: 'layout.html' 了。

建议使用路由 react-router

bla..

等我把手里的项目忙完，再写个例子传到这个 git 上。


<a name="make" />
## 在 webpack 的 config 中使用 Make

这个包内置了一个 webpack 的配置文件，只需要在 webpack config 中引用 Make。

部署环境下只需运行 webpack

```javascript
const Make    = require("react-admin-lte").Make;
const path    = require('path');
const devPath = path.resolve('./dev');
var mm        = new Make({
  devMode  : false,
  devPath  : devPath,
  buildPath: path.resolve('./dist'),
  libAlias : {
    AdminLTE : path.join(devPath, 'res/lib/AdminLTE/'),
    bootstrap: path.join(devPath, 'res/lib/bootstrap/')
  }
});

module.exports = mm.createWebpackConfig();
```
<a name='hot-dev' />

## 热开发模式

请看例子 //index.js

```javascript
var cloudoll = require('cloudoll');
var serve = require('koa-static');

var middles = [
];

if (process.env.debug) {
  console.log("react 热加载启动");
  const path = require('path');
  const Make = require("react-admin-lte").Make;
  const webpack = require("webpack");
  const devPath = path.resolve('./dev');

  var mm = new Make({
    devMode: true,
    devPath: devPath,
    buildPath: __dirname,
    libAlias: {
      AdminLTE: path.join(devPath, 'res/lib/AdminLTE'),
      bootstrap: path.join(devPath, 'res/lib/bootstrap')
    }
  });

  let webpackConf = mm.createWebpackConfig();

  webpackConf.resolveLoader = {
    root: path.join(__dirname, 'node_modules')
  };

  let compiler = webpack(webpackConf);

  var webpackDevMiddleware = require("koa-webpack-dev-middleware")(compiler, {
    hot: true,
    noInfo: false,
    publicPath: webpackConf.output.publicPath,
    headers: { "X-Custom-Header": "yes" },
    stats: {
      colors: true
    }
  });
  middles.push(webpackDevMiddleware);

  var hotMiddleware = require('koa-webpack-hot-middleware')(compiler);
  middles.push(hotMiddleware);
} else {
  // 生产模式下，这部分内容会被 nginx 代理，所以这个中间件是多余的。
  middles.push(serve('./dist'));
}

var app = new cloudoll.KoaApplication({
  middles: middles
});

```

<a name="tools" />

## Tools 工具集

* Tools.parseRouter()

可以直接从 window 对象中解析出路径和 router。

```javascript
//URL 是 "http://localhost:3000/#/login?a=1&b=2";

Tools.parseRouter()
```

返回值为：

```javascript
{
  path: '/login',
  query: {
    a: "1",
    b: "2"
  }
}
```

-------------

<a name="layout-main" />

## LayoutMain 主模板

和 Dashboard 一样，改了个名字，现在把 Dashboard 废弃...

示例：

```jsx
<LayoutMain
  logoMini={<string>U<sup>+</sup></string>}
  logoLarge={<div>U<sup>+</sup> 优服务</div>}
  topMenus={topMenus}
  mainSidebarContent={<MainMenus/>}
  rightContent={rightContent}
  footerContent="版权所有 @ 合一集团"
  >
  {this.props.children}
</LayoutMain>
```




<a name="datatable" />
## DataTable 数据表

这是数据表格

```javascript
import DataTable from 'react-admin-lte/lib/DataTable';
```

删除和修改的行操作已经内置。

onItemEdit, onItemDel 均绑定了一个主键_id/id 作为第一个参数。

onMoreActionHandle 是自定义按钮的事件，绑定了两个参数 id/_id 和 tag。具体参考下面的说明。


```jsx
  <DataTable
    cols={renderCols}
    list={this.state.list}
    onItemEdit={this.edit}
    onItemDel={this.onItemDel}
    noFooter={true}
    noDel={false}
    noEdit={true}
    actionColStyle={myStyles.actionSize}
    onMoreActionHandle={this.onMoreActionHandle}
    moreActions={
    [{
    tag: 'aprrove',label: '审批', iconClass: 'fa fa-thumbs-up'
    }]
    }
  />
```

上面的例子中的 onItemDel 如下：

```
  onItemDel(_id) {
    console.log(_id);
  }
```


### 如何 render 数据？

首选，你需要一个 list， 这个list 可能是你从远程 ajax 中查询回来的。格式如下：

```javascript
[
    {_id: 1, b: "x"}
    {_id: 2, b: "y"}
    {_id: 3, b: "z"}
]
```

需要注意，这个list 中必须包含 id 或者 _id，否则，主键传不出来。

然后，你要指定你需要 render 的 列

```javascript
let renderCols = {
  _id  : "编号",
  b    : "列名"
};

```

如果要格式化显示，那么需要指定 format 函数，请看下面的例子：

```javascript
 let renderCols = {
      happend_date              : {
        label: '日期'
      },
      xamount                : {
        label : '未收款',
        format: function (obj) {
          if (!obj) {
            return 0
          }
          var xsum        = obj.list.reduce(function (prev, curr) {
            return prev + curr.amount
          }, 0);

          return <label className="text-red">￥{xsum.toFixed(2)}</label>
        }
      }
 }
```

还有一个让整行参与计算的参数哦，fullData: true。

```javascript
xtotal: {
        label: '小计', fullData: true, format: function(trData){return sum(trData);}
}
```



### 如果需要自定义按钮？

使用 moreActions 参数：

```javascript
 [
   {
    tag: 'aprrove',label: '审批', iconClass: 'fa fa-thumbs-up'
   }
 ]
```

这个按钮对应的事件是： onMoreActionHandle

按照上面的例子，事件函数的例子如下：

```javascript
  onMoreActionHandle(_id, tag) {
    console.log(_id, tag);
    if (tag == 'approve'){
      //这里做审批按钮的事儿。
    }
  }
```

其中 _id 是数据的主键，tag 是你在按钮中指定的 tag。

### 如果某些行需要隐藏某些操作按钮呢？

指定 hideHandle 就可以了。

```javascript
{
    tag: 'aprrove',
    label: '审批',
    iconClass: 'fa fa-thumbs-up',
    hideHandle: function(ele){
        if (ele.xxx) {
            return true;
        }
        return false;
    }
}
```

noFooter={true} 可以指定不渲染底部的表头

footerRows 属性可以自定义底部表格，比如显示个小计什么的。

<a name="login-box" />

## LoginBox/RegisterBox 登录框/注册框

```jsx
<LoginBox title="大阿里" vtitle="登录吧，骚年，进入享受你的权利！" onLogin={this.login}>
  <p>注册，点击这个连接</p>
  <p>忘记密码，点击这个连接</p>
</LoginBox>
```

```jsx
<RegisterBox
      title={<a href="#/"><b>A<sup>+</sup></b> 大阿里</a>}
      vtitle="没有帐号吗？来注册吧！"
      onRegister={this.register}
    >
  <br/>
  我有帐号呢，现在 <a href="#/login" className="text-center">登录。</a>
</RegisterBox>
```


登录框的按钮事件是 onLogin(passport, password, rememberMe)

注册框的按钮事件是 onRegister(email, mobile, nick, password)


<a name="panel-list" />

## PanelList 列表框

列表框包含了分页控件，创建按钮，搜索区域等。

搜索区域需要自定义。

通常 列表框里会 包含一个数据显示的组件 DataTable

```jsx
<PanelList onCreate={this.create}
            searchPanel={
            <div className="has-feedback">
              <input type="text" className="form-control input-sm" name="qKeyword"
                      onChange={this.handleTextChange} placeholder="输入搜索，然后等等"/>
              <span className="glyphicon glyphicon-search form-control-feedback"/>
            </div>
            }
            limit={this.state.limit} skip={this.state.skip} total={this.state.total}
            onPagerChange={this.pagerChanged}
>
  <DataTable cols={ renderCols} list={this.state.list}
              onItemEdit={this.edit} onItemDel={this.del}
              actionColStyle={myStyles.actionSize}
  />

</PanelList>

```

<a name='user-avatar-menu' />

## UserAvatarMenu 用户头像菜单

示例

```jsx

<UserAvatarMenu cookie={cookie}
  userMenus={[
    { label: "粉丝", href: "#fans" },
    { label: "关注", href: "#fowllers" },
    { label: "朋友", href: "#friends" }
  ]}
  profileUrl="#profile"
  nick="啤酒云"
  slogan="不会做饭的歌手不是好程序员。"
  avatar="http://www.maczapp.com/uploads/icons/mac_20130504102327_2f0330fff1.png"
  />

```

会输出如下的 html 代码， 通常放在 顶部菜单里


```html

<li class="dropdown user user-menu">
    <a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="true"><img src="http://www.maczapp.com/uploads/icons/mac_20130504102327_2f0330fff1.png" class="user-image" alt="啤酒云"><span class="hidden-xs">啤酒云</span></a>
    <ul class="dropdown-menu">
        <li class="user-header"><img src="http://www.maczapp.com/uploads/icons/mac_20130504102327_2f0330fff1.png" class="img-circle" alt="啤酒云">
            <p>
                <!--react-text: 26 -->啤酒云
                <!-- /react-text--><small>不会做饭的歌手不是好程序员。</small></p>
        </li>
        <li class="user-body">
            <div class="row">
                <div class="col-xs-4 text-center"><a href="#fans">粉丝</a></div>
                <div class="col-xs-4 text-center"><a href="#fowllers">关注</a></div>
                <div class="col-xs-4 text-center"><a href="#friends">朋友</a></div>
            </div>
        </li>
        <li class="user-footer">
            <div class="pull-left"><a href="#profile" class="btn btn-default btn-flat">我的资料</a></div>
            <div class="pull-right"><a class="btn btn-default btn-flat">退出</a></div>
        </li>
    </ul>
</li>

```

属性有：

nick, avatar, slogan, profileUrl,  userMenus, cookie

其中 userMenus 的格式为

```javascript
[
  {label: "粉丝", href: "#fans"},
  {label: "关注", href: "#fowllers"},
  {label: "朋友", href: "#friends"}
]
```

cookie 是 这个 库： https://github.com/florian https://github.com/florian/cookie.js

如果指定了 cookie 这个属性，那么会在 logout 中自动移除 ticket 这个 cookie （这个是 cloudark 体系中约定的用户票据）。

```javascript
var cookie = require('your/path/to/this/lib/cookie');
```

属性事件有：

onLogout()

-----------------


<a name="input-datetime" />

## InputDatetime

这是一个可以选择时间的日期选择器，参考 https://github.com/YouCanBookMe/react-datetime

别忘记引入 css，还有 中文的话，需要引入 moment 的 locales 。

此控件不提供 onChange 事件属性


<a name="input-radio" />
## InputRadio

radio 输入框

属性：

onValueChange(key, value)  

options 

json数组，示例：

```
[
    { label: '上门', value: 1 },
    { label: '邮寄维修', value: 2 }
]
```

value

可以直接指定此值，将会默认选中。


<a name="input-select" />

## InputSelect

select 输入框

```
<InputSelect name="test" multiple={false}
            value="21"
            options={[
              { label: '上门', value: 1 },
              { label: '邮寄维修', value: 2 },
              { label: '上门1', value: 11 },
              { label: '邮寄维修1', value: 12 },
              { label: '上门2', value: 21 },
              { label: '邮寄维修2', value: 22 }
            ]} onValueChange={
              function (key, val) {
                console.log(key, val);
              }
            } />
```

属性：

* multiple

boolean 是否可以多选

* value(defaultValue)

如果是多选则是数组，单选则是字符串

如果没有指定 options 属性，那么就取 children。

<a name="input-daterange"/>

# InputDateRange 

这是一个可以选择日期范围的控件

onValueChange 的 value 参数是一个数组，第一个是开始日期, 第二个是结束日期

```
onValueChange(key, value){
  console.log("开始日期", value[0]);
  console.log("结束日期", value[1]);
}
```