TFWEB开发标准模块的操作指南,通过 TFWEB 框架开发一个标准的功能页面,页面包括基本的增、删、改、查功能
模块名称(必选):表示模块的唯一名称,示例:user
数据表(必须):用于存储模块数据的物理数据表,示例:基本信息表 user,详细信息表 user_detail
user
CREATE TABLE user ( userId INT NOT NULL AUTO_INCREMENT, userName VARCHAR(45) NOT NULL, userPass CHAR(32) NOT NULL DEFAULT '', regDT DATETIME NULL, logDT DATETIME NULL, logTimes INT NOT NULL DEFAULT 0, PRIMARY KEY (userId) );
user_detail
CREATE TABLE user_detail ( userId INT NOT NULL DEFAULT 0, realName VARCHAR(45) NOT NULL DEFAULT '', introduce TEXT NOT NULL DEFAULT '', PRIMARY KEY (userId) );
用于控制网页指向的路由器
WEB-INF/Classes/Controllers/Route/PC.inc.php
用于控制API接口指向的路由器
WEB-INF/Classes/Controllers/Route/API.inc.php
设置公开认证模式
WEB-INF/Classes/Controllers/Auth/public.inc.php
对象模型依据一张数据表一个对应模型的基本原则设计,一个数据表必须对应着一个对象模型,模型完成对数据表中的数据的读写操作
示例:包括基本信息对象 TFModel_user,详细信息对象 TFModel_userDetail
对象API用于连接对象模型对外提供资源数据的存取功能
对象API遵循RESTFul规范设计
接口格式
用于响应常规接口调用,返回数据格式如下
{ "status":"状态信息", "message":"内容信息" }
表单处理程序格式
仅用于框架表单处理使用,返回数据格式如下
{ "action":"错误信息", "errmsg":"报错表单提示信息", "errname":"报错表单元素名称" }
示例:提供一个 user 资源
对象视图用于展示模块的用户操作界面功能,通过内建 JS 组件实现功能界面,包括完整的数据表格功能,操作表单功能
内建 JS 组件依赖 VueJS 实现
示例:实现一个 user 功能页面,包括基本的增、删、改、查功能
对象模板是对象视图的载体,用于将对象视图的用户操作界面展示到用户面前
对象模板基于 Smarty 框架实现
示例:完成一个 user 功能模板,挂载 user 视图
对象模型
WEB-INF/Classes/Models/user.inc.php
读取数据接口
WEB-INF/Classes/Views/api/user.inc.php
添加数据接口
WEB-INF/Classes/Views/api/user/actionAdd.inc.php
修改数据接口
WEB-INF/Classes/Views/api/user/actionMod.inc.php
删除数据接口
WEB-INF/Classes/Views/api/user/actionDel.inc.php
添加表单处理程序
WEB-INF/Classes/Views/api/user/action_add.inc.php
修改表单处理程序
WEB-INF/Classes/Views/api/user/action_mod.inc.php
删除表单处理程序
WEB-INF/Classes/Views/api/user/action_del.inc.php
用户界面程序
WEB-INF/Classes/Views/user.inc.php
用户界面脚本
js/pages/user.js
用户界面样式表
css/pages/user.css
用户界面模板
WEB-INF/Templates/Tpl/user.html
WEB-INF/Classes/Controllers/Route/PC.inc.php
class PCEntry extends TFSystemHtml{ public function __construct($opts){ // init parent::__construct($opts, "index", "index"); // meta $this->PEO->setItem('author', (isset($opts['author'])) ? $opts['author'] : '作者信息', 'Metas'); $this->PEO->setItem('keywords', (isset($opts['keywords'])) ? $opts['keywords'] : '关键词,关键词2,关键词3...', 'Metas'); $this->PEO->setItem('description', (isset($opts['description'])) ? $opts['description'] : '网页介绍', 'Metas'); // js & css $this->PEO->appendItem('Jses', 'extends@js/jquery/2.1.1/jquery-2.1.1.min'); $this->PEO->appendItem('Jses', 'extends@js/vue/2.5.16/vue-2.5.16.dev'); $this->PEO->appendItem('Jses', 'tfjs'); $this->PEO->appendItem('Jses', 'plugins/UI.Vue'); $this->PEO->appendItem('Jses', 'pc'); $this->PEO->appendItem('Csses', 'pc'); // system $this->PEO->initLog(); $this->PEO->initTFCache(); // set rest default ( temp ) $restClientObj = new TFRESTClient($this->PEO, TFConfig::getV('restInternalRoot', 'UriPrefix')); $this->PEO->setItem('rest', $restClientObj); // set user envTFJS $this->PEO->items['envTFJS']['env']['imgRoot'] = TFConfig::getV("imgRoot", "UriPrefix"); $this->smartyObj->setForceCompile(true); } protected function prepareOP(){ $myPageLoaderObj = new TFPageLoader($this->PEO, $this->smartyObj); $myPageLoaderObj->load(); $this->PEO->title = $this->PEO->title . " - 同福网 - TONGFU.net"; } }
WEB-INF/Classes/Controllers/Route/API.inc.php
class APIEntry extends TFSystemRest{ public function __construct($opts){ // init parent::__construct($opts); // system $this->PEO->initLog(); $this->PEO->initTFDO(); $this->PEO->initTFCache(); // set rest default ( temp ) $restClientObj = new TFRESTClient($this->PEO, TFConfig::getV('restInternalRoot', 'UriPrefix')); $this->PEO->setItem('rest', $restClientObj); // set user envTFJS $this->PEO->items['envTFJS']['env']['imgRoot'] = TFConfig::getV("imgRoot", "UriPrefix"); } protected function prepareData(){ $myRESTLoaderObj = new TFRESTLoader($this->PEO); $myRESTLoaderObj->auth( // enable auth true, // rules array( // default auth method 'default' => "public", // no default models 'noDefaultModels' => array( ), // user auth methods 'matches' => array( ), // no auth models 'noAuthModels' => array( ), ) ); $myRESTLoaderObj->load(); return $myRESTLoaderObj->getData(); } }
WEB-INF/Classes/Controllers/Auth/public.inc.php
class TFAuth_public extends TFAuthorize{ protected function prepare($opts){ $authData = array(); // public return $this->makeData("AUTHORIZED", "success", $authData); } }
WEB-INF/Classes/Models/user.inc.php
class TFModel_user extends TFAction{ public function __construct($PEO){ parent::__construct($PEO, "#user#", // 数据表名称,表名称前后的#用于默认的表名称前缀和表名称后缀 "userId" // 数据表主见字段名称,关联主键通过数组传入 ); $this->setCacheName("user" // 数据缓存名称 ); $this->setCacheMode(TFActionCacheMode::T_MODE_SINGLE); $this->setOpt('UKName', "userName"); // 设置字段userName是约束 } } class TFModel_userDetail extends TFAction{ public function __construct($PEO){ parent::__construct($PEO, "#user_detail#", // 数据表名称,表名称前后的#用于默认的表名称前缀和表名称后缀 "userId", // 数据表主键字段名称,关联主键通过数组传入 TFActionPKType::T_PK_UNI // 主键类型使用UNIQUE ); $this->setCacheName("user" // 数据缓存名称 ); $this->setCacheMode(TFActionCacheMode::T_MODE_SINGLE); } }
WEB-INF/Classes/Views/api/user.inc.php
class TFResource_user extends TFResource{ protected function prepareTableData($opts){ $pageSize = 10; $pageNumber = 1; $sql = $this->model->getDBO()->GS("SELECT u.userId, u.userName, ud.realName, u.regDT, u.logDT, u.logTimes FROM #user# u LEFT JOIN #user_detail# ud ON u.userId = ud.userId "); $datas = $this->model->getTableBySql($sql, array( 'disableAutoSort' => false, // 关闭自动排序功能 'pageSize' => $pageSize, // 设置每页显示条目数 'pageNumber' => $pageNumber, // 设置当前页码 ) ); return $datas; } protected function prepareRowData($ID, $opts){ $dataRow = $sql = $this->model->getDBO()->DR("SELECT u.userId, u.userName, u.regDT, u.logDT, u.logTimes FROM #user# u LEFT JOIN #user_detail# ud ON u.userId = ud.userId WHERE u.userId = Integer", $ID); return $dataRow; } protected function prepareRowsData($IDs, $opts){ $dataRows = $sql = $this->model->getDBO()->DT("SELECT u.userId, u.userName, u.regDT, u.logDT, u.logTimes FROM #user# u LEFT JOIN #user_detail# ud ON u.userId = ud.userId WHERE u.userId in NumericList", $IDs); return $dataRows; } }
WEB-INF/Classes/Views/api/user/actionAdd.inc.php
class TFResource_user_add extends TFResource{ protected function prepareAction(){ $myUserMdlObj = new TFModel_user($this->PEO); $myUserDetailMdlObj = new TFModel_userDetail($this->PEO); $post = $this->PEO->input->rest->data; // do add $dupUserId = $myUserMdlObj->getID($post['user']); if($dupUserId > 0){ return $this->actionResultData('error', 'user_exists', array('errmsg' => "用户名已存在", 'errname' => "user")); } $retBase = $myUserMdlObj->add(array( 'userName'=>$post['user'], 'userPass'=>md5($post['pwd']), 'regDT'=>date("Y-m-d H:i:s"), )); if($retBase === false){ return $this->actionResultData('error', 'user_save_failed', array('errmsg' => "系统忙碌,请稍后再试")); } $newAutoId = $myUserMdlObj->getLIID(); $retDetail = $myUserDetailMdlObj->add(array( 'userId'=>$newAutoId, 'realName'=>$post['realName'], 'introduce'=>$post['introduce'], )); if($retDetail === false){ return $this->actionResultData('error', 'user_detail_save_failed', array('errmsg' => "系统忙碌,请稍后再试")); } return $this->actionResultData('success', 'ok', array('userid' => $newAutoId)); } }
WEB-INF/Classes/Views/api/user/actionMod.inc.php
class TFResource_user_mod extends TFResource{ protected function prepareAction(){ $myUserMdlObj = new TFModel_user($this->PEO); $myUserDetailMdlObj = new TFModel_userDetail($this->PEO); $post = $this->PEO->input->rest->data; $dataId = $this->PEO->input->server->tfargv['user']; // check current $userInfo = $myUserMdlObj->get($dataId); $userDetailInfo = $myUserDetailMdlObj->get($dataId); if($userInfo === false || $userDetailInfo === false){ return $this->actionResultData('error', 'user_not_exists', array('errmsg' => "用户数据不存在", 'errname' => "user")); } // do modify $retBase = $myUserMdlObj->mod($dataId, array( 'userPass'=>md5($post['pwd']), )); if($retBase === false){ return $this->actionResultData('error', 'user_update_failed', array('errmsg' => "系统忙碌,请稍后再试")); } $retDetail = $myUserDetailMdlObj->mod($dataId, array( 'realName'=>$post['realName'], 'introduce'=>$post['introduce'], )); if($retDetail === false){ return $this->actionResultData('error', 'user_detail_update_failed', array('errmsg' => "系统忙碌,请稍后再试")); } return $this->actionResultData('success', 'ok', array('userid' => $dataId)); } }
WEB-INF/Classes/Views/api/user/actionDel.inc.php
class TFResource_user_del extends TFResource{ protected function prepareAction(){ $myUserMdlObj = new TFModel_user($this->PEO); $myUserDetailMdlObj = new TFModel_userDetail($this->PEO); $post = $this->PEO->input->rest->data; $dataId = $this->PEO->input->server->tfargv['user']; // check current $userInfo = $myUserMdlObj->get($dataId); $userDetailInfo = $myUserDetailMdlObj->get($dataId); if($userInfo === false || $userDetailInfo === false){ return $this->actionResultData('error', 'user_not_exists', array('errmsg' => "用户数据不存在", 'errname' => "user")); } // do delete $retBase = $myUserMdlObj->del($dataId); if($retBase === false){ return $this->actionResultData('error', 'user_delete_failed', array('errmsg' => "系统忙碌,请稍后再试")); } $retDetail = $myUserDetailMdlObj->del($dataId); if($retDetail === false){ return $this->actionResultData('error', 'user_detail_delete_failed', array('errmsg' => "系统忙碌,请稍后再试")); } return $this->actionResultData('success', 'ok', array('userid' => $dataId)); } }
WEB-INF/Classes/Views/api/user/action_add.inc.php
class TFResource_user_add extends TFResource{ protected function prepareAction(){ $myDataValidateObj = new TFFormValidate(); $myUserMdlObj = new TFModel_user($this->PEO); $myUserDetailMdlObj = new TFModel_userDetail($this->PEO); $post = $this->PEO->input->post->getArray(); // set data valid rules $myDataValidateObj->setDataValidRules(array( 'user' => array('rule' => "max:20;min:2;", 'msgs' => array('用户名最多20个字符', '用户名最少2个字符')), 'pwd' => array('rule' => "max:20;min:6;", 'msgs' => array('密码最多20个字符', '密码最少6个字符')), 'vpwd' => array('rule' => "max:20;min:6;", 'msgs' => array('两次输入的密码不一样', '两次输入的密码不一样')), )); if(!$myDataValidateObj->dataValidationAuto($post, $errors)){ foreach($errors as $errname => $errmsg){ return $this->formResultData('error', array('errmsg' => $errmsg, 'errname' => $errname)); } } // check verify pwd if($post['pwd'] != $post['vpwd']){ return $this->formResultData('error', array('errmsg' => "两次输入的密码不一样", 'errname' => "vpwd")); } // do add $dupUserId = $myUserMdlObj->getID($post['user']); if($dupUserId > 0){ return $this->formResultData('error', array('errmsg' => "用户名已存在", 'errname' => "user")); } $retBase = $myUserMdlObj->add(array( 'userName' => $post['user'], 'userPass' => md5($post['pwd']), 'regDT' => date("Y-m-d H:i:s"), )); if($retBase === false){ return $this->formResultData('error', array('errmsg' => "系统忙碌,请稍后再试")); } $newAutoId = $myUserMdlObj->getLIID(); $retDetail = $myUserDetailMdlObj->add(array( 'userId' => $newAutoId, 'realName' => $post['realName'], 'introduce' => $post['introduce'], )); if($retDetail === false){ return $this->formResultData('error', array('errmsg' => "系统忙碌,请稍后再试")); } return $this->formResultData('success', array('userid' => $newAutoId)); } }
WEB-INF/Classes/Views/api/user/action_mod.inc.php
class TFResource_user_mod extends TFResource{ protected function prepareAction(){ $myDataValidateObj = new TFFormValidate(); $myUserMdlObj = new TFModel_user($this->PEO); $myUserDetailMdlObj = new TFModel_userDetail($this->PEO); $post = $this->PEO->input->post->getArray(); $dataId = $this->PEO->input->server->tfargv['user']; // set data valid rules $myDataValidateObj->setDataValidRules(array( 'pwd' => array('rule' => "max:20;min:6;", 'msgs' => array('密码最多20个字符', '密码最少6个字符')), 'vpwd' => array('rule' => "max:20;min:6;", 'msgs' => array('两次输入的密码不一样', '两次输入的密码不一样')), )); if(!$myDataValidateObj->dataValidationAuto($post, $errors)){ foreach($errors as $errname => $errmsg){ return $this->formResultData('error', array('errmsg' => $errmsg, 'errname' => $errname)); } } // check current $userInfo = $myUserMdlObj->get($dataId); $userDetailInfo = $myUserDetailMdlObj->get($dataId); if($userInfo === false || $userDetailInfo === false){ return $this->formResultData('error', array('errmsg' => "用户数据不存在", 'errname' => "user")); } // do modify $retBase = $myUserMdlObj->mod($dataId, array( 'userPass'=>md5($post['pwd']), )); if($retBase === false){ return $this->formResultData('error', array('errmsg' => "系统忙碌,请稍后再试")); } $retDetail = $myUserDetailMdlObj->mod($dataId, array( 'realName'=>$post['realName'], 'introduce'=>$post['introduce'], )); if($retDetail === false){ return $this->formResultData('error', array('errmsg' => "系统忙碌,请稍后再试")); } return $this->formResultData('success', array('userid' => $dataId)); } }
WEB-INF/Classes/Views/api/user/action_del.inc.php
class TFResource_user_del extends TFResource{ protected function prepareAction(){ $myUserMdlObj = new TFModel_user($this->PEO); $myUserDetailMdlObj = new TFModel_userDetail($this->PEO); $post = $this->PEO->input->post->getArray(); $dataId = $this->PEO->input->server->tfargv['user']; // check current $userInfo = $myUserMdlObj->get($dataId); $userDetailInfo = $myUserDetailMdlObj->get($dataId); if($userInfo === false || $userDetailInfo === false){ return $this->formResultData('error', array('errmsg' => "用户数据不存在", 'errname' => "user")); } // do delete $retBase = $myUserMdlObj->del($dataId); if($retBase === false){ return $this->formResultData('error', array('errmsg' => "系统忙碌,请稍后再试")); } $retDetail = $myUserDetailMdlObj->del($dataId); if($retDetail === false){ return $this->formResultData('error', array('errmsg' => "系统忙碌,请稍后再试")); } return $this->formResultData('success', array('userid' => $dataId)); } }
WEB-INF/Classes/Views/user.inc.php
class userPage extends TFPage{ public function load(){ } }
js/pages/user.js
/** * table box */ TFHomeUI.registerTableBox( 'user-list', { info: { id: 'user-list', list_rest_url: 'api/user', list_params: {}, cbfield: 'userId', actions_visible: true, user_actions: '<a class="btn btn-default" @click="showAddDlg">新建</a>', }, titles: [ {label: "ID", field: "userId", sort: "desc", width: 80}, {label: "名称", field: "userName", sort: "none"}, {label: "姓名", field: "realName", sort: "none", width: 80}, {label: "创建时间", field: "regDT", sort: "none", width: 100}, {label: "最后登录", field: "logDT", sort: "none", width: 80}, {label: "登录次数", field: "logTimes", sort: "none", width: 80}, {label: "操作", field: "_opers_", width: 120} ], callbacks: { filterData: function (d) { $.forEach(d.data, function (item, idx) { d.data[idx]['_opers_'] = '<a class="btn btn-white btn-xs" data-id="' + item.userId + '" onclick="TFHomeUI.getTable(\'user-list\').showModDlg(this)">修改</a> ' + '<a class="btn btn-white btn-xs" data-id="' + item.userId + '" onclick="TFHomeUI.getTable(\'user-list\').showDelDlg(this)">删除</a> '; }); return d; } } }, { mounted: function () { }, methods: { showAddDlg: function () { TFHomeUI.loadFormBoxInDialog( 'user-add' ); }, showModDlg: function (e) { var userId = TFHomeUI.getRowDataId(e); if (userId) { $.getREST('api/user/' + userId, {}, function (d) { TFHomeUI.loadFormBoxInDialog( 'user-mod', null, { data: { form: { info: { action: $.U('api/user/' + userId + '/_mod') }, data: d } } } ); }); } }, showDelDlg: function (e) { var userId = TFHomeUI.getRowDataId(e); if (userId) { $.getREST('api/user/' + userId, {}, function (d) { TFHomeUI.loadFormBoxInDialog( 'user-del', null, { data: { form: { info: { action: $.U('api/user/' + userId + '/_del') }, data: d } } } ); }); } } } } ); /** * form box ( user-add ) */ TFHomeUI.registerFormBox( 'user-add', { info: { title: '新建用户', descript: '新建用户', method: 'post', action: $.U('api/user/_add') }, validators: { user: {triggers: ['blur'], rules: [{min: 2, msg: '用户名最少2个字符'}, {max: 20, msg: '用户名最多20个字符'}]}, pwd: {triggers: ['blur'], rules: [{min: 6, msg: '密码最少6个字符'}]}, vpwd: { triggers: ['blur'], rules: [{ fun: function (form, elem, name) { return (form.vpwd.value != form.pwd.value); }, msg: '两次输入的密码不一致' }] } }, elements: { user: { type: 'text', label: '用户名', name: "user" }, pwd: { type: 'password', label: '密码', name: "pwd" }, vpwd: { type: 'password', label: '确认密码', name: "vpwd" }, realName: { type: 'text', label: '姓名', name: "realName" }, introduce: { type: 'textarea', label: '介绍', name: "introduce" }, submit: { type: "submit", name: "smt", value: "提交" } } }, { methods: { onSuccess: function (d) { TFHomeUI.getTableBox('user-list').gotoPage(1); } } } ); /** * form box ( user-mod ) */ TFHomeUI.registerFormBox( 'user-mod', { info: { title: '修改用户', descript: '修改用户', method: 'post', action: $.U('api/user/_mod') }, validators: { pwd: {triggers: ['blur'], rules: [{min: 6, msg: '密码最少6个字符'}]}, vpwd: { triggers: ['blur'], rules: [{ fun: function (form, elem, name) { return (form.vpwd.value != form.pwd.value); }, msg: '两次输入的密码不一致' }] } }, elements: { userName: { type: 'static', label: '姓名', name: "userName" }, pwd: { type: 'password', label: '密码', name: "pwd" }, vpwd: { type: 'password', label: '确认密码', name: "vpwd" }, realName: { type: 'text', label: '姓名', name: "realName" }, introduce: { type: 'textarea', label: '介绍', name: "introduce" }, submit: { type: "submit", name: "smt", value: "保存" } } }, { mounted: function () { this.form.elements.user.value = this.form.data.userName; this.form.elements.realName.value = this.form.data.realName; this.form.elements.introduce.value = this.form.data.introduce; }, methods: { onSuccess: function (d) { TFHomeUI.getTableBox('user-list').gotoPage(1); } } } ); /** * form box ( user-del ) */ TFHomeUI.registerFormBox( 'user-del', { info: { title: '删除用户', descript: '删除用户', method: 'post', action: $.U('api/user/_del') }, validators: {}, elements: { user: { type: 'static', label: '用户名', name: 'user' }, submit: { type: "submit", name: "smt", value: "删除" } } }, { mounted: function () { this.form.elements.user.value = this.form.data.userName; }, methods: { onSuccess: function (d) { TFHomeUI.getTableBox('user-list').gotoPage(1); } } } ); function pageLoad() { TFHomeUI.loadTableBox( 'user-list', null, { compBoxSL: '.user-list' } ); }
css/pages/user.css
body{ }
WEB-INF/Templates/Tpl/user.html
<!DOCTYPE html> <html> <head> <% $_TFPage->getHead() %> </head> <body> <div class="wrapper"> <div class="wrapper-content"> <div class="row user-list"> </div> </div> </div> </body> </html> <% $_TFPage->getFoot() %>
POST请求
POST /api/user HTTP/1.1 Content-Type: application/json { "user":"test1", "pwd":"123456", "realName":"测试1", "introduce":"这是一个测试而已" }
返回结果
{ "status": "success", "message": "ok", "userid": "1" }
PUT请求
PUT /api/user/1 HTTP/1.1 Content-Type: application/json { "pwd":"12345678", "realName":"修改一下1", "introduce":"这是测试1的修改内容" }
返回结果
{ "status": "success", "message": "ok", "userid": "1" }
DELETE请求
DELETE /api/user/1 HTTP/1.1
返回结果
{ "status": "success", "message": "ok", "userid": "6" }
数据表格页面
打开网址 /user
添加表单
修改表单
删除表单