iCMS 应用开发流程
概述
iCMS v8 提供了完整的应用开发框架,允许开发者创建功能丰富的第三方应用。本文档基于 article(文章应用) 的实际代码编写,详细介绍了应用的开发流程、目录结构和各个组件的作用。
⚠️ 重要提示
本文档所有示例代码均参考 app/article/ 实际代码,确保了写法的准确性。关键要点:
类继承关系(必须遵循)
- ✅ TestAdmincp → 继承
AdmincpCommon+ 使用AdmincpCommonTrait这部分可以 去查看 iCMS 后台应用开发说明 - ✅ TestApp → 继承
AppsBase+ 使用AppsAppTrait - ✅ TestUserApp → 继承
UserContentApp - ✅ TestEvent → 继承
DBEvent - ✅ TestFunc → 继承
AppsFuncCommon+ 实现AppsFuncInterface - ✅ TestCategoryAdmincp → 继承
NodeAdmincp(构造函数中传入 APPID)
方法命名规范
- ✅ 后台管理(Admincp):使用
do_xxx()前缀 - ✅ 用户中心(UserApp):使用
api_xxx()或done_xxx()前缀 - ✅ 前台应用(App):使用
do_xxx()前缀 - ✅ 模板标签(Func):使用
public static function xxx($vars)
Model 事件系统
- ✅ 在 Model 中使用
$events数组定义事件 - ✅ 事件方法签名:
public static function 事件名($response, $Builder, $event, $isMulti) - ✅ 使用
$casts定义字段类型转换 - ✅ 使用
$callback定义数据库错误回调
配置文件格式
- ✅ 菜单
href不包含admincp.php,直接写/test/index - ✅ 批处理配置使用键值对形式,键为操作标识
- ✅ 使用
caption而不是name作为菜单标题(在 admincp.json 中)
快速开始
1. 创建应用
- 在后台 应用管理 中添加一个应用,选择 第三方应用
- 在
app目录下创建与应用同名的目录(例如:app/test) - 按照标准目录结构创建相应的文件
2. 应用命名规范
- 应用目录名:小写字母,如
test - 类文件名:首字母大写驼峰命名,如
TestApp.php - 类名:与文件名一致,如
class TestApp
目录结构总览
app/test/
├── TestAdmincp.php # 后台管理程序
├── TestApp.php # 前台程序
├── TestNodeAdmincp.php # 分类管理
├── TestDataModel.php # 数据表模型(icms_test_data)
├── TestModel.php # 主表模型(icms_test)
├── TestEvent.php # 事件处理类
├── TestFunc.php # 模板标签类
├── TestFuncSearch.php # 搜索模板标签类
├── TestHook.php # 系统钩子定义
├── Test.php # 应用主类
├── TestUserApp.php # 用户中心程序
├── TestAaaAdmincp.php # 其它模块后台管理
├── TestAaaModel.php # 其它模块数据模型
├── TestAaa.php # 其它模块主类
├── assets/ # 静态资源目录
│ ├── add.css # 样式文件
│ └── js/
│ ├── add.js # 添加页面脚本
│ ├── edit.js # 编辑页面脚本
│ ├── index.js # 列表页面脚本
│ ├── user.js # 用户页面脚本
│ └── vEditor.js # 自定义脚本
├── etc/ # 配置文件目录
│ ├── admincp/
│ │ ├── Test.index.batch.json # 批处理配置
│ │ ├── home.quick.json # 快捷链接配置
│ │ └── home.stats.json # 统计配置
│ ├── menu/
│ │ ├── admincp.cache.json # 缓存菜单配置
│ │ ├── admincp.json # 后台主菜单配置
│ │ └── usercp.content.json # 用户中心菜单配置
│ └── route/
│ ├── node.json # 分类路由配置
│ └── route.json # 应用路由配置
└── views/ # 视图模板目录
├── aaa/ # 子模块模板目录
│ ├── bbb.html # aaa 模块的 bbb 页面
│ ├── ccc.html # aaa 模块的 ccc 页面
│ └── index.html # aaa 模块主页
├── app/
│ ├── config/
│ │ ├── Test.html # 应用配置页面
│ │ └── Test.sphinx.html # 应用扩展配置页面
│ └── node/
│ └── add.html # 自定义分类添加页面
├── batch.html # 批处理 UI 模板
├── add.html # 添加内容页面
├── edit.html # 编辑内容页面
└── index.html # 管理列表页面
核心文件详解
1. 后台管理程序 (TestAdmincp.php)
作用:处理应用的后台管理逻辑,包括增删改查等操作。
功能特点:
- 继承
AdmincpCommon并使用AdmincpCommonTrait - 实现后台管理的各种方法(增、删、改、查)
- 处理表单提交和数据验证
- 权限控制和访问验证
常用方法:
do_index()- 列表页面do_add()- 添加/编辑页面do_save()- 保存数据do_delete()- 删除数据do_batch()- 批量操作
示例(参考 ArticleAdmincp.php):
<?php
defined('iPHP') or exit('What are you doing?');
defined('APP_URL') or define('APP_URL', '/admincp.php/test');
class TestAdmincp extends AdmincpCommon
{
use AdmincpCommonTrait;
public static $orderBy = [
'id' => 'ID',
'hits' => '点击',
'postime' => '时间',
];
/**
* 添加/编辑内容
*/
public function do_add()
{
$rs = [];
// 如果有ID则获取数据(编辑模式)
if ($this->id) {
$rs = Test::get($this->id);
}
// 获取分类
$rs['cid'] = $rs['cid'] ?: (int) Request::get('cid');
$Node = $rs['cid'] ? Node::get($rs['cid']) : [];
// 新增时的默认值
if (empty($this->id)) {
$rs['status'] = '1';
$rs['postype'] = '1';
$rs['editor'] = Admin::$nickname;
$rs['userid'] = Admin::$user_id;
}
include self::view();
}
/**
* 保存数据
*/
public function do_save()
{
// 处理数据...
$data = $this->data();
if ($this->id) {
Test::update($data, ['id' => $this->id]);
} else {
$this->id = Test::create($data);
}
return [true,'保存成功'];
}
}
访问路径:admincp.php/test/index、admincp.php/test/add 等
2. 前台程序 (TestApp.php)
作用:处理应用的前台逻辑,包括页面渲染和 API 接口。
功能特点:
- 继承
AppsBase并使用AppsAppTrait do_xxx()方法:渲染模板页面api_xxx()方法:提供 API 接口(返回 JSON)- 静态方法:工具方法,供模板标签等调用
display()方法:通用的数据展示方法
方法类型说明
1. do_ 前缀方法 - 页面渲染(有模板)
- 用于渲染 HTML 页面
- 使用
self::render()返回视图 - 访问路径:
api.php/test/方法名
2. api_ 前缀方法 - API 接口(无模板)
- 用于提供数据接口
- 返回数组或 JSON 格式数据
- 通常供前端 Ajax 调用
3. display() 方法 - 通用展示
- 标准的数据展示方法
- 调用
getData()获取数据 - 调用
values()处理数据 - 调用
render()渲染模板
4. 静态方法 - 工具方法
- 供其他地方调用的工具方法
- 如
values(),data(),resource()等
示例代码(参考 VideoApp.php 和 XxxxApp.php)
<?php
class TestApp extends AppsBase
{
use AppsAppTrait;
protected static $DATA = [];
/**
* do_ 前缀:页面渲染方法(有模板)
* 访问:api.php/test/detail&id=1
*/
public function do_detail($id = null, $tpl = null)
{
$id === null && $id = (int)Request::get('id');
// 获取数据
$test = TestModel::where([
'id' => $id,
'status' => 1
])->find();
empty($test) && self::alert(['errors:not_found', [self::$app, 'id', $id]], 10001);
// 处理数据
$vars = ['tag' => true, 'user' => true];
self::values($test, $vars, $tpl);
// 渲染模板
$tpl === null && $tpl = self::$DATA['node']['template']['test:detail'];
return self::render($test, $tpl);
}
/**
* api_ 前缀:API 接口方法(无模板,返回JSON)
* 访问:api.php/test/get_list
*/
public function api_get_list()
{
$page = Request::get('page', 1);
$pageSize = Request::get('pageSize', 10);
$_GET['page'] = $page;
$obj = TestModel::where(['status' => 1]);
$obj->orderby('id', 'desc');
$rows = $obj->paging($pageSize);
return [
'list' => $rows,
'total' => Paging::$total,
'page' => $page,
'pageSize' => $pageSize
];
}
/**
* api_ 前缀:API 接口 - 提交反馈
*/
public function api_feedback()
{
$data = [
'userid' => User::$id,
'test_id' => Request::post('test_id'),
'content' => Request::post('content'),
'type' => Request::post('type'),
'ip' => Request::ip(),
'created' => $_SERVER['REQUEST_TIME'],
];
TestFeedbackModel::create($data);
return [true, '非常感谢您的反馈!'];
}
/**
* display() 方法:通用展示方法
* 供路由或其他地方调用
*/
public function display($value, $field = 'id', $tpl = true)
{
$vars = ['tag' => true, 'user' => true];
// 获取主表数据
$test = $this->getData($value, $field, $tpl);
if ($test === false) {
return false;
}
// 获取扩展表数据
$testData = TestDataModel::field('body, images')
->where('test_id', $test['id'])
->first();
// 处理数据
self::values($test, $testData, $vars, $tpl);
self::getCustomData($test, $vars);
// 渲染
return self::render($test, $tpl);
}
/**
* 静态方法:数据处理
* @param array $test 主表数据
* @param array $data 扩展数据
* @param array $vars 变量配置
* @param mixed $tpl 模板
*/
public static function values(&$test, $data = null, $vars = [], $tpl = false)
{
self::initialize($test, $tpl);
// 处理扩展数据
if ($data) {
$test['images'] = FilesPic::findImgUrl($data['images']);
$test['body'] = $data['body'];
}
// 处理标签
$vars['tag'] && TagApp::getArray($test, $test['node']['name'], 'tags');
// 使用 AppsCommon 处理通用数据
AppsCommon::init($test, $vars)
->link() // 生成链接
->user() // 用户信息
->comment() // 评论数
->pic() // 图片处理
->hits() // 点击数
->params(); // 参数处理
return $test;
}
/**
* 静态方法:获取扩展数据
* @param array $idArray ID数组
* @param string $fields 字段
*/
public static function data($idArray = [], $fields = '*')
{
$data = TestDataModel::field($fields)->where([
'test_id' => $idArray,
])->select();
// 转换为以 test_id 为键的数组
$data = array_column($data, null, 'test_id');
return $data;
}
/**
* 静态方法:处理资源数据
*/
public static function resource($value)
{
if (is_array($value)) {
// 生成URL
$value['iurl'] = (array) Route::get('test:resource', [$value]);
$value['url'] = $value['iurl']['url'];
// 生成链接
$value['link'] = sprintf(
'<a href="%s" class="test_resource_link" target="_blank">%s</a>',
$value['url'],
$value['title']
);
}
return $value;
}
}
访问路径说明
do_ 方法:
api.php/test/detail?id=1→do_detail()api.php/test/list→do_list()
api_ 方法:
api.php/test/get_list→api_get_list()api.php/test/feedback→api_feedback()
display() 方法:
- 通常由路由系统调用
- 或在其他代码中调用:
(new TestApp())->display($id)
核心方法详解
1. getData() - 获取数据(继承自 AppsAppTrait)
$test = $this->getData($value, $field, $tpl);
参数:
$value: 值(如 ID)$field: 字段名(默认 'id')$tpl: 模板(true/false/模板路径)
返回:数组或 false
2. self::render() - 渲染模板
return self::render($data, $tpl);
return self::render($data, $tpl, 'varName'); // 自定义变量名
return self::render($data, $tpl, 'varName', 'prefix'); // 自定义前缀
参数:
$data: 数据数组$tpl: 模板路径$varName: 模板变量名(可选)$prefix: 变量前缀(可选)
3. AppsCommon::init() - 通用数据处理
AppsCommon::init($test, $vars)
->link() // 生成 URL 和 link
->user() // 处理用户信息
->comment() // 处理评论数
->pic() // 处理图片
->hits() // 处理点击数
->params() // 处理参数
->text2link(); // 文本转链接(可选)
链式调用:可根据需要选择调用的方法
完整示例:视频应用(参考 VideoApp.php)
class VideoApp extends AppsBase
{
use AppsAppTrait;
protected static $DATA = [];
/**
* 播放页面
*/
public function do_play($id = null, $vid = null, $tpl = null)
{
$id === null && $id = (int)Request::get('id');
$vid === null && $vid = (int)Request::get('vid');
$resource = VideoResourceModel::where([
'id' => $id,
'status' => 1
])->find();
empty($resource) && self::alert(['errors:not_found', [self::$app, 'id', $id]], 10001);
$resource = self::resource($resource);
$tpl === null && $tpl = self::$DATA['node']['template']['video:play'];
// HTTPS 处理
if (iPHP_REQUEST_SCHEME == 'https') {
$resource['src'] = str_replace('http://', 'https://', $resource['src']);
}
$vars = [];
AppsCommon::init($resource, $vars)->hits();
$resource['params'] = [
'id' => $resource['id'],
'video_id' => $resource['video_id'],
'src' => $resource['src'],
];
return self::render($resource, $tpl, 'player', 'video');
}
/**
* 获取资源(API接口)
*/
public function api_get_res()
{
View::assign('vrs_count', Request::get('count'));
View::assign('vrs_video_id', Request::get('video_id'));
View::assign('vrs_source', Request::get('source'));
View::display('iCMS://video.ajax.htm');
}
}
重要提示
- ✅ 方法命名:
do_用于页面,api_用于接口 - ✅ 参数处理:使用
Request::get()/Request::post() - ✅ 数据验证:空数据使用
self::alert()返回错误 - ✅ 模板渲染:使用
self::render()而不是$this->view() - ✅ API返回:返回数组,系统自动转 JSON
- ✅ 静态方法:供模板标签或其他地方调用
- ✅ 链式调用:使用
AppsCommon::init()->link()->user()->...
3. 分类管理 (TestCategoryAdmincp.php)
作用:管理应用的分类体系,继承自 NodeAdmincp。
功能特点:
- 继承系统分类管理功能
- 可自定义分类字段和验证规则
- 支持分类树形结构
- 可扩展分类特殊属性
使用场景:
- 文章分类
- 产品分类
- 视频分类
- 任何需要分类管理的场景
示例(参考 ArticleCategoryAdmincp.php):
<?php
defined('iPHP') or exit('What are you doing?');
/**
* 测试应用分类管理
*/
class TestCategoryAdmincp extends NodeAdmincp
{
public function __construct()
{
parent::__construct(iCMS_APP_TEST); // 传入应用ID
$this->app = 'test';
$this->title = '测试';
$this->primary = 'cid';
$this->CONTENT_MODEL = new TestModel();
$this->NODE_NAME = "栏目";
/**
* URL规则选项(可选)
*/
// $this->setRule();
}
}
注意事项:
- 必须在构造函数中调用
parent::__construct(应用ID) $this->CONTENT_MODEL指定内容模型$this->NODE_NAME定义分类名称
4. 数据模型 (TestModel.php / TestDataModel.php)
TestModel.php - 主表模型
作用:对应数据库主表 icms_test,处理主要业务数据。
功能特点:
- 继承
Model基类 - 使用
$casts定义字段类型转换 - 使用
$events数组定义事件监听 - 数据验证和过滤
示例(参考 ArticleModel.php):
<?php
defined('iPHP') or exit('What are you doing?');
class TestModel extends Model
{
/**
* 状态映射
*/
public static $statusMap = [
'0' => '草稿',
'1' => '正常',
'2' => '回收站',
'3' => '待审核',
];
/**
* 字段类型转换
*/
protected $casts = [
'picdata' => 'array', // 自动转换为数组
'nodeAttrs' => 'array',
];
/**
* 事件监听
*/
protected $events = [
'delete' => ['TestEvent', 'delete'], // 删除前
'changed' => ['TestEvent', 'changed'], // 创建或更新后
];
}
重要属性说明:
$casts- 字段类型自动转换(如 JSON 转数组)$events- 模型事件绑定到 Event 类的方法$statusMap- 状态值的文本映射
TestDataModel.php - 数据表模型
作用:对应扩展数据表 icms_test_data,存储更多字段信息。
使用场景:
- 主表存储核心字段(标题、分类、状态等)
- 数据表存储扩展字段(详细内容、自定义字段等)
- 实现表分离,提高查询效率
- 支持分表存储(sharding)
示例(参考 ArticleDataModel.php):
<?php
defined('iPHP') or exit('What are you doing?');
class TestDataModel extends Model
{
protected $casts = [
// 'body' => 'html',
];
/**
* 数据库错误回调
*/
protected $callback = [
'SQLSTATE:42S02' => [__CLASS__, 'createTable'], // 表不存在自动创建
];
public static $appid = iCMS_APP_TEST;
/**
* 分表策略
* @param int $test_id 主表ID
* @return TestDataModel
*/
public static function sharding($test_id)
{
$model = self::getInstance();
$model->sharding = (int)$test_id % 10; // 按ID取模分10张表
return $model;
}
/**
* 自动创建表
*/
public static function createTable()
{
try {
$target = self::getTableName();
$source = self::table(__CLASS__);
DB::copy($source, $target);
return true;
} catch (sException $ex) {
$state = $ex->getState();
if ($state === '42000') { // 无创建表权限
throw $ex;
}
return false;
}
}
}
分表使用示例:
// 使用分表查询
$dataModel = TestDataModel::sharding($test_id);
$data = $dataModel->where('test_id', $test_id)->get();
5. 事件处理类 (TestEvent.php)
作用:处理模型事件,如数据创建、更新、删除时的关联操作。
功能特点:
- 继承
DBEvent基类 - 监听模型事件(created、updated、deleted等)
- 处理关联数据更新(分类、标签、属性等)
- 文件和缓存管理
事件方法签名:
所有事件方法必须遵循以下签名:
public static function 事件名($response, $Builder, $event, $isMulti)
常用事件:
changed- 创建或更新后触发(包含 created 和 updated)delete- 删除前触发deleted- 删除后触发created- 创建后触发updated- 更新后触发
示例(参考 ArticleEvent.php):
<?php
defined('iPHP') or exit('What are you doing?');
class TestEvent extends DBEvent
{
public static $appid = iCMS_APP_TEST;
/**
* 创建或更新后的事件
* @param array $response 保存的数据
* @param object $Builder 查询构建器
* @param string $event 事件名 (created/updated)
* @param bool $isMulti 是否批量操作
*/
public static function changed($response, $Builder, $event, $isMulti)
{
// 获取受影响的ID列表
if ($event == 'updated') {
$idArr = $Builder->field('id')->pluck();
} else {
$idArr = [$response['id']];
}
array_map(function ($id) use ($response, $Builder, $event, $isMulti) {
if (!$id) {
return;
}
// 处理分类变化
if (isset($response['cid'])) {
Node::change('cid', $response['cid'], $event, $id, self::$appid);
}
// 处理副分类变化
if (isset($response['scid'])) {
AppsMap::change('scid', self::$appid, $response['scid'], $event, $id, 'Node');
}
// 处理属性变化
if (isset($response['pid'])) {
AppsMap::change('pid', self::$appid, $response['pid'], $event, $id, 'Prop');
}
// 处理标签变化
if (isset($response['tags'])) {
Tag::$APPID = self::$appid;
if (!isset($response['tags']['raw'])) {
Tag::change('tags', $response['tags'], $event, $id, self::$appid, $Builder);
}
}
// 处理缩略图变化
if (isset($response['pic']) || isset($response['bpic'])) {
FilesPic::change($response, self::$appid, $event, $id);
}
// 内容归档
if ($event == 'updated') {
Archive::update(self::$appid, $id, $response);
} else {
Archive::save(self::$appid, $id, $response);
}
}, $idArr);
}
/**
* 删除前的事件
*/
public static function delete($response, $Builder, $event, $isMulti)
{
$idArr = $Builder->field('id')->pluck();
array_map(function ($id) {
if (!$id) {
return;
}
// 删除应用映射(分类、标签、属性)
AppsMap::delete(self::$appid, $id, 'Node');
AppsMap::delete(self::$appid, $id, 'Tag');
AppsMap::delete(self::$appid, $id, 'Prop');
// 删除关联文件
Files::delete(self::$appid, $id);
// 删除评论
Comment::delete(self::$appid, $id);
// 删除归档
Archive::delete(self::$appid, $id);
}, $idArr);
}
}
重要说明:
- 事件在 Model 的
$events数组中定义 - 事件方法必须是
public static - 所有事件方法签名必须一致
$response包含保存的数据$Builder可用于查询当前操作的记录
6. 模板标签类 (TestFunc.php)
作用:为模板提供数据标签,在模板中使用 <!--{iCMS:test:方法名}--> 调用。
功能特点:
- 继承
AppsFuncCommon并实现AppsFuncInterface - 所有方法必须是
public static - 使用基类提供的辅助方法(nodes、tags、props、orderby等)
- 返回资源数组供模板渲染
模板调用方式:
<!--{iCMS:test:list}--> # 调用 TestFunc::lists() 注:list=>lists() 比较特殊系统处理过的,其它都是对应的方法
<!--{iCMS:test:list row="10"}--> # 传递参数
<!--{iCMS:test:data id="1"}--> # 获取详情
示例(参考 ArticleFunc.php):
<?php
defined('iPHP') or exit('What are you doing?');
class TestFunc extends AppsFuncCommon implements AppsFuncInterface
{
/**
* 获取列表
*/
public static function lists($vars)
{
$resource = array();
$whereNot = array();
$model = TestModel::field('id');
$status = isset($vars['status']) ? $vars['status'] : 1;
$where = [['status', $status]];
// 条件筛选
$vars['call'] == 'user' && $where[] = ['postype', 0];
$vars['call'] == 'admin' && $where[] = ['postype', 1];
isset($vars['userid']) && $where[] = ['userid', $vars['userid']];
isset($vars['pic']) && $where[] = ['haspic', '1'];
isset($vars['nopic']) && $where[] = ['haspic', '0'];
// 日期范围
if (isset($vars['startdate'])) {
$where[] = ['pubdate', '>=', str2time($vars['startdate'])];
}
if (isset($vars['enddate'])) {
$where[] = ['pubdate', '<=', str2time($vars['enddate'])];
}
// 使用基类方法
self::inited($vars, $model, $where, $whereNot);
self::nodes('cid'); // 处理分类
self::tags(); // 处理标签
self::props(); // 处理属性
self::keywords(); // 处理关键词
// 排序映射
self::orderby([
'hot' => 'hits',
'today' => 'hits_today',
'week' => 'hits_week',
'month' => 'hits_month'
]);
self::where();
return self::getResource(__METHOD__);
}
/**
* 获取资源详情(由 getResource 自动调用)
*/
public static function resource($idsArray = null)
{
$vars = self::$vars;
$resource = TestModel::field('*')
->where($idsArray)
->orderBy('id', $idsArray)
->select();
$resource = self::many($vars, $resource);
return $resource;
}
/**
* 获取单条数据
*/
public static function data($vars)
{
$id = $vars['id'] ?: $vars['test_id'];
$id or self::msg(['func_param', ['iCMS:test:data', 'id']]);
$where = ['id' => $id];
$model = TestModel::where($where);
$hash = md5($model->getSql());
self::paging($hash, __METHOD__);
$resource = self::getCache(self::$cacheName);
if (empty($resource)) {
$resource = $model->field('*')->get();
$cacheTime = isset($vars['time']) ? (int) $vars['time'] : -1;
$vars['cache'] && Cache::set(self::$cacheName, $resource, $cacheTime);
}
return $resource;
}
/**
* 上一篇
*/
public static function prev($vars)
{
$vars['order'] = 'p';
return self::next($vars);
}
/**
* 下一篇
*/
public static function next($vars)
{
// 实现上下篇逻辑
// ...
}
}
模板使用示例:
<!-- 获取列表 -->
<ul>
<!--{iCMS:test:list row="10" cid="1" order="hits"}-->
<li><a href="<!--{$test_list.url}-->"><!--{$test_list.title}--></a></li>
<!--{/iCMS}-->
</ul>
<!-- 获取详情 -->
<!--{iCMS:test:data id="$id"}-->
<h1><!--{$test_data.title}--></h1>
<div><!--{$test_data.content}--></div>
重要说明:
list()返回列表,模板中变量为$test_listdata()返回单条,模板中变量为$test_data- 使用基类方法可以自动处理分类、标签、分页等
7. 搜索模板标签类 (TestFuncSearch.php)
作用:专门处理搜索相关的模板标签 <!--{iCMS:test:search}-->。
功能特点:
- 实现高级搜索功能
- 支持多条件筛选
- 支持分页
- 支持排序
示例:
class TestFuncSearch implements AppsFuncInterface
{
public static function search($vars)
{
$keyword = $vars['keyword'] ?? '';
$cid = $vars['cid'] ?? 0;
$page = $vars['page'] ?? 1;
$pagesize = $vars['pagesize'] ?? 20;
$query = TestModel::query();
// 关键词搜索
if ($keyword) {
$query->where('title', 'like', "%{$keyword}%")
->orWhere('content', 'like', "%{$keyword}%");
}
// 分类筛选
if ($cid) {
$query->where('cid', $cid);
}
// 分页
$total = $query->count();
$list = $query->offset(($page - 1) * $pagesize)
->limit($pagesize)
->get();
return [
'list' => $list,
'total' => $total,
'page' => $page,
'pagesize' => $pagesize,
];
}
}
8. 系统钩子 (TestHook.php)
作用:定义和处理系统级别的钩子,在特定事件触发时执行。
功能特点:
- 监听系统事件
- 扩展系统功能
- 不修改核心代码的情况下添加功能
- 实现插件化开发
常用钩子场景:
- 用户注册/登录时
- 内容发布时
- 系统初始化时
- 缓存更新时
示例:
class TestHook
{
// 注册钩子
public static function hooks()
{
return [
'user.register.after' => 'onUserRegister',
'content.publish.after' => 'onContentPublish',
'system.init' => 'onSystemInit',
];
}
// 用户注册后的钩子
public static function onUserRegister($user)
{
// 为新用户创建默认数据
TestModel::create([
'uid' => $user['id'],
'title' => '欢迎 ' . $user['nickname'],
]);
}
// 内容发布后的钩子
public static function onContentPublish($content)
{
// 更新统计
Test::updateStats($content);
}
}
9. 应用主类 (Test.php)
作用:定义应用的公用方法和常量,提供工具函数。
功能特点:
- 定义应用常量(APP、APPID、状态映射等)
- 提供数据操作的封装方法
- 提供静态工具方法
- 简化常用操作
示例(参考 Article.php):
<?php
class Test
{
const APP = 'test';
const APPID = iCMS_APP_TEST;
/**
* 状态映射
*/
public static $stypeMap = array(
'inbox' => '0', // 草稿
'normal' => '1', // 正常
'trash' => '2', // 回收站
'examine' => '3', // 待审核
'off' => '4', // 未通过
'delete' => '5', // 删除
'lock' => '6', // 锁定
'hidden' => '7', // 隐藏
);
/**
* 检查字段值是否存在
* @param mixed $value 要检查的值
* @param int $id 排除的ID
* @param string $field 字段名
* @return mixed
*/
public static function check($value, $id = 0, $field = 'title')
{
$where = array($field => $value);
$id && $where['id'] = array('<>', $id);
return TestModel::field('id')->where($where)->value();
}
/**
* 获取单个字段值
* @param string $field 字段名
* @param int $id ID
* @return mixed
*/
public static function value($field = 'id', $id = 0)
{
if (empty($id)) {
return '';
}
return TestModel::field($field)->where($id)->value();
}
/**
* 获取单条记录
* @param int $id ID
* @param string $field 字段
* @param array $where 条件
* @return array
*/
public static function get($id = 0, $field = '*', $where = array())
{
$where['id'] = $id;
return TestModel::field($field)->where($where)->get();
}
/**
* 获取数据(包含扩展表)
* @param int $id 主表ID
* @param int $dataId 数据表ID
* @param int $userid 用户ID
* @return array [主表数据, 扩展数据, 其他信息]
*/
public static function data($id = 0, $dataId = 0, $userid = 0)
{
$userid && $where['userid'] = $userid;
$test = TestModel::where($where)->get($id);
$data = array();
if ($test) {
$DataModel = TestDataModel::sharding($test['id']);
try {
$DataModel = $DataModel->where('test_id', $test['id']);
$dataId && $DataModel = $DataModel->where('id', $dataId);
$data = $DataModel->get();
} catch (\sException $ex) {
$state = $ex->getState();
if ($state == '42S02') { // 表不存在
// TestDataModel::createTable();
}
}
}
return array($test, $data, []);
}
/**
* 创建记录
* @param array $data 数据
* @return int 新增ID
*/
public static function create($data)
{
return TestModel::create($data, true);
}
/**
* 更新记录
* @param array $data 数据
* @param array $where 条件
* @return int 影响行数
*/
public static function update($data, $where)
{
return TestModel::update($data, $where);
}
/**
* 删除记录
* @param int $id ID
* @return int 影响行数
*/
public static function delete($id)
{
return TestModel::delete($id);
}
}
重要说明:
- 常量
APP和APPID必须定义 - 主类提供对 Model 的封装,简化调用
data()方法用于获取主表+扩展表数据- 使用静态方法,方便全局调用
10. 用户中心程序 (TestUserApp.php)
作用:处理用户中心相关的功能,如用户投稿、内容管理等。
功能特点:
- 继承
UserContentApp基类 - 实现用户权限验证
- 处理用户个人数据
- 前台用户交互
常用方法:
api_manage()- 我的内容管理api_publish()- 发布/编辑页面api_delete()- 删除内容done_save()- 保存数据
示例(参考 ArticleUserApp.php):
<?php
class TestUserApp extends UserContentApp
{
/**
* 内容管理页面
*/
public function api_manage()
{
return $this->display();
}
/**
* 发布/编辑页面
*/
public function api_publish()
{
$config = User::$config['post'];
$id = (int)Request::get('id');
// 获取数据(编辑模式)
if ($id) {
$data = Test::get($id);
// 验证权限
if ($data['userid'] != User::$id) {
AppsBase::alert('无权操作');
}
}
View::assign('test', $data);
return $this->display();
}
/**
* 删除内容
*/
public function api_delete()
{
$id = (int)Request::post('id');
$id or AppsBase::alert('errors:empty:id');
// 验证权限并软删除(状态改为2)
$where = ['id' => $id, 'userid' => User::$id];
return (bool)Test::update(['status' => 2], $where);
}
/**
* 保存数据
*/
public function done_save()
{
$config = User::$config['post'];
// 验证码检查
if ($config['captcha']) {
Captcha::check();
}
// 发布间隔检查
if ($config['interval']) {
$last_postime = TestModel::where(['userid' => User::$id])
->max('postime');
if ((int)$_SERVER['REQUEST_TIME'] - (int)$last_postime < (int)$config['interval']) {
AppsBase::alert('user:publish:interval');
}
}
$data = Request::post();
// 数据过滤和验证
$data = array_filter_keys($data, 'id,cid,title,body,source,author,pic,tags,description');
$data['userid'] = User::$id;
$data['author'] = $data['author'] ?: User::$nickname;
$data['editor'] = User::$nickname;
// 验证必填项
empty($data['title']) && iJson::error('user:publish:empty:title');
empty($data['cid']) && iJson::error('user:publish:empty:cid');
empty($data['body']) && iJson::error('user:publish:empty:body');
// 内容过滤
array_walk_recursive($data, function (&$value, $key) {
$fwd = iPHP::callback('Filter::run', [&$value], false);
$fwd && iJson::error('user:publish:filter_' . $key);
});
$data['pubdate'] = time();
$data['postype'] = '0'; // 用户发布
// 检查权限,决定是否需要审核
$node = NodeCache::getId($data['cid']);
$roleArray = $node['config']['role'];
$data['status'] = UserCenter::checkRole($roleArray['examine']) ? 3 : 1;
// 保存数据
if ($data['id']) {
Test::update($data, ['id' => $data['id'], 'userid' => User::$id]);
} else {
$data['postime'] = time();
$data['id'] = Test::create($data);
}
$msgMap = [
'1' => 'user:publish:success',
'3' => 'user:publish:examine',
];
$url = Route::create('TestUser/manage');
return [true, $msgMap[$data['status']], $url];
}
}
访问路径:api.php/TestUser/manage、api.php/TestUser/publish 等
重要说明:
- 方法前缀使用
api_而不是do_ - 保存方法使用
done_save()前缀 - 必须验证用户权限(userid)
- 用户发布的内容
postype为0 - 根据分类权限决定是否需要审核
11. 其它模块开发
应用可以包含多个子模块,每个模块遵循类似的命名规范。
TestAaaAdmincp.php - 子模块后台管理
作用:管理应用的子模块(如 aaa 模块)。
示例:测试应用(test)的问答模块(qa)
TestQaAdmincp.php- 问答后台管理TestQaModel.php- 问答数据模型(icms_test_qa 表)TestQa.php- 问答主类
TestAaaModel.php - 子模块数据模型
作用:对应子模块的数据表 icms_test_aaa。
TestAaa.php - 子模块主类
作用:提供子模块的公共方法和工具函数。
静态资源目录 (assets/)
目录结构
assets/
├── add.css # 样式文件
└── js/
├── add.js # 添加页面脚本
├── edit.js # 编辑页面脚本
├── index.js # 列表页面脚本
├── user.js # 用户页面脚本
└── vEditor.js # 自定义脚本
JavaScript 文件命名规则
脚本文件名与后台方法对应:
add.js→admincp.php/test/add页面自动加载edit.js→admincp.php/test/edit页面自动加载index.js→admincp.php/test/index页面自动加载user.js→admincp.php/test/user页面自动加载
add.js 示例
define(["iCMS", "vue"], function (iCMS, Vue) {
// 添加一个虚假的历史记录项
window.history.pushState(null, null, window.location.href);
// 监听浏览器的返回按钮事件
window.addEventListener('popstate', function (event) {
if (confirm('您确定要离开此页面吗?')) {
// 如果用户确认,则允许跳转
// 如果需要真正禁用,这里不执行任何操作即可
} else {
// 用户取消,重新推入当前状态
window.history.pushState(null, null, window.location.href);
}
});
// window.addEventListener("beforeunload", function (e) {
// const confirmationMessage = "您确定要离开此页面吗?";
// e.returnValue = confirmationMessage; // 兼容旧浏览器
// return confirmationMessage; // 兼容新浏览器
// });
const factory = {
add_success: function (json) {
console.log(json);
},
edit: function () {
},
add: function () {
$(".cms-editor").on('change', function () {
console.log('内容改变了');
});
},
storage: function () {
},
};
return factory;
});
自定义脚本引用
如 vEditor.js,可以在其它脚本手动引入:
define(["iCMS", "vue", "./vEditor.js"], function (iCMS, Vue, vEd) {
vEd.aaa();//调整vEditor.js内部方法
CSS 文件
可以创建任意 CSS 文件,在模板中引入:
<link rel="stylesheet" href="{{APP_URL}}/test/assets/add.css">
配置文件目录 (etc/)
1. admincp/ - 后台配置
Test.index.batch.json - 批处理配置
作用:定义 admincp.php/test/index 页面的批量操作。
示例(参考 Article.index.batch.json):
{
"pubdate=now": {
"name": "更新发布时间",
"icon": "clock",
"sort": 1,
"call": "default"
},
"status=1&pubdate=now": {
"name": "发布并更新时间",
"icon": "clock",
"sort": 2,
"call": "default",
"show": {
"stype": ["!=", "normal"]
}
},
"status=0": {
"name": "转为草稿",
"icon": "inbox",
"sort": 3,
"call": "default"
},
"status=2": {
"name": "移入回收站",
"icon": "trash-alt",
"sort": 5,
"call": "default"
},
"move": {
"name": "移动栏目",
"icon": "fighter-jet",
"sort": 10,
"call": "move"
},
"prop": {
"name": "设置属性",
"icon": "puzzle-piece",
"sort": 12,
"call": "prop"
},
"tag": {
"name": "设置标签",
"icon": "tags",
"sort": 17,
"call": "tag"
},
"divider1": {
"name": "divider",
"sort": 19
},
"dels": {
"name": "删除",
"icon": "trash-alt",
"sort": 21,
"call": "dels"
}
}
字段说明:
name: 操作名称icon: 图标类名(Font Awesome)sort: 排序call: 调用的方法名(default为默认更新,其他为自定义方法)show: 显示条件(可选)divider: 分隔线(name 为 "divider")
home.quick.json - 快捷链接配置
作用:在管理首页添加快捷操作链接。
示例:
{
"quick": [
{
"name": "添加内容",
"icon": "fa fa-plus",
"url": "admincp.php/test/add",
"class": "btn-primary"
},
{
"name": "内容管理",
"icon": "fa fa-list",
"url": "admincp.php/test/index"
}
]
}
2. menu/ - 菜单配置
admincp.json - 后台主菜单
作用:定义应用在后台的菜单结构。
示例(参考 article/etc/menu/admincp.json):
[
{
"id": "test",
"sort": "10",
"caption": "测试应用",
"icon": "si si-grid",
"children": [
{
"caption": "应用配置",
"href": "/test/config",
"icon": "cog",
"sort": 103,
"access": "/test/config",
"id": "menu_test_config"
},
{
"caption": "添加内容",
"href": "/test/add",
"icon": "edit",
"sort": 104,
"access": "/test/add",
"id": "menu_test_add"
},
{
"caption": "栏目管理",
"href": "/testCategory/tree",
"icon": "sitemap",
"sort": 105,
"access": "/testCategory/tree",
"id": "menu_test_category",
"children": [
{
"caption": "添加栏目",
"href": "/testCategory/add",
"icon": "edit",
"sort": 106,
"access": "/testCategory/add",
"id": "menu_test_category_add"
},
{
"caption": "更新缓存",
"href": "/testCategory/cache",
"icon": "sync",
"ui": "cms:ui:ajax",
"sort": 107,
"access": "/testCategory/cache",
"id": "menu_test_category_cache"
}
],
"expanded": true
},
{
"caption": "-",
"sort": 108,
"access": "test-divider-108",
"id": "menu_test_divider1"
},
{
"caption": "内容管理",
"href": "/test/index",
"icon": "list",
"sort": 109,
"access": "/test/index",
"id": "menu_test_index",
"children": [
{
"caption": "草稿箱",
"href": "/test/index/inbox",
"icon": "inbox",
"sort": 110,
"access": "/test/index/inbox",
"id": "menu_test_inbox"
},
{
"caption": "回收站",
"href": "/test/index/trash",
"icon": "trash-alt",
"sort": 111,
"access": "/test/index/trash",
"id": "menu_test_trash"
}
],
"expanded": true
}
],
"access": "test",
"expanded": false
}
]
字段说明:
id: 菜单唯一标识caption: 菜单标题icon: 图标类名(Font Awesome 或 Simple Icons)href: 链接地址(不含 admincp.php,如/test/index)sort: 排序(数字越小越靠前)access: 权限标识children: 子菜单数组expanded: 是否展开ui: UI 交互方式(如cms:ui:ajax表示 Ajax 请求)caption: "-": 分隔线
admincp.cache.json - 缓存菜单配置
作用:在缓存工具下注入文章相关的缓存清理工具。
示例:
[{
"id": "tools",
"children": [{
"id": "cache",
"children": [{
"caption": "更新文章栏目缓存",
"href": "/articleCategory/cache",
"icon": "sync",
"ui": "cms:ui:ajax"
}, {
"caption": "更新文章栏目统计",
"href": "/articleCategory/recount",
"icon": "sync",
"ui": "cms:ui:ajax"
}]
}]
}]
usercp.content.json - 用户中心菜单
作用:定义用户中心的应用菜单。
示例:
[
{
"id": "ArticleUser",
"sort": 5,
"caption": "我的文章",
"icon": "fa fa-cog",
"children": [{
"id": "ArticleUserManage",
"sort": 5,
"caption": "文章管理",
"url": "ArticleUser/manage",
"icon": "fa fa-cog",
"app": "article",
"template": "iCMS://user/article/manage.htm"
},
{
"id": "ArticleUserPublish",
"sort": 7,
"caption": "撰写文章",
"url": "ArticleUser/publish",
"icon": "fa fa-cog",
"app": "article",
"template": "iCMS://user/article/publish.htm"
}
]
}
]
3. route/ - 路由配置
route.json - 应用路由配置
作用:定义应用的前台路由规则。
示例:
{
"user": "app=user",
"user/home": "app=user&do=home",
"user/manage/category": "app=user&do=manage&s=category",
"{userid}": "app=user&do=home&userid={userid}",
"{userid}/home": "app=user&do=home&userid={userid}",
"{userid}/follower": "app=user&do=follower&userid={userid}",
"{userid}/{cid}": "app=user&do=home&userid={userid}&cid={cid}",
"{userid}/favorite/{id}": "app=user&do=favorite&userid={userid}&id={id}",
}
字段说明:
- "URL 路径":"转发目标"
- "{参数1}/aaa/bbb":"转发目标{参数1}"
- "/aaa/ccc/{参数1}/{参数2}":"转发目标{参数1}/{参数2}"
路由效果:
/test→api.php/test/index/test/123→api.php/test/detail?id=123
node.json - 分类路由配置
作用:定义分类中应用的配置。
示例:
{
"test": {
"label": "test",
"template": "{iTPL}\/test.htm",
"rule": "\/{CDIR}\/{YYYY}\/{MM}{DD}\/{ID}{EXT}",
"tips": "{ID},{0xID},{LINK},{HASH@ID},{HASH@0xID}"
}
}
其它示例:
{
"index": {
"label": "首页",
"template": "{iTPL}\/video.index.htm",
"rule": "\/{CDIR}\/",
"tips": "{CID},{0xCID},{CDIR},{HASH@CID},{HASH@0xCID}",
"info": "[NODE@NAME]的首页模板(可制作用于频道封面、单页等)"
},
"list": {
"label": "列表",
"template": "{iTPL}\/video.list.htm",
"rule": "\/{CDIR}\/index_{P}{EXT}",
"tips": "{CID},{0xCID},{CDIR},{HASH@CID},{HASH@0xCID}",
"info": "当[NODE@NAME]有分页且当前页号大于1时使用该模板"
},
"video": {
"label": "视频",
"template": "{iTPL}\/video.htm",
"rule": "\/video\/{ID}{EXT}",
"tips": "{ID},{0xID},{LINK},{HASH@ID},{HASH@0xID}"
},
"video:play": {
"label": "播放",
"template": "{iTPL}\/video.play.htm",
"rule": "\/play\/{VIDEO:ID}\/{ID}{EXT}",
"tips": "{VIDEO:ID},{ID},{0xID},{HASH@ID},{HASH@0xID}"
},
"video:down": {
"label": "下载",
"template": "{iTPL}\/video.down.htm",
"rule": "\/down\/{VIDEO:ID}\/{ID}{EXT}",
"tips": "{VIDEO:ID},{ID},{0xID},{HASH@ID},{HASH@0xID}"
},
"video:story": {
"label": "剧情",
"template": "{iTPL}\/video.story.htm",
"rule": "\/story\/{VIDEO:ID}\/{ID}{EXT}",
"tips": "{VIDEO:ID},{ID},{0xID},{HASH@ID},{HASH@0xID}"
},
"tag": {
"label": "标签",
"template": "{iTPL}\/video.tag.htm",
"rule": "\/video\/{TKEY}{EXT}",
"tips": "{ID},{0xID},{TKEY},{NAME},{ZH_CN},{HASH@ID},{HASH@0xID}"
}
}
视图模板目录 (views/)
重要说明
iCMS v8 的视图模板使用 PHP 混合模式,不是传统的 Smarty 模板。主要特点:
- ✅ 使用
self::head()和self::foot()输出头尾 - ✅ 使用
<?php ... ?>嵌入 PHP 代码 - ✅ 使用
{{变量名}}输出变量(双花括号) - ✅ 使用
i="cms:ui:xxx"定义 UI 组件 - ✅ 列表页的表格数据由 JavaScript 动态加载
- ✅ 表单使用
i="cms:ui:form"提交
1. 主要模板文件
index.html - 管理列表页
作用:后台内容列表页面。
访问路径:admincp.php/test/index
功能:
- 搜索表单
- 数据表格(由 JS 动态加载)
- 工具栏(添加、编辑、删除、批量操作)
完整示例(参考 article/views/index.html):
<?php
defined('iPHP') or exit('What are you doing?');
self::head();
?>
<div class="content" id="{{APP_MAINID}}">
<!-- 搜索区域 -->
<div class="block block-rounded">
<div class="block-header block-header-default">
<h3 class="block-title">搜索</h3>
<div class="block-options">
<button type="button" class="btn-block-option" data-toggle="block-option" data-action="content_toggle">
<i class="si si-arrow-up"></i>
</button>
</div>
</div>
<div class="block-content block-content-full">
<form action="{{APP_DOURL}}" method="get" class="table-search">
<div class="row">
<!-- 状态筛选 -->
<div class="col-md-6 col-lg-4 mt-1">
<div class="input-group input-group-sm">
<label class="input-group-text">状态</label>
<input type="text" data-source='statusMap' class="form-control"
i="cms:ui:selectpicker" name="status" value="">
</div>
</div>
<!-- 分类筛选 -->
<div class="col-md-6 col-lg-4 mt-1">
<div class="input-group input-group-sm">
<label class="input-group-text">栏目</label>
<input type="text" class="form-control" i="cms:ui:node"
data-node-access="cm" name="cid" value="">
</div>
</div>
<!-- 日期范围 -->
<div class="col-md-6 col-lg-4 mt-1">
<div class="input-group input-group-sm">
<span class="input-group-text">发布时间</span>
<input type="text" class="form-control" i="cms:ui:daterangepicker"
name="pubdate" value="<?php echo $_GET['pubdate']; ?>"
placeholder="开始时间 - 结束时间" />
</div>
</div>
<!-- 排序和每页数量 -->
<div class="col-md-6 col-lg-4 mt-1">
<div class="input-group input-group-sm">
<label class="input-group-text">排序</label>
<select data-source='./orderby' class="form-control"
i="cms:ui:bs-selectpicker" name="orderby">
<option value="">默认排序</option>
</select>
<label class="input-group-text">每页</label>
<input class="form-control" style="max-width:56px;" type="text"
name="pageSize" value="<?php echo Paging::$pageSize; ?>" />
<label class="input-group-text">条</label>
</div>
</div>
<!-- 关键词搜索 -->
<div class="col-md-6 col-lg-4 mt-1">
<div class="input-group input-group-sm">
<label class="input-group-text">查找</label>
<select name="st" class="form-control">
<option value="title" selected>标题</option>
<option value="tag">标签</option>
<option value="id">ID</option>
</select>
<input type="text" name="keywords" class="form-control"
value="<?php echo $_GET['keywords']; ?>" />
</div>
</div>
<!-- 搜索按钮 -->
<div class="col-sm-12 d-flex justify-content-end mt-2">
<button class="btn btn-sm btn-alt-primary table-btn-search me-2" type="submit">
<i class="fa fa-fw fa-search"></i> 搜 索
</button>
<button class="btn btn-sm btn-alt-secondary table-btn-reset" type="reset">
<i class="fa fa-fw fa-repeat"></i> 重 置
</button>
</div>
</div>
</form>
</div>
</div>
<!-- 数据表格区域 -->
<div class="block">
<div class="block-content p-0">
<!-- 工具栏 -->
<div id="toolbar" class="toolbar row">
<div class="input-group input-group-sm" role="group">
<a href="{{test/add}}" id="add" class="btn btn-alt-primary">
<i class="fa fa-fw fa-add"></i> {{#Lang('Add')}}
</a>
<a href="{{test/edit}}" id="edit" class="btn btn-alt-primary table-btn-edit disabled" disabled>
<i class="fa fa-fw fa-edit"></i> {{#Lang('Edit')}}
</a>
<a href="{{test/delete}}" id="delete" class="btn btn-alt-primary table-btn-delete disabled" disabled>
<i class="fa fa-fw fa-trash"></i> {{#Lang('Delete')}}
</a>
<?php AdmincpBatch::group(); ?>
</div>
</div>
<!-- 数据表格(由 JS 动态加载数据) -->
<div class="table-responsive">
<table id="table" width="100%"></table>
</div>
</div>
</div>
</div>
<?php self::foot(); ?>
关键点说明:
-
i="cms:ui:xxx"- UI 组件声明i="cms:ui:selectpicker"- 下拉选择器i="cms:ui:node"- 分类选择器i="cms:ui:daterangepicker"- 日期范围选择器i="cms:ui:bs-selectpicker"- Bootstrap 选择器
-
{{变量名}}- 变量输出{{APP_MAINID}}- 应用主ID{{APP_DOURL}}- 应用处理URL{{test/add}}- 生成URL{{#Lang('Add')}}- 语言包函数
-
<table id="table"></table>- 空表格,数据由对应的index.js动态加载 -
<?php AdmincpBatch::group(); ?>- 批量操作按钮组
add.html - 添加/编辑内容页
作用:后台添加和编辑内容页面(同一个页面)。
访问路径:
- 添加:
admincp.php/test/add - 编辑:
admincp.php/test/add?id=1
完整示例(参考 article/views/add.html):
<?php
defined('iPHP') or exit('What are you doing?');
self::head();
?>
<div class="content" id="{{APP_MAINID}}">
<div class="block">
<!-- 选项卡导航 -->
<ul i="cms:ui:tabs" class="nav nav-tabs nav-tabs-block" data-toggle="tabs" role="tablist">
<li class="nav-item">
<a class="nav-link active" href="#test-base">
<i class="fa fa-fw fa-info-circle"></i> 基本信息
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#test-publish">
<i class="fa fa-fw fa-rocket"></i> 发布设置
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#apps-meta">
<i class="fa fa-fw fa-sitemap"></i> 动态属性
</a>
</li>
</ul>
<div class="block-content">
<!-- 表单 -->
<form action="{{APP_DOURL}}" method="POST" i="cms:ui:form" data-success="cms:test:add:success">
<!-- 隐藏字段 -->
<input name="rs[id]" type="hidden" value="<?php echo $this->id; ?>" />
<input name="rs[userid]" type="hidden" value="<?php echo $rs['userid']; ?>" />
<input name="REFERER" type="hidden" value="<?php echo iPHP_REFERER; ?>" />
<div id="test-add" class="tab-content">
<!-- 基本信息选项卡 -->
<div id="test-base" class="tab-pane active">
<div class="row">
<div class="col-sm-9 border-end">
<!-- 标题 -->
<div class="form-floating mb-3">
<input type="text" name="rs[title]"
class="form-control form-control-lg fw-bold fs-4"
id="title" value="<?php echo $rs['title']; ?>"
data-rule="required" />
<label for="title">标题</label>
</div>
<!-- 分类的动态属性 -->
<?=NodeWidget::attrs($rs['nodeAttrs'],'rs[nodeAttrs]','#cid');?>
<!-- 编辑器区域 -->
<div class="form-group">
<label>内容</label>
<textarea name="rs[body]" class="form-control cms-editor"
i="cms:ui:editor" rows="20"><?php echo $rs['body']; ?></textarea>
</div>
<!-- 提交按钮 -->
<div class="form-group">
<button class="btn btn-primary" type="submit"
data-loading-text="提交中,请稍候...">
<i class="fa fa-fw fa-check"></i> 提交
</button>
</div>
</div>
<div class="col-sm-3">
<!-- 分类选择 -->
<div class="form-floating mb-3">
<input type="text" name="rs[cid]" class="form-control"
id="cid" i="cms:ui:node" data-node-access="cm"
value="<?php echo $rs['cid']; ?>" data-rule="required" />
<label for="cid">栏目</label>
</div>
<!-- 副栏目 -->
<div class="form-floating mb-3">
<input type="text" name="rs[scid]" class="form-control"
id="scid" i="cms:ui:node" data-max="5" multiple="multiple"
value="<?php echo $rs['scid']; ?>" />
<label for="scid">副栏目</label>
</div>
<!-- 缩略图 -->
<div class="form-floating mb-3">
<input type="text" name="rs[pic]" class="form-control"
id="pic" i="cms:ui:pic"
value="<?php echo $rs['pic']; ?>" />
<label for="pic">缩略图</label>
</div>
<!-- 标签 -->
<div class="form-floating mb-3">
<input type="text" name="rs[tags]" class="form-control"
id="tags" i="cms:ui:tags"
value="<?php echo $rs['tags']; ?>" />
<label for="tags">标签</label>
</div>
<!-- 描述 -->
<div class="form-floating mb-3">
<textarea name="rs[description]" class="form-control"
id="description" rows="4"><?php echo $rs['description']; ?></textarea>
<label for="description">描述</label>
</div>
</div>
</div>
</div>
<!-- 发布设置选项卡 -->
<div id="test-publish" class="tab-pane">
<div class="row">
<!-- 状态 -->
<div class="col-md-6 mb-3">
<div class="form-floating">
<select name="rs[status]" class="form-control" id="status"
i="cms:ui:bs-selectpicker" data-source="statusMap">
<option value="<?php echo $rs['status']; ?>" selected></option>
</select>
<label for="status">状态</label>
</div>
</div>
<!-- 发布时间 -->
<div class="col-md-6 mb-3">
<div class="form-floating">
<input type="text" name="rs[pubdate]" class="form-control"
id="pubdate" i="cms:ui:datetimepicker"
value="<?php echo $rs['pubdate'] ? date('Y-m-d H:i:s', $rs['pubdate']) : ''; ?>" />
<label for="pubdate">发布时间</label>
</div>
</div>
<!-- 来源 -->
<div class="col-md-6 mb-3">
<div class="form-floating">
<input type="text" name="rs[source]" class="form-control"
id="source" value="<?php echo $rs['source']; ?>" />
<label for="source">来源</label>
</div>
</div>
<!-- 作者 -->
<div class="col-md-6 mb-3">
<div class="form-floating">
<input type="text" name="rs[author]" class="form-control"
id="author" value="<?php echo $rs['author']; ?>" />
<label for="author">作者</label>
</div>
</div>
</div>
</div>
<!-- 动态属性选项卡 -->
<div id="apps-meta" class="tab-pane">
<?php AppsWidget::metadata($rs['id'], $rs['metadata']); ?>
</div>
</div>
</form>
</div>
</div>
</div>
<?php self::foot(); ?>
关键点说明:
-
i="cms:ui:form"- 表单组件,自动处理提交data-success="cms:test:add:success"- 成功后的回调
-
UI 组件:
i="cms:ui:editor"- 编辑器(UEditor/Markdown)i="cms:ui:node"- 分类选择器i="cms:ui:pic"- 图片上传i="cms:ui:tags"- 标签输入i="cms:ui:datetimepicker"- 日期时间选择器i="cms:ui:bs-selectpicker"- Bootstrap 下拉选择
-
数据绑定:
- 使用
name="rs[字段名]"格式 - 使用
<?php echo $rs['字段名']; ?>输出值 data-rule="required"标记必填项
- 使用
-
小部件:
<?=NodeWidget::attrs(...)?>- 分类动态属性<?php AppsWidget::metadata(...); ?>- 动态属性管理
edit.html - 编辑内容页
作用:复用添加页面。
访问路径:admincp.php/test/edit?id=1
示例(参考 article/views/edit.html):
<?php include self::view('add'); ?>
说明:
- 编辑页面直接复用
add.html - 通过
$this->id判断是添加还是编辑模式 - 编辑时自动填充
$rs数组中的数据
batch.html - 批处理 UI 模板
作用:定义批量操作的表单 UI。
访问路径:系统自动调用
示例(参考 article/views/batch.html):
<!-- 设置副栏目 -->
<div batch="scid">
<div class="input-group input-group-sm">
<label class="input-group-text">副栏目</label>
<input name="BatchData[scid]" class="form-control"
i="cms:ui:node" data-max="5" multiple="multiple"
placeholder="请选择副栏目(可多选)..." value="">
</div>
</div>
<!-- 设置发布类型 -->
<div batch="postype">
<div class="input-group input-group-sm">
<label class="input-group-text">发布类型</label>
<input name="BatchData[postype]" type="text"
data-source='postypeMap' class="form-control"
i="cms:ui:selectpicker" value="">
</div>
</div>
<!-- 设置动态属性 -->
<div batch="meta">
<?php AppsWidget::metadata(0); ?>
</div>
关键点说明:
batch="操作标识"- 与Test.index.batch.json中的 key 对应name="BatchData[字段名]"- 批量数据格式- 可以使用所有
i="cms:ui:xxx"UI 组件
2. 常用 UI 组件列表
iCMS v8 提供了丰富的 UI 组件,通过 i="cms:ui:xxx" 属性使用:
| 组件 | 说明 | 示例 |
|---|---|---|
cms:ui:form |
表单提交 | `` |
cms:ui:editor |
富文本编辑器 | `` |
cms:ui:node |
分类选择器 | `` |
cms:ui:pic |
图片上传 | `` |
cms:ui:tags |
标签输入 | `` |
cms:ui:selectpicker |
下拉选择 | `` |
cms:ui:bs-selectpicker |
Bootstrap选择 | `` |
cms:ui:datepicker |
日期选择 | `` |
cms:ui:datetimepicker |
日期时间选择 | `` |
cms:ui:daterangepicker |
日期范围选择 | `` |
cms:ui:tabs |
选项卡 | `` |
cms:ui:checked |
复选框状态 | `` |
常用属性:
data-source='statusMap'- 数据源(从 Model 的 $statusMap)data-source='./orderby'- 数据源(从 API)data-rule="required"- 验证规则(必填)data-max="5"- 最大选择数量multiple="multiple"- 多选data-success="cms:test:add:success"- 成功回调
3. 应用配置模板 (views/app/config/)
配置模板使用纯 PHP/HTML,系统会自动处理保存。
Test.html - 基本配置
访问路径:admincp.php/apps/config?app=test
示例:
id="pagesize" value="" />
每页显示数量
class="form-check-input" id="enable_audit" value="1"
>
启用审核
>UEditor
>Vditor(Markdown)
编辑器
Test.sphinx.html - 扩展配置
作用:高级或扩展配置。
4. 分类自定义模板 (views/app/node/)
add.html - 分类额外字段
作用:在分类添加/编辑页面添加自定义字段。
示例:
id="meta_icon" value="" />
分类图标
id="meta_color" value="" />
分类颜色
5. 模板变量说明
常用系统变量
| 变量 | 说明 |
|---|---|
{{APP_MAINID}} |
应用主容器ID |
{{APP_DOURL}} |
当前处理URL |
{{APP_URL}} |
应用URL |
{{#Lang('key')}} |
语言包函数 |
id; ?> |
当前编辑的ID |
| `` | 数据字段 |
| `` | 来源URL |
| `` | 管理员昵称 |
| `` | 分页大小 |
PHP 代码规范
// 1. 安全检查(必须)
defined('iPHP') or exit('What are you doing?');
// 2. 输出头部
self::head();
?>
// 3. 输出尾部
self::foot();
?>
6. 实际开发建议
-
复制现有模板
- 从
app/article/views/复制对应文件 - 替换应用名称和字段
- 从
-
最小化模板
views/ ├── index.html # 必需:列表页 ├── add.html # 必需:添加/编辑页 ├── edit.html # 必需:复用 add.html └── batch.html # 可选:批量操作 -
模板复用
// 复用 add.html -
调试技巧
// 查看数据 // 查看配置
数据库设计规范
1. 主表设计 (icms_test)
基本字段:
CREATE TABLE `icms_test` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`cid` int(11) unsigned DEFAULT '0' COMMENT '分类ID',
`uid` int(11) unsigned DEFAULT '0' COMMENT '用户ID',
`title` varchar(255) NOT NULL COMMENT '标题',
`description` varchar(500) DEFAULT NULL COMMENT '描述',
`tags` varchar(255) DEFAULT NULL COMMENT '标签',
`status` tinyint(1) DEFAULT '1' COMMENT '状态:0草稿,1发布,2待审核',
`hits` int(11) unsigned DEFAULT '0' COMMENT '点击数',
`ordernum` int(11) DEFAULT '0' COMMENT '排序',
`created_at` int(11) unsigned DEFAULT '0' COMMENT '创建时间',
`updated_at` int(11) unsigned DEFAULT '0' COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `cid` (`cid`),
KEY `uid` (`uid`),
KEY `status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='测试应用主表';
2. 数据表设计 (icms_test_data)
扩展字段:
CREATE TABLE `icms_test_data` (
`id` int(11) unsigned NOT NULL COMMENT 'ID,关联主表',
`body` longtext COMMENT '正文内容',
`template` varchar(100) DEFAULT NULL COMMENT '模板',
`metadata` text COMMENT '元数据(JSON)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='测试应用数据表';
3. 子模块表设计 (icmstest*)
子模块表命名:icms_应用名_模块名
例如:icms_test_qa(测试应用的问答模块)
开发流程详解
第一步:创建应用
- 在后台 应用管理 → 添加应用
- 选择 第三方应用
- 填写应用信息:
- 应用标识:
test(英文小写) - 应用名称:测试应用
- 应用描述:这是一个测试应用
- 应用标识:
- 记录应用 ID(假设为
iCMS_APP_TEST,值为 8)
第二步:创建目录结构
mkdir app/test
mkdir app/test/assets
mkdir app/test/assets/js
mkdir app/test/etc
mkdir app/test/etc/admincp
mkdir app/test/etc/menu
mkdir app/test/etc/route
mkdir app/test/views
mkdir app/test/views/app
mkdir app/test/views/app/config
第三步:创建核心文件
1. 创建 Test.php(应用主类)
参考:Article.php
/**
* Test 应用主类
*/
defined('iPHP') or exit('What are you doing?');
class Test
{
const APP = 'test';
const APPID = iCMS_APP_TEST; // 应用ID常量
/**
* 状态映射
*/
public static $stypeMap = array(
'inbox' => '0', // 草稿
'normal' => '1', // 正常
'trash' => '2', // 回收站
'examine' => '3', // 待审核
);
/**
* 检查字段值是否存在
*/
public static function check($value, $id = 0, $field = 'title')
{
$where = array($field => $value);
$id && $where['id'] = array('<>', $id);
return TestModel::field('id')->where($where)->value();
}
/**
* 获取单个字段值
*/
public static function value($field = 'id', $id = 0)
{
if (empty($id)) {
return '';
}
return TestModel::field($field)->where($id)->value();
}
/**
* 获取单条记录
*/
public static function get($id = 0, $field = '*', $where = array())
{
$where['id'] = $id;
return TestModel::field($field)->where($where)->get();
}
/**
* 获取数据(包含扩展表)
*/
public static function data($id = 0, $dataId = 0, $userid = 0)
{
$where = array();
$userid && $where['userid'] = $userid;
$test = TestModel::where($where)->get($id);
$data = array();
if ($test) {
$DataModel = TestDataModel::sharding($test['id']);
try {
$DataModel = $DataModel->where('test_id', $test['id']);
$dataId && $DataModel = $DataModel->where('id', $dataId);
$data = $DataModel->get();
} catch (\sException $ex) {
// 处理表不存在等异常
}
}
return array($test, $data, []);
}
/**
* 创建记录
*/
public static function create($data)
{
return TestModel::create($data, true);
}
/**
* 更新记录
*/
public static function update($data, $where)
{
return TestModel::update($data, $where);
}
/**
* 删除记录
*/
public static function delete($id)
{
return TestModel::delete($id);
}
}
2. 创建 TestModel.php(主表模型)
参考:ArticleModel.php
/**
* Test 主表模型
*/
defined('iPHP') or exit('What are you doing?');
class TestModel extends Model
{
/**
* 状态映射
*/
public static $statusMap = [
'0' => '草稿',
'1' => '正常',
'2' => '回收站',
'3' => '待审核',
];
/**
* 字段类型转换
*/
protected $casts = [
'picdata' => 'array', // 图片数据
'nodeAttrs' => 'array', // 分类动态属性
'metadata' => 'array', // 元数据
];
/**
* 事件监听
*/
protected $events = [
'delete' => ['TestEvent', 'delete'], // 删除前
'changed' => ['TestEvent', 'changed'], // 创建或更新后
];
}
3. 创建 TestDataModel.php(扩展表模型)
参考:ArticleDataModel.php
/**
* Test 扩展数据表模型
*/
defined('iPHP') or exit('What are you doing?');
class TestDataModel extends Model
{
protected $casts = [];
/**
* 数据库错误回调
*/
protected $callback = [
'SQLSTATE:42S02' => [__CLASS__, 'createTable'], // 表不存在时自动创建
];
public static $appid = iCMS_APP_TEST;
/**
* 分表策略
*/
public static function sharding($test_id)
{
$model = self::getInstance();
$model->sharding = (int)$test_id % 10; // 按ID取模分10张表
return $model;
}
/**
* 自动创建表
*/
public static function createTable()
{
try {
$target = self::getTableName();
$source = self::table(__CLASS__);
DB::copy($source, $target);
return true;
} catch (sException $ex) {
$state = $ex->getState();
if ($state === '42000') { // 无创建表权限
throw $ex;
}
return false;
}
}
}
4. 创建 TestEvent.php(事件处理)
参考:ArticleEvent.php
/**
* Test 事件处理类
*/
defined('iPHP') or exit('What are you doing?');
class TestEvent extends DBEvent
{
public static $appid = iCMS_APP_TEST;
/**
* 创建或更新后的事件
*/
public static function changed($response, $Builder, $event, $isMulti)
{
// 获取受影响的ID列表
if ($event == 'updated') {
$idArr = $Builder->field('id')->pluck();
} else {
$idArr = [$response['id']];
}
array_map(function ($id) use ($response, $Builder, $event, $isMulti) {
if (!$id) {
return;
}
// 处理分类变化
if (isset($response['cid'])) {
Node::change('cid', $response['cid'], $event, $id, self::$appid);
}
// 处理副分类变化
if (isset($response['scid'])) {
AppsMap::change('scid', self::$appid, $response['scid'], $event, $id, 'Node');
}
// 处理标签变化
if (isset($response['tags'])) {
Tag::$APPID = self::$appid;
if (!isset($response['tags']['raw'])) {
Tag::change('tags', $response['tags'], $event, $id, self::$appid, $Builder);
}
}
// 处理缩略图变化
if (isset($response['pic'])) {
FilesPic::change($response, self::$appid, $event, $id);
}
// 内容归档
if ($event == 'updated') {
Archive::update(self::$appid, $id, $response);
} else {
Archive::save(self::$appid, $id, $response);
}
}, $idArr);
}
/**
* 删除前的事件
*/
public static function delete($response, $Builder, $event, $isMulti)
{
$idArr = $Builder->field('id')->pluck();
array_map(function ($id) {
if (!$id) {
return;
}
// 删除应用映射(分类、标签、属性)
AppsMap::delete(self::$appid, $id, 'Node');
AppsMap::delete(self::$appid, $id, 'Tag');
AppsMap::delete(self::$appid, $id, 'Prop');
// 删除关联文件
Files::delete(self::$appid, $id);
// 删除评论
Comment::delete(self::$appid, $id);
// 删除归档
Archive::delete(self::$appid, $id);
}, $idArr);
}
}
5. 创建 TestAdmincp.php(后台管理)
参考:ArticleAdmincp.php
/**
* Test 后台管理
*/
defined('iPHP') or exit('What are you doing?');
defined('APP_URL') or define('APP_URL', '/admincp.php/test');
class TestAdmincp extends AdmincpCommon
{
use AdmincpCommonTrait;
/**
* 排序字段映射
*/
public static $orderBy = [
'id' => 'ID',
'hits' => '点击',
'postime' => '时间',
];
/**
* 添加/编辑内容
*/
public function do_add()
{
$rs = [];
// 如果有ID则获取数据(编辑模式)
if ($this->id) {
$rs = Test::get($this->id);
}
// 获取分类
$rs['cid'] = $rs['cid'] ?: (int) Request::get('cid');
$Node = $rs['cid'] ? Node::get($rs['cid']) : [];
// 新增时的默认值
if (empty($this->id)) {
$rs['status'] = '1';
$rs['postype'] = '1';
$rs['editor'] = Admin::$nickname;
$rs['userid'] = Admin::$user_id;
}
include self::view();
}
/**
* 保存数据
*/
public function do_save()
{
// 获取提交的数据
$data = $this->data();
// 数据验证
empty($data['title']) && self::json(['code' => 0, 'msg' => '标题不能为空']);
empty($data['cid']) && self::json(['code' => 0, 'msg' => '请选择栏目']);
// 保存主表数据
if ($this->id) {
Test::update($data, ['id' => $this->id]);
} else {
$data['postime'] = time();
$this->id = Test::create($data);
}
// 保存扩展表数据(如果有)
if (isset($data['body'])) {
$dataModel = TestDataModel::sharding($this->id);
$extData = [
'test_id' => $this->id,
'body' => $data['body'],
];
$dataModel->replace($extData);
}
return [true, '保存成功'];
}
/**
* 删除
*/
public function do_delete()
{
$id = (int)Request::get('id');
$id or self::json(['code' => 0, 'msg' => 'ID不能为空']);
Test::delete($id);
// 删除扩展表数据
$dataModel = TestDataModel::sharding($id);
$dataModel->where('test_id', $id)->delete();
return [true,'删除成功'];
}
}
6. 创建 TestCategoryAdmincp.php(分类管理)
参考:ArticleCategoryAdmincp.php
/**
* Test 分类管理
*/
defined('iPHP') or exit('What are you doing?');
class TestCategoryAdmincp extends NodeAdmincp
{
public function __construct()
{
parent::__construct(iCMS_APP_TEST); // 传入应用ID
$this->app = 'test';
$this->title = '测试';
$this->primary = 'cid';
$this->CONTENT_MODEL = new TestModel();
$this->NODE_NAME = "栏目";
}
}
7. 创建 TestFunc.php(模板标签)
参考:ArticleFunc.php
/**
* Test 模板标签类
*/
defined('iPHP') or exit('What are you doing?');
class TestFunc extends AppsFuncCommon implements AppsFuncInterface
{
/**
* 获取列表
*/
public static function lists($vars)
{
$resource = array();
$whereNot = array();
$model = TestModel::field('id');
$status = isset($vars['status']) ? $vars['status'] : 1;
$where = [['status', $status]];
// 条件筛选
isset($vars['userid']) && $where[] = ['userid', $vars['userid']];
isset($vars['pic']) && $where[] = ['haspic', '1'];
// 使用基类方法
self::inited($vars, $model, $where, $whereNot);
self::nodes('cid'); // 处理分类
self::tags(); // 处理标签
self::keywords(); // 处理关键词
// 排序映射
self::orderby([
'hot' => 'hits',
'week' => 'hits_week',
'month' => 'hits_month'
]);
self::where();
return self::getResource(__METHOD__);
}
/**
* 获取资源详情(由 getResource 自动调用)
*/
public static function resource($idsArray = null)
{
$vars = self::$vars;
$resource = TestModel::field('*')
->where($idsArray)
->orderBy('id', $idsArray)
->select();
$resource = self::many($vars, $resource);
return $resource;
}
/**
* 获取单条数据
*/
public static function data($vars)
{
$id = $vars['id'] ?: $vars['test_id'];
$id or self::msg(['func_param', ['iCMS:test:data', 'id']]);
$where = ['id' => $id];
$model = TestModel::where($where);
$hash = md5($model->getSql());
self::paging($hash, __METHOD__);
$resource = self::getCache(self::$cacheName);
if (empty($resource)) {
$resource = $model->field('*')->get();
$cacheTime = isset($vars['time']) ? (int) $vars['time'] : -1;
$vars['cache'] && Cache::set(self::$cacheName, $resource, $cacheTime);
}
return $resource;
}
}
第四步:创建视图模板
1. 创建 views/index.html(列表页)
defined('iPHP') or exit('What are you doing?');
self::head();
?>
搜索
栏目
name="cid" value="">
关键词
value="" />
搜索
</span></span> </form><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <!-- 数据表格 --><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="block"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="block-content p-0"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div id="toolbar" class="toolbar row"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="input-group input-group-sm"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <a href="{{test/add}}" class="btn btn-alt-primary"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <i class="fa fa-add"></i> 添加<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </a><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <a href="{{test/edit}}" id="edit" class="btn btn-alt-primary table-btn-edit disabled" disabled><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <i class="fa fa-edit"></i> 编辑<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </a><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <a href="{{test/delete}}" id="delete" class="btn btn-alt-primary table-btn-delete disabled" disabled><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <i class="fa fa-trash"></i> 删除<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </a><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <?php AdmincpBatch::group(); ?><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="table-responsive"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <table id="table" width="100%"></table><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span><?php self::foot(); ?></span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">2. 创建 views/add.html(添加/编辑页)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">php</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text"><?php<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>defined('iPHP') or exit('What are you doing?');<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>self::head();<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>?><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span><div class="content" id="{{APP_MAINID}}"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="block"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="block-content"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <form action="{{APP_DOURL}}" method="POST" i="cms:ui:form"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <input name="rs[id]" type="hidden" value="<?php echo $this->id; ?>" /><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <input name="REFERER" type="hidden" value="<?php echo iPHP_REFERER; ?>" /><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="row"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="col-sm-9 border-end"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <!-- 标题 --><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="form-floating mb-3"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <input type="text" name="rs[title]" class="form-control form-control-lg" <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> id="title" value="<?php echo $rs['title']; ?>" data-rule="required" /><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <label for="title">标题</label><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <!-- 内容 --><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="form-group mb-3"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <label>内容</label><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <textarea name="rs[body]" class="form-control" <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> i="cms:ui:editor" rows="20"><?php echo $rs['body']; ?></textarea><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <!-- 提交按钮 --><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="form-group"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <button class="btn btn-primary" type="submit"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <i class="fa fa-check"></i> 提交<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </button><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="col-sm-3"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <!-- 分类 --><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="form-floating mb-3"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <input type="text" name="rs[cid]" class="form-control" <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> id="cid" i="cms:ui:node" <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> value="<?php echo $rs['cid']; ?>" data-rule="required" /><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <label for="cid">栏目</label><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <!-- 缩略图 --><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="form-floating mb-3"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <input type="text" name="rs[pic]" class="form-control" <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> id="pic" i="cms:ui:pic" <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> value="<?php echo $rs['pic']; ?>" /><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <label for="pic">缩略图</label><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <!-- 标签 --><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="form-floating mb-3"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <input type="text" name="rs[tags]" class="form-control" <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> id="tags" i="cms:ui:tags" <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> value="<?php echo $rs['tags']; ?>" /><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <label for="tags">标签</label><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <!-- 状态 --><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <div class="form-floating mb-3"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <select name="rs[status]" class="form-control" id="status" <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> i="cms:ui:bs-selectpicker" data-source="statusMap"><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <option value="<?php echo $rs['status']; ?>" selected></option><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </select><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <label for="status">状态</label><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </form><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> </div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span><?php self::foot(); ?></span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">3. 创建 views/edit.html(编辑页)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">php</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text"><?php include self::view('add'); ?></span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">第五步:创建配置文件</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">1. 创建 etc/menu/admincp.json(后台菜单)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">json</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">[{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "id": "test",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "sort": "10",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "caption": "测试应用",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "icon": "si si-grid",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "children": [<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "caption": "添加内容",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "href": "/test/add",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "icon": "edit",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "sort": 101,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "access": "/test/add",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "id": "menu_test_add"<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "caption": "栏目管理",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "href": "/testCategory/tree",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "icon": "sitemap",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "sort": 102,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "access": "/testCategory/tree",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "id": "menu_test_category"<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "caption": "内容管理",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "href": "/test/index",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "icon": "list",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "sort": 103,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "access": "/test/index",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "id": "menu_test_index"<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ],<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "access": "test",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "expanded": false<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}]</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">2. 创建 etc/admincp/Test.index.batch.json(批量操作)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">json</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "status=1": {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "name": "发布",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "icon": "check",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "sort": 1,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "call": "default"<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "status=0": {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "name": "转为草稿",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "icon": "inbox",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "sort": 2,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "call": "default"<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "status=2": {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "name": "移入回收站",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "icon": "trash-alt",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "sort": 3,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "call": "default"<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "move": {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "name": "移动栏目",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "icon": "fighter-jet",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "sort": 10,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "call": "move"<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "dels": {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "name": "删除",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "icon": "trash-alt",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "sort": 20,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "call": "dels"<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">3. 创建 etc/route/node.json(分类路由配置)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">json</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "test": {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "label": "test",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "template": "{iTPL}/test.htm",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "rule": "/{CDIR}/{YYYY}/{MM}{DD}/{ID}{EXT}",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "tips": "{ID},{0xID},{LINK},{HASH@ID},{HASH@0xID}"<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">第六步:创建 JavaScript 文件</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">assets/js/index.js(列表页脚本)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">javascript</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">define(["admincp", "table"], function (admincp, Table) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> const factory ={<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> user: function () {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return factory.index()<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> index: function () {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> var $table = $table || $('#table');<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> Table.init($table, {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> app: $APP.name,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> urls: {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "index": window.location.href,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // "add": "article/add",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // "edit": "article/edit",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // "detail": "article/detail",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // "publish": "article/update",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // "trash": "article/trash",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // "import": "article/import",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // "delete": "article/delete"<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // search:true,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> columns: [{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> field: 'id',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> title: 'fields:id',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> width: "90",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> sortable: true<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> field: 'cid',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> title: 'fields:nodeName',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> setting: { map: 'node/mapper' },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> search: "=",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> formatter: Table.format.search,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> field: 'title',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> title: 'fields:title',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> class: "text-nowrap w-25",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> formatter: function (value, row, index) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> var html = [Table.format.link.apply(this, arguments)];<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> if (row['postype'] == 0) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> html.push('<i class="fa fa-fw fa-user text-primary-light"></i>');<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> if (row['pic']) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> html.push('<i class="fa fa-fw fa-image text-success"></i>');<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> if (row['title_extra']) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> html.push(`<br /><span class="py-1 px-3 rounded-pill bg-warning-light text-warning">${row['title_extra']}</span>`);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // console.log(html);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return html.join(' ');<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> field: 'pid',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> title: 'fields:propName',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> setting: { map: "prop/mapper" },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> formatter: Table.format.flag,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> field: 'pubdate',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> title: 'fields:pubdate',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // formatter: Table.format.datetime,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> formatter: function (value, row, index) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> var format = 'YYYY-MM-DD HH:mm';<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return (row['pubdate'] > 0 ? iCMS.utils.moment.unix(row['pubdate']).format(format) : '-') +<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> '<br />' +<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> (row['postime'] > 0 ? iCMS.utils.moment.unix(row['postime']).format(format) : '-')<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> field: 'hits',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> title: 'fields:hits_text',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> formatter: function (value, row, index) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> var s = {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> href: "javascript:;",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> text: value,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> format: '<a class="btn btn-sm btn-light" href="{{href}}" title="{{title}}" {{attr}}>{{text}}</a>',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> s.attr = [<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'i="cms:ui:popover"',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'data-html="true"',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'data-placement="top"',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'data-content="' + [<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> '总点击数: ' + row.hits,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> '当天点击数: ' + row.hits_today,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> '昨天点击数: ' + row.hits_yday,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> '周点击数: ' + row.hits_week,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> '月点击数: ' + row.hits_month,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> '---------------',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> '赞: ' + row.good,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> '踩: ' + row.bad,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> '评论数: ' + row.comment,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> '收藏数: ' + row.favorite,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ].join('<br/>') + '"'<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ].join(" ");<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return iCMS.utils.display(s.format, s);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> field: 'status',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> title: 'fields:status',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> formatter: Table.format.status,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }, {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> field: 'operate',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> buttons: [{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> title: Lang('subApp'),<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> id: 'dropdown-default-primary',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> class: 'btn btn-alt-primary dropdown-toggle',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> icon: 'fa fa-fw fa-bars',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> attr: {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "data-bs-toggle": "dropdown",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "aria-haspopup": "true",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "aria-expanded": "false",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> visible: function(){<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> if(isArray($APP.context.subApp) && $APP.context.subApp.length > 0){<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return true;<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return false;<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> dropdown: $APP.context.subApp<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // dropdown: [<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // 'href': './dfg',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // 'text': 'fgh',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // 'class': 'btn-modal'<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // 'href': './dfg',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // 'text': 'fgh',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // 'class': 'btn-modal'<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // ]<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }],<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> formatter: Table.format.operate,<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ]<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> });<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // $("#test").on("click",function (e) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // e.preventDefault();<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // var row = Table.method.refresh({<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // 'sortName': 'id',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // 'sortOrder': 'asc'<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // });<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // // alert(JSON.stringify(row));<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // // var that = this;<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // // var href = $(this).attr("href");<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // // console.log(href);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // // $.each(Table.ids, function (idx, value) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // // var url = iCMS.api.makeUrl(href, { id: value });<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // // console.log(url);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // // iCMS.ui.modal(that, { url });<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // // });<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // // alert(this.href)<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // });<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return factory;<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>});</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">assets/js/add.js(添加页脚本)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">javascript</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">define(["iCMS"], function (iCMS) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> const factory = {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> add_success: function (json) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> console.log('保存成功', json);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> };<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return factory;<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>});</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">assets/js/edit.js(编辑页脚本)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">javascript</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">define(["./add"], function (add) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return add;<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>});</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">第七步:清除缓存并测试</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">1. </span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">清除缓存</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="padding"> </span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="padding"> </span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">进入后台:</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">系统管理</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text"> → </span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">缓存管理</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="padding"> </span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">点击:</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">清除应用缓存</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="padding"> </span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">点击:</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">更新菜单缓存</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">2. </span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">访问测试</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="padding"> </span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="padding"> </span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">后台列表:</span><span class="vditor-sv__marker">`</span><span>admincp.php/test/index</span><span class="vditor-sv__marker">`</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="padding"> </span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">后台添加:</span><span class="vditor-sv__marker">`</span><span>admincp.php/test/add</span><span class="vditor-sv__marker">`</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="padding"> </span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">分类管理:</span><span class="vditor-sv__marker">`</span><span>admincp.php/testCategory/tree</span><span class="vditor-sv__marker">`</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="padding"> </span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">前台访问:</span><span class="vditor-sv__marker">`</span><span>api.php/test/index</span><span class="vditor-sv__marker">`</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">第八步:常见问题处理</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">问题1:菜单不显示</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">检查 </span><span class="vditor-sv__marker">`</span><span>etc/menu/admincp.json</span><span class="vditor-sv__marker">`</span><span data-type="text"> 格式是否正确</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">清除菜单缓存</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">检查 </span><span class="vditor-sv__marker">`</span><span>caption</span><span class="vditor-sv__marker">`</span><span data-type="text">(不是 </span><span class="vditor-sv__marker">`</span><span>name</span><span class="vditor-sv__marker">`</span><span data-type="text">),</span><span class="vditor-sv__marker">`</span><span>href</span><span class="vditor-sv__marker">`</span><span data-type="text">(不是 </span><span class="vditor-sv__marker">`</span><span>url</span><span class="vditor-sv__marker">`</span><span data-type="text">)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">问题2:列表页空白</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">检查 </span><span class="vditor-sv__marker">`</span><span>assets/js/index.js</span><span class="vditor-sv__marker">`</span><span data-type="text"> 是否存在</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">浏览器控制台查看 JS 错误</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">确认 </span><span class="vditor-sv__marker">`</span><span><table id="table"></table></span><span class="vditor-sv__marker">`</span><span data-type="text"> 存在</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">问题3:保存失败</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">检查 </span><span class="vditor-sv__marker">`</span><span>do_save()</span><span class="vditor-sv__marker">`</span><span data-type="text"> 方法的返回格式:</span><span class="vditor-sv__marker">`</span><span>return [true, '消息'];</span><span class="vditor-sv__marker">`</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">检查表单 </span><span class="vditor-sv__marker">`</span><span>name="rs[字段名]"</span><span class="vditor-sv__marker">`</span><span data-type="text"> 格式</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">检查 Model 的 </span><span class="vditor-sv__marker">`</span><span>$events</span><span class="vditor-sv__marker">`</span><span data-type="text"> 事件处理</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">问题4:事件不触发</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">检查 Model 的 </span><span class="vditor-sv__marker">`</span><span>$events</span><span class="vditor-sv__marker">`</span><span data-type="text"> 数组是否定义</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">检查 Event 类的方法签名是否正确</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">检查 Event 类是否继承 </span><span class="vditor-sv__marker">`</span><span>DBEvent</span><span class="vditor-sv__marker">`</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker">---</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h2" data-type="heading-marker">## </span><span data-type="text" class="h2">最佳实践</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">1. 代码规范</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">遵循 PSR-4 自动加载规范</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">类名使用 PascalCase(首字母大写驼峰)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">方法名使用 camelCase(小驼峰)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">常量使用 UPPER_CASE(全大写下划线)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">2. 安全建议</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">所有用户输入必须验证和过滤</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">使用参数化查询防止 SQL 注入</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">对敏感操作进行权限验证</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">XSS 防护:输出时进行 HTML 转义</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">3. 性能优化</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">合理使用缓存</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">避免 N+1 查询问题</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">分表存储大字段内容</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">使用索引优化查询</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">4. 可维护性</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">代码注释清晰</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">功能模块化</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">遵循单一职责原则</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">使用事件解耦业务逻辑</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker">---</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h2" data-type="heading-marker">## </span><span data-type="text" class="h2">常见问题</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">Q1: 如何在模板中调用应用数据?</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">A</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">: 使用模板标签,在 </span><span class="vditor-sv__marker">`</span><span>TestFunc.php</span><span class="vditor-sv__marker">`</span><span data-type="text"> 中定义方法:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">php</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">public static function list($vars) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return TestModel::all();<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="text">模板中调用:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">html</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text"><!--{iCMS:test:list loop="true"}--><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {{$test_list.title}}<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span><!--{/iCMS}--></span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">Q2: 如何自定义 URL 路由?</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">A</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">: 在 </span><span class="vditor-sv__marker">`</span><span>etc/route/route.json</span><span class="vditor-sv__marker">`</span><span data-type="text"> 中配置:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">json</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "/test/{id}":"api.php/test/detail?id={id}"<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">Q3: 如何添加应用配置项?</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">A</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">1. </span><span data-type="text">在 </span><span class="vditor-sv__marker">`</span><span>views/app/config/Test.html</span><span class="vditor-sv__marker">`</span><span data-type="text"> 创建配置表单</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">2. </span><span data-type="text">使用 </span><span class="vditor-sv__marker">`</span><span>Test::config('key')</span><span class="vditor-sv__marker">`</span><span data-type="text"> 获取配置值</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">Q4: 如何实现数据关联?</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">A</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">: 在 Model 中定义关联关系:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">php</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">class TestModel extends Model<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public function category()<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return $this->belongsTo(CategoryModel::class, 'cid');<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public function user()<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return $this->belongsTo(UserModel::class, 'uid');<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">Q5: 如何处理文件上传?</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">A</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">: 使用系统的文件上传组件:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">php</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">public function do_upload()<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> $file = Files::upload('image', [<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'dir' => 'test',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'ext' => 'jpg,png,gif',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'size' => 2 * 1024 * 1024 // 2MB<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ]);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return ['url' => $file['url']];<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker">---</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h2" data-type="heading-marker">## </span><span data-type="text" class="h2">附录</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">常用方法参考</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">AdmincpBase 类方法</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>view($template, $data)</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 渲染视图</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>success($msg, $url)</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 成功提示</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>error($msg, $url)</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 错误提示</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>json($data)</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 返回 JSON</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">Model 类方法</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>all()</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 获取所有记录</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>find($id)</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 根据 ID 查找</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>where($field, $value)</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 条件查询</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>create($data)</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 创建记录</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>update($data)</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 更新记录</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>delete()</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 删除记录</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>paginate($perPage)</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 分页</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">Request 类方法</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>Request::get($key)</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 获取 GET 参数</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>Request::post($key)</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 获取 POST 参数</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>Request::input($key)</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 获取请求参数</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>Request::isPost()</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 判断是否 POST 请求</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>Request::ip()</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 获取客户端 IP</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker">---</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h2" data-type="heading-marker">## </span><span data-type="text" class="h2">Article 应用完整分析</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="text">为了更好地理解 iCMS v8 的应用开发,下面我们分析 </span><span class="vditor-sv__marker">`</span><span>app/article/</span><span class="vditor-sv__marker">`</span><span data-type="text"> 文章应用的实际结构:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">文件列表</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info"></span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">app/article/<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── Article.php # 主类<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── ArticleModel.php # 主表模型<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── ArticleDataModel.php # 数据表模型(分表)<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── ArticleAdmincp.php # 后台管理<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── ArticleApp.php # 前台应用<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── ArticleUserApp.php # 用户中心<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── ArticleCategoryAdmincp.php # 分类管理<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── ArticleEvent.php # 事件处理<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── ArticleFunc.php # 模板标签<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── ArticleFuncSearch.php # 搜索标签<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── ArticleHook.php # 系统钩子<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── ArticleWidgetAdmincp.php # 后台小部件<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── ArticleShellApp.php # Shell 命令行应用<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── assets/ # 静态资源<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ └── js/<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ ├── add.js<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ ├── edit.js<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ └── index.js<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>├── etc/ # 配置文件<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ ├── admincp/<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ │ ├── Article.index.batch.json<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ │ ├── home.quick.json<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ │ └── home.stats.json<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ ├── menu/<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ │ ├── admincp.json<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ │ ├── admincp.cache.json<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ │ └── usercp.content.json<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ └── route/<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ ├── node.json<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>│ └── route.ArticleUser.json<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>└── views/ # 视图模板<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ├── add.html<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ├── edit.html<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ├── index.html<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ├── batch.html<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> └── app/<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ├── config/<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> │ ├── article.html<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> │ └── article.sphinx.html<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> └── node/<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> └── add.html</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">核心类分析</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">1. Article.php - 应用主类</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">关键代码</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">php</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">class Article<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> const APP = 'article';<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> const APPID = iCMS_APP_ARTICLE;<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static $stypeMap = array(<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'inbox' => '0',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'normal' => '1',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'trash' => '2',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'examine' => '3',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // ...<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> );<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static function check($value, $id = 0, $field = 'title') { }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static function value($field = 'id', $id = 0) { }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static function get($id = 0, $field = '*', $where = array()) { }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static function data($id = 0, $dataId = 0, $userid = 0) { }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static function create($data) { }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static function update($data, $where) { }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static function delete($id) { }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">特点</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">定义应用常量和状态映射</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">提供静态工具方法</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">封装 Model 操作</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">2. ArticleModel.php - 主表模型</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">关键代码</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">php</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">class ArticleModel extends Model<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static $statusMap = [<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> '0' => '草稿',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> '1' => '正常',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // ...<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ];<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> protected $casts = [<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'picdata' => 'array',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'nodeAttrs' => 'array',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ];<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> protected $events = [<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'delete' => ['ArticleEvent', 'delete'],<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'changed' => ['ArticleEvent', 'changed'],<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ];<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">特点</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">使用 </span><span class="vditor-sv__marker">`</span><span>$casts</span><span class="vditor-sv__marker">`</span><span data-type="text"> 自动转换 JSON 为数组</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">使用 </span><span class="vditor-sv__marker">`</span><span>$events</span><span class="vditor-sv__marker">`</span><span data-type="text"> 绑定事件到 ArticleEvent</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">定义状态映射数组</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">3. ArticleAdmincp.php - 后台管理</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">关键代码</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">php</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">class ArticleAdmincp extends AdmincpCommon<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> use AdmincpCommonTrait;<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static $orderBy = [<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'id' => 'ID',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'hits' => '点击',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'postime' => '时间',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ];<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public function do_add() {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // 添加/编辑页面<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> include self::view();<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public function do_save() {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // 保存数据<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return [true, '保存成功'];<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">特点</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">继承 </span><span class="vditor-sv__marker">`</span><span>AdmincpCommon</span><span class="vditor-sv__marker">`</span><span data-type="text"> + </span><span class="vditor-sv__marker">`</span><span>AdmincpCommonTrait</span><span class="vditor-sv__marker">`</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">使用 </span><span class="vditor-sv__marker">`</span><span>do_xxx()</span><span class="vditor-sv__marker">`</span><span data-type="text"> 方法</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">使用 </span><span class="vditor-sv__marker">`</span><span>include self::view()</span><span class="vditor-sv__marker">`</span><span data-type="text"> 加载视图</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">4. ArticleEvent.php - 事件处理</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">关键代码</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">php</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">class ArticleEvent extends DBEvent<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static $appid = iCMS_APP_ARTICLE;<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static function changed($response, $Builder, $event, $isMulti)<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // 处理分类、标签、属性、文件等变化<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> if (isset($response['cid'])) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> Node::change('cid', $response['cid'], $event, $id, self::$appid);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> if (isset($response['tags'])) {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> Tag::change('tags', $response['tags'], $event, $id, self::$appid, $Builder);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // ...<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static function delete($response, $Builder, $event, $isMulti)<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // 删除关联数据<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> AppsMap::delete(self::$appid, $id, 'Node');<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> Files::delete(self::$appid, $id);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> // ...<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">特点</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">固定的方法签名</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">处理分类、标签、文件等关联</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">使用系统提供的工具类</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">5. ArticleFunc.php - 模板标签</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">关键代码</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">php</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">class ArticleFunc extends AppsFuncCommon implements AppsFuncInterface<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static function lists($vars)<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> $model = ArticleModel::field('id');<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> $where = [['status', 1]];<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> self::inited($vars, $model, $where, $whereNot);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> self::nodes('cid');<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> self::tags();<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> self::orderby([<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'hot' => 'hits',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> 'week' => 'hits_week',<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ]);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> self::where();<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return self::getResource(__METHOD__);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public static function resource($idsArray = null)<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> $resource = ArticleModel::field('*')<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ->where($idsArray)<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ->orderBy('id', $idsArray)<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> ->select();<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> return self::many($vars, $resource);<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">特点</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">使用基类的辅助方法(nodes、tags、orderby)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>lists()</span><span class="vditor-sv__marker">`</span><span data-type="text"> 方法配合 </span><span class="vditor-sv__marker">`</span><span>resource()</span><span class="vditor-sv__marker">`</span><span data-type="text"> 方法</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">返回资源数组供模板使用</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">关键配置文件</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">etc/menu/admincp.json</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">json</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">[{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "id": "article",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "caption": "文章",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "icon": "si si-grid",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "href": "/article/index",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "children": [...]<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}]</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">etc/admincp/Article.index.batch.json</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">json</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "status=1": {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "name": "发布",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "icon": "check",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "call": "default"<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> },<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "move": {<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "name": "移动栏目",<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> "call": "move"<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">数据表设计</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">icms_article(主表)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">id, cid, uid, title, description, tags, status, hits, postime, pubdate, etc.</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h4" data-type="heading-marker">#### </span><span data-type="text" class="h4">icms_article_data_0 ~ icms_article_data_9(分表)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">id, article_id, subtitle, body</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker">---</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h2" data-type="heading-marker">## </span><span data-type="text" class="h2">开发建议</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">1. 从 Article 应用学习</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="text">Article 应用是最完整的参考示例,包含了:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">✅ 完整的 CRUD 操作</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">✅ 分类、标签、属性管理</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">✅ 用户投稿功能</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">✅ 事件处理机制</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">✅ 数据分表策略</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span data-type="text">✅ 模板标签系统</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">2. 复制改造法</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="text">最快的开发方式:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">1. </span><span data-type="text">复制整个 </span><span class="vditor-sv__marker">`</span><span>article</span><span class="vditor-sv__marker">`</span><span data-type="text"> 目录为 </span><span class="vditor-sv__marker">`</span><span>yourapp</span><span class="vditor-sv__marker">`</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">2. </span><span data-type="text">批量替换所有 </span><span class="vditor-sv__marker">`</span><span>Article</span><span class="vditor-sv__marker">`</span><span data-type="text"> 为 </span><span class="vditor-sv__marker">`</span><span>Yourapp</span><span class="vditor-sv__marker">`</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">3. </span><span data-type="text">批量替换所有 </span><span class="vditor-sv__marker">`</span><span>article</span><span class="vditor-sv__marker">`</span><span data-type="text"> 为 </span><span class="vditor-sv__marker">`</span><span>yourapp</span><span class="vditor-sv__marker">`</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">4. </span><span data-type="text">修改配置文件中的菜单、路由等</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">5. </span><span data-type="text">根据需求调整字段和逻辑</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">3. 必须定义的文件</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">最小化应用</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">至少需要:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>Yourapp.php</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 主类(定义 APP 和 APPID 常量)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>YourappModel.php</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 数据模型</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>YourappAdmincp.php</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 后台管理</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>etc/menu/admincp.json</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 后台菜单</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>views/add.html</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 添加页面</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>views/index.html</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 列表页面</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">完整应用</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">还应包括:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>YourappApp.php</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 前台应用</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>YourappFunc.php</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 模板标签</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>YourappEvent.php</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 事件处理</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>YourappUserApp.php</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 用户中心(如需用户投稿)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">- </span><span class="vditor-sv__marker">`</span><span>YourappCategoryAdmincp.php</span><span class="vditor-sv__marker">`</span><span data-type="text"> - 分类管理(如需分类)</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h3" data-type="heading-marker">### </span><span data-type="text" class="h3">4. 常见陷阱</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="text">❌ </span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">错误</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">php</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">class TestAdmincp extends AdmincpBase // 错误的基类<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public function index() { } // 缺少 do_ 前缀<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="text">✅ </span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">正确</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="code-block-open-marker" class="vditor-sv__marker">```</span><span class="vditor-sv__marker--info" data-type="code-block-info">php</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="text">class TestAdmincp extends AdmincpCommon<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>{<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> use AdmincpCommonTrait;<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> <span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span> public function do_index() { }<span data-type="padding"></span><span data-type="newline"><br /><span style="display: none">
</span></span>}</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="code-block-close-marker" class="vditor-sv__marker">```</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker">---</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--heading h2" data-type="heading-marker">## </span><span data-type="text" class="h2">总结</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="text">iCMS v8 提供了完整而灵活的应用开发框架,遵循 MVC 设计模式,通过规范的目录结构和命名约定,让开发者能够快速构建功能强大的应用。</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">核心要点</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">1. </span><span data-type="text">✅ </span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">参考 Article</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:以 article 应用为标准参考</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">2. </span><span data-type="text">✅ </span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">继承关系</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:严格遵循正确的类继承关系</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">3. </span><span data-type="text">✅ </span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">方法前缀</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:Admincp 用 </span><span class="vditor-sv__marker">`</span><span>do_</span><span class="vditor-sv__marker">`</span><span data-type="text">,UserApp 用 </span><span class="vditor-sv__marker">`</span><span>api_/done_</span><span class="vditor-sv__marker">`</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">4. </span><span data-type="text">✅ </span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">事件系统</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:使用 </span><span class="vditor-sv__marker">`</span><span>$events</span><span class="vditor-sv__marker">`</span><span data-type="text"> 数组 + 固定方法签名</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">5. </span><span data-type="text">✅ </span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">配置格式</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:参考实际 JSON 配置文件格式</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">6. </span><span data-type="text">✅ </span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">模板标签</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:继承 AppsFuncCommon,使用辅助方法</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">7. </span><span data-type="text">✅ </span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">分表策略</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:大数据量使用 sharding 方法</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">8. </span><span data-type="text">✅ </span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">安全验证</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:数据验证、权限控制、防注入</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span class="vditor-sv__marker--bi strong">**</span><span data-type="text" class="strong">推荐开发流程</span><span class="vditor-sv__marker--bi strong">**</span><span data-type="text">:</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="li-marker" class="vditor-sv__marker">1. </span><span data-type="text">复制 article 应用为模板</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">2. </span><span data-type="text">批量替换类名和文件名</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">3. </span><span data-type="text">修改数据表结构</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">4. </span><span data-type="text">调整业务逻辑</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">5. </span><span data-type="text">测试功能</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="li-marker" class="vditor-sv__marker">6. </span><span data-type="text">清除缓存</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="text">祝您开发顺利!如有问题,请参考 </span><span class="vditor-sv__marker">`</span><span>app/article/</span><span class="vditor-sv__marker">`</span><span data-type="text"> 目录中的实际代码。</span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div><div data-block="0"><span data-type="text"><wbr></span><span data-type="newline"><br /><span style="display: none">
</span></span><span data-type="newline"><br /><span style="display: none">
</span></span></div>