Skip to content

新增页面开发

本文档基于 CondorAdmin 模块化架构,指导如何快速创建新的管理页面。

开发流程

完整流程图

1. 后端开发
   ├── 创建数据库表
   ├── 创建 Model 模型
   ├── 创建 Controller 控制器
   ├── 配置路由
   └── 添加菜单权限 SQL

2. 前端开发
   ├── 创建页面文件
   ├── 定义类型接口
   ├── 创建翻译文件
   ├── 配置路由标签
   └── 生成路由

3. 联调测试
   └── 功能验证

第一步:后端开发

1.1 创建数据库表

表命名规范con_{plugin_name}_xxx

sql
CREATE TABLE `con_system_notice` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `title` varchar(100) NOT NULL COMMENT '标题',
  `content` text COMMENT '内容',
  `type` tinyint NOT NULL DEFAULT '1' COMMENT '类型(1=系统通知 2=活动公告)',
  `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态(0=禁用 1=启用)',
  `weigh` int NOT NULL DEFAULT '0' COMMENT '排序权重',
  `createtime` bigint DEFAULT NULL COMMENT '创建时间',
  `updatetime` bigint DEFAULT NULL COMMENT '更新时间',
  `deleted_at` bigint DEFAULT NULL COMMENT '软删除时间',
  PRIMARY KEY (`id`),
  KEY `status` (`status`),
  KEY `createtime` (`createtime`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统公告表';

字段规范

  • ID:int unsignedbigint unsigned
  • 时间:bigint(Unix 时间戳,非 datetime)
  • 状态:tinyint NOT NULL DEFAULT 1
  • 排序:weigh int NOT NULL DEFAULT 0
  • 字符集:utf8mb4_unicode_ci

1.2 创建 Model

位置plugin/condoradmin/app/model/SystemNotice.php

php
<?php

declare(strict_types=1);

namespace plugin\condoradmin\app\model;

use support\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class SystemNotice extends Model
{
    use SoftDeletes;

    protected $table = 'system_notice';

    public $timestamps = true;

    protected $dateFormat = 'U';  // Unix 时间戳格式

    const CREATED_AT = 'createtime';
    const UPDATED_AT = 'updatetime';
    const DELETED_AT = 'deleted_at';

    protected $guarded = [];  // 或指定 $fillable

    // 字段类型转换
    protected $casts = [
        'status' => 'integer',
        'type' => 'integer',
        'weigh' => 'integer',
    ];
}

关键点

  • ✅ 继承 support\Model
  • ✅ 使用 SoftDeletes trait
  • $dateFormat = 'U'(Unix 时间戳)
  • ✅ 自定义时间字段常量
  • ✅ 添加 declare(strict_types=1);

1.3 创建 Controller

位置plugin/condoradmin/app/controller/NoticeController.php

php
<?php

declare(strict_types=1);

namespace plugin\condoradmin\app\controller;

use plugin\condoradmin\app\library\Backend;
use plugin\condoradmin\app\model\SystemNotice;

class NoticeController extends Backend
{
    protected $model;

    // 搜索字段配置
    protected array $searchable = [
        'title'      => ['type' => 'string'],  // 模糊搜索
        'type'       => ['type' => 'int'],     // 精确搜索
        'status'     => ['type' => 'int'],
        'createtime' => ['type' => 'timestamp'], // 时间范围
    ];

    // 排序字段白名单
    protected array $sortable = ['id', 'weigh', 'createtime'];

    // 下拉选择无需权限
    protected $noNeedRight = ['selectpage'];

    public function __construct()
    {
        $this->model = new SystemNotice();
        parent::__construct();
    }
}

配置说明

配置项说明示例
$searchable可搜索字段['field' => ['type' => 'string']]
$sortable可排序字段['id', 'createtime']
$noNeedRight无需权限验证的方法['selectpage']
$dataLimit数据权限'auth''personal'
$hidden隐藏字段['password']

searchable 类型

type 值适用场景查询方式
'string'文本字段LIKE 模糊查询
'int'数字、状态、外键= 精确查询
'timestamp'时间范围BETWEEN 查询

1.4 配置路由

位置config/route.php

php
use Webman\Route;
use plugin\condoradmin\app\controller\NoticeController;

Route::group('/api/condoradmin', function () {
    // CRUD 完整路由(自动生成 6 个方法)
    createRoutes('/notice', NoticeController::class);
    
    // 等价于手动注册:
    // Route::post('/notice/index', [NoticeController::class, 'index']);
    // Route::post('/notice/add', [NoticeController::class, 'add']);
    // Route::post('/notice/edit', [NoticeController::class, 'edit']);
    // Route::post('/notice/del', [NoticeController::class, 'del']);
    // Route::post('/notice/multi', [NoticeController::class, 'multi']);
    // Route::post('/notice/selectpage', [NoticeController::class, 'selectpage']);
})->middleware([
    \plugin\condoradmin\app\middleware\AuthToken::class,
    \plugin\condoradmin\app\middleware\AuthPermission::class,
]);

只读页面(仅查看):

php
// 仅注册 index 方法
Route::post('/notice/index', [NoticeController::class, 'index']);

1.5 添加菜单权限

位置install.sql 或手动执行

sql
INSERT INTO `con_system_menu_rule` 
(`id`, `is_keep`, `pid`, `name`, `title`, `icon`, `path`, `component`, `i18nkey`, `menu_type`, `weigh`, `status`, `createtime`, `updatetime`) 
VALUES
-- 页面菜单
(200, NULL, 1, 'system_notice', '系统公告', '', '/system/notice', 'view.system_notice', 'route.system_notice', 1, 10, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),

-- 权限按钮
(201, 1, 200, 'notice_index', '查看', '', '/api/condoradmin/notice/index', NULL, 'condor.route.view', 0, 0, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
(202, 1, 200, 'notice_add', '添加', '', '/api/condoradmin/notice/add', NULL, 'condor.route.add', 0, 0, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
(203, 1, 200, 'notice_edit', '编辑', '', '/api/condoradmin/notice/edit', NULL, 'condor.route.edit', 0, 0, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
(204, 1, 200, 'notice_del', '删除', '', '/api/condoradmin/notice/del', NULL, 'condor.route.delete', 0, 0, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
(205, 1, 200, 'notice_multi', '批量操作', '', '/api/condoradmin/notice/multi', NULL, 'condor.route.bulk_actions', 0, 0, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());

字段说明

字段说明
pid页面菜单为父菜单 ID(如 1=系统管理),按钮为页面 ID
name页面用路由名,按钮用 {module}_{method}
path页面为前端路由,按钮为后端 API 路径
component页面为 view.{路由名},按钮为 NULL
menu_type1=菜单,0=按钮
is_keep按钮为 1,菜单为 NULL

第二步:前端开发

2.1 创建页面文件

位置src/views/system/notice/index.vue

vue
<script setup lang="ts">
import { ref } from 'vue';
import { $t } from '@/locales';

defineOptions({ name: 'SystemNotice' });

const config = ref<Condor.Table.Config>({
  urls: {
    index: '/api/condoradmin/notice/index',
    add: '/api/condoradmin/notice/add',
    edit: '/api/condoradmin/notice/edit',
    del: '/api/condoradmin/notice/del',
    multi: '/api/condoradmin/notice/multi'
  },
  rowKey(row) { 
    return row.id; 
  },
  columns: [
    { 
      type: 'selection', 
      key: 'id', 
      title: 'ID',
      width: 80
    },
    {
      key: 'title',
      title() { return $t('system.notice.title'); },
      operator: 'like',  // 模糊搜索
      rules: [
        { required: true, message: '请输入标题', trigger: 'blur' },
        { min: 2, max: 100, message: '标题长度为 2-100 个字符', trigger: 'blur' }
      ]
    },
    {
      key: 'type',
      title() { return $t('system.notice.type'); },
      value: 1,
      operator: '=',
      component: { 
        name: 'condor-dict-radio',
        props: { code: 'notice_type' }
      }
    },
    {
      key: 'content',
      title() { return $t('system.notice.content'); },
      table: false,  // 列表不显示
      component: { 
        name: 'condor-editor'  // 富文本编辑器
      }
    },
    {
      key: 'status',
      title() { return $t('condor.common.status'); },
      value: 1,
      operator: '=',
      component: { 
        name: 'n-switch', 
        props: { 
          checkedValue: 1, 
          uncheckedValue: 0 
        } 
      }
    },
    {
      key: 'weigh',
      title() { return $t('condor.common.weigh'); },
      value: 0,
      operator: false,  // 不作为搜索条件
      component: { 
        name: 'n-input-number'
      }
    },
    {
      key: 'createtime',
      title() { return $t('condor.common.createtime'); },
      form: false,  // 表单不显示
      operator: 'BETWEEN',  // 时间范围搜索
      render: 'datetime'
    },
    { 
      type: 'operate', 
      title() { return $t('common.operate'); }, 
      width: 120, 
      buttons: ['edit', 'del']
    }
  ]
});
</script>

<template>
  <div class="h-full">
    <CondorTable :config="config" />
  </div>
</template>

CondorTable 列配置速查

配置项说明示例
key字段名'username'
title()列标题(函数,支持 i18n)() => $t('xxx.username')
operator搜索操作符'like' / '=' / 'BETWEEN' / false
table是否在列表显示false(默认 true)
form是否在表单显示false(默认 true)
value默认值1
rules表单验证规则[{ required: true }]
component表单组件{ name: 'n-input' }
render列渲染类型'datetime' / 'image'

常用组件

组件用途配置示例
n-input文本输入{ name: 'n-input' }
n-input-number数字输入{ name: 'n-input-number' }
n-switch开关{ name: 'n-switch', props: { checkedValue: 1, uncheckedValue: 0 } }
condor-dict-radio字典单选{ name: 'condor-dict-radio', props: { code: 'dict_code' } }
condor-dict-select字典下拉{ name: 'condor-dict-select', props: { code: 'dict_code' } }
condor-select远程下拉{ name: 'condor-select', props: { url: '/api/xxx/selectpage' } }
condor-editor富文本{ name: 'condor-editor' }
condor-upload文件上传{ name: 'condor-upload', props: { type: 'image' } }

2.2 定义类型接口

位置src/views/system/notice/types/i18n.d.ts

typescript
export interface SystemNoticeSchema {
  title: string;
  content: string;
  type: string;
  status: string;
  weigh: string;
  createtime: string;
  updatetime: string;
}

注册到全局(可选):

如果需要全局类型提示,在 src/typings/i18n/index.d.ts 添加:

typescript
import type { SystemNoticeSchema } from '@/views/system/notice/types/i18n';

declare global {
  namespace App.I18n {
    interface PageSchema {
      system: {
        notice: SystemNoticeSchema;
        // ... 其他页面
      };
    }
  }
}

2.3 创建翻译文件

中文翻译

位置src/views/system/notice/locales/zh-cn.ts

typescript
import type { SystemNoticeSchema } from '../types/i18n';

const local: SystemNoticeSchema = {
  title: '标题',
  content: '内容',
  type: '类型',
  status: '状态',
  weigh: '排序',
  createtime: '创建时间',
  updatetime: '更新时间'
};

export default local;

英文翻译

位置src/views/system/notice/locales/en-us.ts

typescript
import type { SystemNoticeSchema } from '../types/i18n';

const local: SystemNoticeSchema = {
  title: 'Title',
  content: 'Content',
  type: 'Type',
  status: 'Status',
  weigh: 'Weight',
  createtime: 'Create Time',
  updatetime: 'Update Time'
};

export default local;

翻译入口

位置src/views/system/notice/locales/index.ts

typescript
import zhCn from './zh-cn';
import enUs from './en-us';

export default {
  'zh-CN': zhCn,
  'en-US': enUs
};

2.4 配置路由标签

位置src/locales/langs/zh-cn.ts 或专门的 route.ts

typescript
const locale: App.I18n.Schema = {
  route: {
    system_notice: '系统公告'
  }
};

英文src/locales/langs/en-us.ts

typescript
const locale: App.I18n.Schema = {
  route: {
    system_notice: 'System Notice'
  }
};

2.5 生成路由

bash
# 生成路由
pnpm sa gen-route

# 类型检查
pnpm typecheck

# 代码检查
pnpm lint

自动生成的文件(不要手动编辑):

  • src/router/elegant/routes.ts
  • src/router/elegant/imports.ts
  • src/router/elegant/transform.ts
  • src/typings/elegant-router.d.ts

第三步:联调测试

3.1 启动服务

bash
# 后端
cd condor-webman
php start.php start

# 前端
cd condor-admin
pnpm dev

3.2 功能验证

  • ✅ 菜单显示正常
  • ✅ 列表查询
  • ✅ 搜索过滤
  • ✅ 新增数据
  • ✅ 编辑数据
  • ✅ 删除数据
  • ✅ 批量操作
  • ✅ 权限控制

高级场景

多语言表(TranslatableBackend)

后端 Controller

php
<?php

namespace plugin\condoradmin\app\controller;

use plugin\condoradmin\app\library\TranslatableBackend;
use plugin\condoradmin\app\model\SystemNotice;
use plugin\condoradmin\app\model\SystemNoticeTranslations;

class NoticeController extends TranslatableBackend
{
    protected $model;
    protected $translationModel = null;

    // 多语言字段
    protected array $multilingualFields = ['title', 'content'];

    protected array $searchable = [
        'title' => ['type' => 'string'],
        'status' => ['type' => 'int'],
    ];

    protected array $sortable = ['id', 'weigh', 'createtime'];

    public function __construct()
    {
        $this->model = new SystemNotice();
        $this->translationModel = new SystemNoticeTranslations();
        parent::__construct();
    }
}

翻译表结构

sql
CREATE TABLE `con_system_notice_translations` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `main_id` int unsigned NOT NULL COMMENT '主表ID',
  `locale` varchar(10) NOT NULL COMMENT '语言(zh-cn/en-us)',
  `title` varchar(100) DEFAULT NULL COMMENT '标题',
  `content` text COMMENT '内容',
  PRIMARY KEY (`id`),
  UNIQUE KEY `main_locale` (`main_id`,`locale`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

自定义查询

php
public function index(Request $request)
{
    // 自定义查询逻辑
    $query = $this->getQueryBuilder();
    
    // 添加额外条件
    $query->where('status', 1);
    $query->with(['user', 'category']);
    
    // 继续使用 Backend 的分页逻辑
    return parent::index($request);
}

数据权限

php
class NoticeController extends Backend
{
    // 启用数据权限
    protected $dataLimit = 'auth';  // 或 'personal'
    
    // 数据权限字段
    protected $dataLimitField = 'admin_id';
}

最佳实践

1. 表设计

  • ✅ 使用 Unix 时间戳(bigint)而非 datetime
  • ✅ 添加 statusweigh 标准字段
  • ✅ 添加 createtimeupdatetimedeleted_at
  • ✅ 字段名使用有意义的英文单词
  • ✅ 添加字段注释

2. 控制器

  • $model 在构造函数中实例化
  • $searchable 配置可搜索字段
  • $sortable 限制可排序字段
  • ✅ 使用 protected array 类型声明

3. 前端页面

  • ✅ 使用 CondorTable 配置模式
  • ✅ 所有标题使用 $t() 国际化
  • ✅ 配置合适的表单验证规则
  • ✅ 选择合适的表单组件

4. 翻译

  • ✅ 前后端翻译键保持一致
  • ✅ 使用类型接口约束翻译内容
  • ✅ 提供中英文双语

常见问题

Q1: 菜单不显示?

检查:

  1. SQL 菜单是否正确插入
  2. menu_type 是否为 1
  3. status 是否为 1
  4. 当前用户是否有权限

Q2: 列表查询失败?

检查:

  1. 后端路由是否注册
  2. Controller 是否继承 Backend
  3. $model 是否正确实例化
  4. 数据库表是否存在

Q3: 表单提交失败?

检查:

  1. 字段名是否与数据库一致
  2. 验证规则是否正确
  3. API 路径是否正确
  4. 权限是否配置

相关文档