### 快速入门: 一个简单的商品管理模块
#### 安装注意事项
1. 环境需求 php >= 7.1, mysql >= 5.7, composer, ext-gd, ext-dom 等
#### 安装
1. 从 git 获取最新代码
2. cd 到项目目录使用 composer 安装依赖
3. 将 `db.sql` 导入到数据库
4. 修改 `application\database.php` 文件中相关的数据库配置
#### 安装示例
从 git 拉取最新代码:
```bash
git clone http://47.94.83.94:8082/r/framework.git myDemo
```
安装依赖:
```bash
cd myDemo
composer install
```
*如果提示缺少拓展, 或php版本错误请检查相应拓展是否安装, 或者php版本是否满足要求*
导入 数据库文件
使用命令行
```bash
# 使用 mysql 客户端
mysql -u root
create database shop;
use shop;
source db.sql
```
或使用其它第三方的数据库管理工具
*如果提示语法错误, 或不支持 datetime 类型请检查数据库版本是否满足要求*
最后将 相关数据库配置修改成你当前使用的配置
安装成功:
![](https://box.kancloud.cn/38151a3dde3c81f5250179dc51748cdf_2998x1704.png)
**账号: admin**
**密码: 111111**
#### 数据库结构
商品分类表
```sql
CREATE TABLE `product_category` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
商品表
```sql
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cid` int(11) NOT NULL COMMENT '商品分类',
`name` varchar(255) NOT NULL COMMENT '商品名称',
`price` decimal(10,2) NOT NULL COMMENT '价格',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态 1 上架 2 下架',
`thumb` varchar(255) NOT NULL COMMENT '商品封面',
`banners` varchar(255) DEFAULT NULL COMMENT '商品 banner',
`information` text COMMENT '详情',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '最后一次修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
*商品表中表示状态的 status 字段并没有使用 0 或者 NULL 作为判断条件, 具体请参考[数据库建议规范](数据库规范建议.md) 中第二条*
#### 使用代码生成器生成管理模块
使用账号密码登陆后默认的首页如图所示:
![首页](https://box.kancloud.cn/af522415b199c367ca3af7273f116eda_2999x1709.png)
点击右侧菜单中的 代码生成:
![代码生成](https://box.kancloud.cn/5b8d22c6b5c35dafa8965b4be0e70173_479x116.png)
代码生成器的界面如图:
![未选择](https://box.kancloud.cn/b635970ef737ec3ce59eb26cc2562f09_2996x1717.png)
我们选择刚刚创建的商品分类表 (product_category), 系统读取表的信息, 并生成一个待填写的表单:
![product_category](https://box.kancloud.cn/ceb26712d64b91894951d66296e0ed5e_2996x1720.png)
将表单按如同所示填写完成 并 提交:
![product_category](https://box.kancloud.cn/d2e10bb5535a7903d0d5b962e79f81e0_2501x1553.png)
提示 `success` 后, 重新选择商品表, 并按下图填写 提交:
![product](https://box.kancloud.cn/da725a8395db7d54d8966a9292c70454_2493x1548.png)
![product](https://box.kancloud.cn/cc75a90488d045a5facba1cbcdd31427_2477x1022.png)
*代码生成器的说明请查看 [代码生成器说明](代码生成器说明.md)*
#### 添加角色权限
系统实现了 RBAC 的权限管理, 所以要让用户能够访问对应的页面, 需要给用户或者角色对应的权限才行
这里我们给管理员角色访问之前创建的两个模块的权限
点击右侧菜单 选择 角色和权限 => 权限管理 => 角色权限管理
![menu](https://box.kancloud.cn/b8ebede3c3969e964232edada58bb816_499x862.png)
点击右上角添加按钮:
![role](https://box.kancloud.cn/669f5c6906da70536cfdba70dbcdb6ec_2998x1703.png)
角色选择 `管理员` 并勾选 `/admin/product/` 开头 和 `/admin/product_category` 开头的所有权限 并提交
如图:
![role](https://box.kancloud.cn/3dd505f14e9095b352b8d5586beb5019_2989x1718.png)
#### 创建菜单栏目
这个时候我们已经可以正常访问相应的管理页面了, 但是菜单上还没有对应的入口, 所以我们需要创建两个菜单栏目
点击权限和菜单 => 菜单管理
点击添加按钮:
![menu](https://box.kancloud.cn/f328e5693a5005bc173e7c7f34f374e7_2991x1716.png)
按下图填写:
![menu](https://box.kancloud.cn/cae87379e28f3e06a136cefdc8e8d835_2994x1722.png)
*菜单排序默认 50, 数值越大, 在右侧菜单栏的排序越考上*
*菜单类名用于给菜单添加图标, 内置 `layui` 图标, 详情参考: [layui-icon](https://www.layui.com/doc/element/icon.html)*
*菜单权限是连接对应的页面*
提交后右侧菜单栏中就已经有了商品分类管理:
![menu](https://box.kancloud.cn/19c416b6c7c3e90a62d4eb0c934e57a6_481x719.png)
类似的我们再继续把 商品管理 也添加上:
![menu](https://box.kancloud.cn/9f0d8af2e872f34ebeae285f22eed4a2_2504x1555.png)
分别查看 商品分类管理 和 商品管理, 页面如同所示:
![商品分类管理](https://box.kancloud.cn/e113cedee8d6f69babcfcb0693432075_3000x1712.png)
![商品管理](https://box.kancloud.cn/8cca25d56ccdd03b3496c368a0086fda_2991x1709.png)
这样商品分类和商品管理两个模块就完成了
#### 进一步完善
上一步结束后, 虽然基础的功能都已经完成, 但是还不够完善, 比如说 添加和编辑页面的商品分类需要手动填写对应 id, 商品详情还是普通的 input 表单, 搜索的时候 状态 也需要手动填写状态代码, 表格中显示的商品分类 和 状态都是数字等等, 接下来我们来完善这些问题
用 编辑器打开 项目, 可以看到 `application\admin` 目录下有一个 `viewModel` 目录, 里面有刚刚由代码生成器生成的 Product.php, ProductCategory.php 这两个类文件都继承了 app\common\ViewModel 类, 我们把它叫做 `视图模型` 它的功能就是通过自身的属性和方法, 来生成对应的视图 (参考 [视图模型](视图模型.md))
我们需要修改的是 `application\admin\viewModel\Product.php` , 使用编辑器打开这个文件
文件内容如下:
```php
<?php
/**
* 由代码生成工具自动生成
* Date: 2019-03-16
* Time: 15:30:07
*/
namespace app\admin\viewModel;
use app\common\ViewModel;
class Product extends ViewModel
{
public $fieldsName = [
'id' => 'ID',
'cid' => '商品分类',
'name' => '商品名称',
'price' => '商品价格',
'status' => '状态',
'thumb' => '商品封面',
'banners' => '商品banner',
'information' => '商品详情',
'create_time' => '添加时间',
'update_time' => '修改时间',
];
public $indexFields = [
'id' => 'text',
'cid' => 'text',
'name' => 'text',
'price' => 'text',
'status' => 'text',
'thumb' => 'img',
'create_time' => 'text',
'update_time' => 'text',
];
public $updateFields = [
'cid' => ['text', 'require'],
'name' => ['text', 'require'],
'price' => ['text', 'require'],
'status' => ['text', 'require'],
'thumb' => ['image', 'require'],
'banners' => ['image', null],
'information' => ['text', null],
];
public $addFields = [
'cid' => ['text', 'require'],
'name' => ['text', 'require'],
'price' => ['text', 'require'],
'status' => ['text', 'require'],
'thumb' => ['image', 'require'],
'banners' => ['image', null],
'information' => ['text', null],
];
public $search = [
'name' => ['text', 'like'],
'create_time' => ['datetime', ],
'cid' => ['text', ],
'status' => ['text', ],
];
public $exportFields = [
'id,ID',
'cid,商品分类',
'name,商品名称',
'price,商品价格',
'status,状态',
'thumb,商品封面',
'banners,商品banner',
'information,商品详情',
'create_time,添加时间',
'update_time,修改时间',
];
public $importFields = [
'id,ID',
'cid,商品分类',
'name,商品名称',
'price,商品价格',
'status,状态',
'thumb,商品封面',
'banners,商品banner',
'information,商品详情',
'create_time,添加时间',
'update_time,修改时间',
];
}
```
属性说明:
`$fieldsName` 对应字段的展示名, 是一个键值对数组, 数组的键为相应的字段, 数组的值 可以是 数组和 字符串是对应字段的展示名(字符串)或参数(数组)
`$indexFields` 表格中展示的字段 和 相应的参数, 是一个键值对数组, 键为字段名, 值为相应参数 (使用的表格组件和参数)
`$updateFields` 和 `$addFields` 分别是编辑页面 和 添加页面的表单字段和参数, `$addFields` 可以省略, 如果省略的话则直接使用 `$updateFields` 的规则
`$search` 搜索字段的规则, 是一个键值对数组
其它属性和方法参考 [视图模型](视图模型.md)
修改后的代码如下, 修改对应的作用看注释:
```php
<?php
/**
* 由代码生成工具自动生成
* Date: 2019-03-16
* Time: 15:30:07
*/
namespace app\admin\viewModel;
use app\common\ViewModel;
class Product extends ViewModel
{
// 定义表的别名 $1 表示 数组的第一个, $2 表示第二个 以此类推, $0 为当前视图模型对应的表名
public $variables = ['product_category'];
// 定义关联
public $join = [
['$1', '$1.id=$0.cid', 'left']
];
public $fieldsName = [
'id' => 'ID',
'cid' => '商品分类',
'name' => '商品名称',
'price' => '商品价格',
'status' => '状态',
'thumb' => '商品封面',
'banners' => '商品banner',
'information' => '商品详情',
'create_time' => '添加时间',
'update_time' => '修改时间',
// 添加 product_category.name 字段的展示名
'$1.name' => '商品分类',
//
'c_name' => '商品分类'
];
public $indexFields = [
'id' => 'text',
// 表格中的商品分类显示 product_category.name 字段,
// 由于 product_category.name 与 product.name 字段冲突, 会导致商品名称 和 商品分类显示的内容完全一样, 这里我们给他定义一个别名
// 'alias' => 'c_name', 同时 fieldsName 中也需要添加 c_name 的显示名, 不然会报错: 未定义数组索引: product.c_name
'$1.name' => ['text', 'alias' => 'c_name'],
'name' => 'text',
'price' => 'text',
// 状态使用 表格组件中的 Convert 组件
'status' => ['convert', [1 => '上架', 2 => '下架']],
'thumb' => 'img',
'create_time' => 'text',
'update_time' => 'text',
];
public $updateFields = [
// 使用表单组件中的 selector 组件, 使用 product_category 表中的数据
'cid' => ['selector', 'require', null, [
'table' => 'product_category',
'field' => 'name',
'value' => 'id'
]],
'name' => ['text', 'require'],
'price' => ['text', 'require'],
// 使用表单组件中的 selector 组件, 使用固定数据
'status' => ['selector', 'require', null, [
'list' => [
[1, '上架'],
[2, '下架']
],
'field' => 1,
'value' => 0
]],
'thumb' => ['image', 'require'],
'banners' => ['image', null],
// 使用表单组件 中的 Html 富文本组件
'information' => ['html', null],
];
// $addFields 与 $updateFields 完全相同, 可以去掉
// public $addFields = [
// 'cid' => ['text', 'require'],
// 'name' => ['text', 'require'],
// 'price' => ['text', 'require'],
// 'status' => ['text', 'require'],
// 'thumb' => ['image', 'require'],
// 'banners' => ['image', null],
// 'information' => ['text', null],
// ];
public $search = [
'name' => ['text', 'like'],
'create_time' => ['datetime', ],
// 使用搜索组件中的 selector 组件, 使用 product_category 表中的数据
'cid' => ['selector', null, [
'table' => 'product_category',
'field' => 'name',
'value' => 'id'
]],
// 使用搜索组件中的 selector 组件, 使用固定数据
'status' => ['selector', null, [
'list' => [
[1, '上架'],
[2, '下架']
],
'field' => 1,
'value' => 0
]],
];
public $exportFields = [
'id,ID',
'cid,商品分类',
'name,商品名称',
'price,商品价格',
'status,状态',
'thumb,商品封面',
'banners,商品banner',
'information,商品详情',
'create_time,添加时间',
'update_time,修改时间',
];
public $importFields = [
'id,ID',
'cid,商品分类',
'name,商品名称',
'price,商品价格',
'status,状态',
'thumb,商品封面',
'banners,商品banner',
'information,商品详情',
'create_time,添加时间',
'update_time,修改时间',
];
}
```
可以看到, 上述所说的问题已经解决了