# Yaa

一个基于YII2、VUE3 快速让你实现后台CRUD的开发框架。

<figure><img src="https://crustipfs.live/ipfs/Qme5vqMHFrLAxbQKwz2bRKMBDPNq32izpGymF8U9N8u8Tk?filename=home.jpg" alt=""><figcaption><p>效果图</p></figcaption></figure>

<figure><img src="/files/VJBJrjWodJ4s2RAB1GV9" alt=""><figcaption></figcaption></figure>

##

## 环境要求

* PHP >= 7.3
* Composer >= 2
* Node.js >= 14

## 安装

```shell
composer create-project umono/yaa-yii2 
```

## 特点

* 🎊 界面清爽、简约
* ⚒️ 原生框架轻度改造、不附带任何臃肿第三方库
* ✨ 自带祝福光环加持，助你效率提升1000X

###


# 安装

## 环境要求

* PHP >= 7.3
* Composer >= 2
* Node.js >= 14

## 安装

```shell
composer create-project umono/yaa-yii2
```

在确保项目安装后，需要进入该项目中运行 `php yii init`

```shell
cd yaa-yii2 && php yii init
```

这一指令将会初始化一些yii2后台所需的静态资源，如：gii2、debugtools等页面所需的资源。


# 快速上手

## 前期准备

* 首先你需要一个数据库，并在该项目.env 文件中配置相关信息
* 确保你的运行环境条件满足
* 确保项目在本地环境或线上环境中的nginx里配置解析

## 创建一个用户数据管理页面

### 建立路由信息

如：访问 `admin/user` 路径可看到用户数据的页面

则在对应 `ui/src/router/components/other/admin/index.ts` 里下的路由文件中添加 `user`

```typescript
const routeKeys = [
    'home',
    'admin',
    'admin-group',
    'admin-log',
    'user',// 添加
];
```

{% hint style="info" %}
此处路由信息具体参考 [路由](/he-xin-gong-neng/shu-ju-biao-ge/lu-you)

假设&#x20;

* 前端设置路由：admin/user，
* 后台yii2代码中：admin/user 控制器中的方法

其两者并不冲突

* 前者为url访问资源路径
* 后台为api数据调用

因此在设定路由的时候可以自行配置无需对应匹配。
{% endhint %}

### 完善路由 vue 文件

```html
<template>
    <TableData :subHeight="240" :search="search" ref="tables"
        @view="openModal({ id: $event.id, isEdit: false }, 'formModal')" #查看按钮回调
        @edit="openModal({ id: $event.id, isEdit: true }, 'formModal')"  #编辑按钮
        url="admin/api/user/index" #请求接口数据库
        :handle="_handleBtn">  #按钮参数配置
        <!-- 搜索条件开始 -->
        <n-input v-model:value="search.nickName" filterable placeholder="用户昵称" />
        <n-input v-model:value="search.name" filterable placeholder="姓名" />
        <n-input v-model:value="search.phone" filterable placeholder="手机号码" />
        <!-- 搜索条件结束 -->
    </TableData>
</template>

<script lang="ts">
import TableData from "@/components/common/TableData.vue";

export default defineComponent({
    name: "USER",
    components: { TableData },

    setup() {
        // 搜索条件参数
        // 在开发中尽量使用search参数包含所有条件
        let search = reactive({
            name: '',
        } as any)
        return {
            search
        }
    },
})
</script>
```

### php user 实例模型

使用`gii` 工具生成文件在在对应路径 `src/common/models/other/` 下

```php
<?php

    namespace app\common\models\other;

    use Yii;

    /**
     * This is the model class for table "{{%user}}".
     *
     * @property int         $id
     * @property string|null $nickName   用户昵称
     * @property string|null $name       用户姓名
     * @property string|null $_hash      HASH地址
     * @property string|null $openId     微信openId
     * @property string|null $phone      手机号码
     * @property int|null    $integral   积分数据
     * @property string|null $password   密码
     * @property string|null $avatar     头像
     * @property int|null    $status     账号状态
     * @property string|null $auth_key   权限token，登录TOKEN
     * @property int|null    $gender     性别:1男2女
     * @property int|null    $is_del     是否删除
     * @property string|null $created_at 创建时间
     * @property string|null $updated_at 更新时间
     */
    class User extends \app\common\models\Model
    {
        public $tableImageColumn = ['avatar'];

        /**
         * {@inheritdoc}
         */
        public static function tableName()
        {
            return '{{%user}}';
        }

        /**
         * {@inheritdoc}
         */
        public function rules()
        {
            return [
                [['integral', 'status', 'gender', 'is_del'], 'integer'],
                [['created_at', 'updated_at'], 'safe'],
                [['nickName', 'name', 'phone'], 'string', 'max' => 20],
                [['_hash', 'openId', 'password', 'avatar', 'auth_key'], 'string', 'max' => 255],
            ];
        }

        /**
         * {@inheritdoc}
         */
        public function attributeLabels()
        {
            return [
                'id'         => 'ID',
                'nickName'   => '用户昵称',
                'name'       => '用户姓名',
                '_hash'      => 'HASH地址',
                'openId'     => '微信openId',
                'phone'      => '手机号码',
                'integral'   => '积分数据',
                'password'   => '密码',
                'avatar'     => '头像',
                'status'     => '账号状态',
                'auth_key'   => '权限token，登录TOKEN',
                'gender'     => '性别:1男2女',
                'is_del'     => '是否删除',
                'created_at' => '创建时间',
                'updated_at' => '更新时间',
            ];
        }
    }

```

### php 文件 控制器

```php
<?php
    namespace app\modules\backend\api\controllers;

    use app\modules\backend\api\Controller;
    use app\modules\backend\api\models\other\User;

    class UserController extends Controller
    {
        // 数据列表
        public function actionIndex()
        {
            $get      = $this->get;
            $andWhere = [
                ['like', 'nickName', $get['nickName'] ?? ''],
                ['like', 'name', $get['name'] ?? ''],
                ['like', 'phone', $get['phone'] ?? ''],
            ];
            return User::page()->andWhere($andWhere)->toTableDataArray();
        }

        // 创建、修改、删除
        // ...
    }
```

以上访问 `admin/user` 页面即可得到如下效果：

<figure><img src="/files/Ksi4d8cvsOyCTwq4TxTO" alt=""><figcaption><p>admin/user 数据页面</p></figcaption></figure>


# 项目结构

## Yaa 项目结构如下

```
├── README.md
├── composer.json
├── package.json
├── pnpm-lock.yaml
├── postcss.config.cjs
├── public 
├── src
├── tailwind.config.cjs
├── tsconfig.json
├── tsconfig.node.json
├── ui
├── vite.config.ts
└── yii
```

## public&#x20;

该项目的静态资源文件夹、也是部署服务器的运行入口文件夹、也是vue编译打包资源所在地。

## src

Yaa项目中php主要的核心代码文件夹。

里面包含`common、config、database、modules` 四个主要文件夹。具体如下:

```
├── common                # 公共资源文件夹、
│   ├── commands          #--- yii2 中的console 编写文件夹
│   ├── mail              #--- 邮箱模块文件
│   └── models            #--- 公共资源实例模型文件夹
├── config                # 配置文件夹
│   ├── common.php        #--- 公共参数配置文件
│   ├── console.php       #--- console模式配置文件
│   ├── db.php            #--- db配置文件
│   ├── env.php           #--- .env.* 配置读取文件
│   ├── log.php           #--- log 配置文件
│   ├── mongodb.php       
│   ├── msg.php
│   └── web.php           #--- 整个web项目的配置文件
├── database
│   └── migrations        #--- migrate 生成文件所在文件夹
└── modules               # 各个模块文件夹 
    ├── ModulesConfig.php #--- 各个模块配置文件
    ├── api               #--- api 模块
    │   └── base          #--- api base接口模块
    ├── backend           #--- backend 模块
    │   ├── api           #--- backend api 模块
    │   └── web           #--- bakend web 模块
    └── website           #--- website 模块
```

主要参与项目编码地大多数集中在每个模块上。

在 Yaa 中每个模块目录下必定存在三个文件，分别是：`Module.php、ModuleBootstrap.php、Controller.php`

**因此创建一个新模块必定需要这三个文件。**

### Module.php

该文件可声明自定的url访问路径，在创建新模块的情况下，需要声明以下2个参数。

* moduleId // 模块的id
* urlPrefix  // 模块的url访问路径

### ModuleBootstrap.php

新建该文件后继承 `umono\multiple\modules\AbstractModuleBootstrap`

### Controller.php

需要该模块是因为该模块需要自定的主要公共控制器，便于各个模块之间的管理。

### ModulesConfig.php

该文件用于配置各个模块的启动，所以需要将建立的新模块对应数据填入该文件中。

## ui

编写前端代码vue的集中地，相关配置在根目录。


# 数据表格

数据列表表格作为后台系统常用的数据页面，因此Yaa封装了改数据表格，只为了快速助力开发此页面。

## 开始

开始编写CRUD页面时，需要注意以下文件步骤。

**👉**[ **vue中的路由**](/he-xin-gong-neng/shu-ju-biao-ge/lu-you)

[**👉 vue中的数据页面**](/he-xin-gong-neng/shu-ju-biao-ge/shu-ju-ye-mian)

👉[ **yii2 实例模型**](/he-xin-gong-neng/shu-ju-biao-ge/shi-li-mo-xing)

[**👉 yii2 控制器**](/he-xin-gong-neng/shu-ju-biao-ge/kong-zhi-qi)


# 路由

在路径 `ui/src/router/*`中添加路由路径，并在对应的路径中生成文件。

## 示例

以 `user`为例。

因为所属数据为后台，在 `ui/src/router/components/other/admin/index.ts` 文件中修改如下：

```typoscript
const routeKeys = [
    'home',
    'admin',
    'admin-group',
    'admin-log',
    'user', // 添加 user ， 对应在 views/Admin/ 下新建 user/Index.vue 文件
    'menu',
];
```

### 事项一

如果你的页面及路由符合以下格式

* **url   => admin/template**
* **文件(Index.vue) => views/Admin/template/Index.vue**

🎉 你可以，直接在 `routeKeys` 中添加 `template` 即可

### 事项二

当你的文件不符合以上时，请在 `router/components/other/index.ts` 中添加完善路由信息。如下所示：

```typescript
const routes = [
    {
        path: '/test',
        name: 'Test',
        component: () => import('@/views/test/Index.vue'),
    },
]

export default routes
```

添加路由后并完善路由文件信息。


# 数据页面

在页面中，使用 引入 [`TableData.vue`](/he-xin-gong-neng/shu-ju-biao-ge/shu-ju-ye-mian/shu-ju-biao-ge) 文件即可实现。

数据页面包含以下功能

* 搜索、筛选、导出、排序、删除数据
* 增删改查等按钮功能实现

### 示例：

以用户数据为例：

```html
<template>
    <TableData :subHeight="240" ref="tables"
        @view="openModal({ id: $event.id, isEdit: false }, 'formModal')"
        @edit="openModal({ id: $event.id, isEdit: true }, 'formModal')" 
        url="admin/api/user/index"
        :handle="_handleBtn">
    </TableData>
</template>

<script lang="ts">
import TableData from "@/components/common/TableData.vue";

export default defineComponent({
    name: "USER",
    components: { TableData },
})
</script>
```

对应完善php代码即可实现以下效果：

<figure><img src="https://crustipfs.art/ipfs/QmcvP7mAfjx2b7QazpaGYGxhRjbnhR5mMWyRQyM1ovvP37?filename=user-un.jpg" alt=""><figcaption></figcaption></figure>

### 搜索

在 TableData 组件中添加实现 `search` 参数。并将代码修改如下：

```html
<template>
    <TableData :subHeight="240" :search="search" ref="tables"
        @view="openModal({ id: $event.id, isEdit: false }, 'formModal')"
        @edit="openModal({ id: $event.id, isEdit: true }, 'formModal')" 
        url="admin/api/user/index"
        :handle="_handleBtn">

        <n-input v-model:value="search.nickName" filterable placeholder="用户昵称" />
        <n-input v-model:value="search.name" filterable placeholder="姓名" />
        <n-input v-model:value="search.phone" filterable placeholder="手机号码" />
    </TableData>
</template>

<script lang="ts">
import TableData from "@/components/common/TableData.vue";

export default defineComponent({
    name: "USER",
    components: { TableData },

    setup() {
        let search = reactive({
            name: '',
        } as any)
        return {
            search
        }
    },
})
</script>
```

搜索参数为一个对象，尽量在search参数中添加需要搜索的条件，并在php代码中对应处理。

### 筛选

页面默认自带了筛选项，可对应页面数据属性进行筛选、排序操作。

具体修改以及调整可参照 [`筛选`](/he-xin-gong-neng/shu-ju-biao-ge/shu-ju-ye-mian/shai-xuan)

### 表格按钮

以上代码中 `handle、_handleBtn` 为按钮处理方法，`view、edit、`分别为数据表格中的查看按钮、编辑按钮的回调方法。

具体参考 [`TableData`](/he-xin-gong-neng/shu-ju-biao-ge/shu-ju-ye-mian/shu-ju-biao-ge) 文件方法与属性 。

### 头部按钮

为了统一风格，头部按钮与筛选按钮是连接在一起的。

添加方式如下：

```html
<TableData>
    <template #btn>
        // 如创建按钮
    </template>
</TableData>

```


# 数据表格

主要实现数据页面效果的代码文件

## 介绍

通过二次封装 NavieUI 数据表格，提升CRUD效率，但往往可能因人而异，并不是所有人都喜欢这种开发方式，因此若有修改结构冲动。请参考[ NavieUI 数据表格](https://www.naiveui.com/zh-CN/os-theme/components/data-table)

## API

### Slot&#x20;

| 名称    | 说明                |
| ----- | ----------------- |
| btn   | 头部按钮、如创建按钮与筛选按钮连接 |
| empty | 加载头部按钮之后、如搜索框     |

### Props

<table><thead><tr><th width="249.33333333333331">名称</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td><a href="#search">search</a></td><td><code>Object</code></td><td>搜索参数</td></tr><tr><td><a href="#url">url</a></td><td><code>string</code></td><td>请求url</td></tr><tr><td><del>minHeight</del></td><td><code>Number</code></td><td>最小高度</td></tr><tr><td><del>maxHeight</del></td><td><code>Number</code></td><td>最大高度</td></tr><tr><td><a href="#handle-an-niu">handle</a></td><td>Array&#x3C;DataTableBtn></td><td>表单按钮方法参数</td></tr><tr><td><del>subHeight</del></td><td><code>Number</code></td><td>减去当前数值高度</td></tr><tr><td>isSelection</td><td><code>boolean</code></td><td>是否可选，默认 <code>true</code></td></tr><tr><td>bordered</td><td><code>boolean</code></td><td>是否显示border，默认<code>true</code></td></tr><tr><td>scrollX</td><td><code>Number</code></td><td>表格内容的横向宽度，如果列被水平固定了，则需要设定它</td></tr></tbody></table>

### Search

搜索参数对象格式。

例如：

```typescript
let search = reactive({
    phone: '',
    username: '',
})
```

### URL

* 访问数据接口的具体地址
* 必填项

配置方式通过`.env` 文件中的 APP\_URL + URL 实现。

如：`.env` 中 `APP_URL = http://localhost:3000/`  `url="api/test"`

则TableData中请求数据接口为：`http://localhost:3000/api/test`

### Handle 按钮

* 按钮数据的处理方法
* 默认查看、编辑2个按钮

#### 按钮参数格式

```typescript
interface BtnType {
  size?: SizeType | string,
  emitFunction: string,
  text: string | Function | any,
  type?:BtnTypeStr| string
}

示例如下：

{
  size: 'small',
  emitFunction: "view",
  type: 'info',
  text: "编辑",
}

```

#### 追加按钮方式

通过 `this._handleBtn[0].NButtons.push` 实现。

添加参数需要按钮按钮参数格式

示例如下：

```typescript
const changePassword = {
    type: 'error',
    text: '修改密码',
    emitFunction: 'changePassword'
}
this._handleBtn[0].NButtons.push(changePassword);
this._handleBtn[0].width = 280;
```

#### 修改按钮属性

通过操作数组 `this._handleBtn[0].NButtons` 实现。

示例如下：

```typescript
this._handleBtn[0].NButtons[0].text = '查看2'
```


# 筛选

## 介绍

很多时候每个页面的数据筛选功能大致一样，无外乎只是数据不一样而已，因此作为公共功能，所以在自改动的情况下，需要注意功能需求是否是公共需求。

功能包含以下：

* 自选择列
* 显示顺序
* 导出、删除所选数据

## 数据格式（必要）

如果使用整个Yaa项目进行开发，数据格式可以忽略。但如果将vue项目进行拆分出去，需要后端数据接口匹配该格式、或筛选数据格式匹配后端服务数据格式。

## API

### Props

| 名称           | 类型      | 说明            |
| ------------ | ------- | ------------- |
| ~~toModel~~  | Object  | 模型实例          |
| toUrl        | String  | 当前数据表格请求的url  |
| showModal    | Boolean | 是否显示，默认 false |
| tableTitle   | Arrary  | 自选列数据         |
| attribute    | Array   | 显示顺序数据        |
| selectRowArr | Array   | 自选列中勾选数据      |


# 实例模型

可以使用Yii2自带GII 工具生成模型，建议继承 `app\common\models\Model`

* 访问部署域名 +/gii ，生成 model 实例文件
* 继承 `app\common\models\Model`
* 首次生成文件建议放在 `app\common\models\other`下，详情原因参考 [Yaa项目结构](/ji-chu/xiang-mu-jie-gou)

在各个模块中使用实例模型时建议采取以下方式。

## 示例

以`user`模型作为示例，分别三个模块（后台、小程序、前台）需要用户数据时。

因生成`user`模型首次文件为 `app\common\models\other\User`&#x20;

可对应建立三个模块（backend、applets、frontend）在代码中分别为：

```

├── modules    
│   ├── backend
│   ├── applets
│   ├── frontend

对应中建立不同的  user 文件 如下：

├── modules    
│   ├── backend
│   │   ├── models
│   │   │  ├── User.php
│   ├── applets
│   │   ├── models
│   │   │  ├── User.php
│   ├── frontend
│   │   ├── models
│   │   │  ├── User.php

```

以上三个文件User.php 各司其职，便于管理维护。


# 控制器

可以使用Yii2自带GII 工具生成文件。

参考实例模型处于方式，便于各个模块之间维护。

## 注意事项

<table><thead><tr><th width="281">控制器作用范围</th><th width="406.3333333333333">Yaa文件继承方式</th></tr></thead><tbody><tr><td>Api数据</td><td>umono\multiple\controllers\ApiController</td></tr><tr><td>非Api数据</td><td>umono\multiple\controllers\WebController</td></tr></tbody></table>

使用Api 数据表格需要继承 `umono\multiple\controllers\ApiController`

以上继承方式只做建议，可以根据实际情况进行调整。


# 文档说明

当前文档并不全面，相对于一些拥有vue、yii2开发经验的人来说可能比较上手一些。

📌 [点击提交任何问题及建议](https://github.com/umono/yaa-yii2/issues/new)

{% embed url="<https://github.com/umono/yaa-yii2>" %}
GITHUB 地址
{% endembed %}


