效果图展示
实现流程
一、新建菜单栏:
<li class="list-group-item {:$config['actionname']=='my_ceshiindex'?'active':''}"><a href="{:url('my_ceshi/index')}"><i class="fa fa-compass fa-fw"></i>测试</a></li>
二、新建控制器:
MyCeshi.php
:
<?php
namespace app\index\controller;
use app\common\controller\Frontend;
/**
*
*
* @icon fa fa-circle-o
*/
class MyCeshi extends Frontend
{
/**
* MyCeshi模型对象
* @var \app\admin\model\MyCeshi
*/
protected $model = null;
protected $noNeedLogin = "*";
public function _initialize()
{
parent::_initialize();
$this->model = new \app\index\model\MyCeshi;
}
/**
* 查看
*/
public function index()
{
//当前是否为关联查询
$this->relationSearch = false;
//设置过滤方法
$this->request->filter(['strip_tags', 'trim']);
if ($this->request->isAjax())
{
//如果发送的来源是Selectpage,则转发到Selectpage
if ($this->request->request('keyField'))
{
return $this->selectpage();
}
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
$total = $this->model
->where($where)
->order($sort, $order)
->count();
$list = $this->model
->where($where)
->order($sort, $order)
->limit($offset, $limit)
->select();
foreach ($list as $row) {
$row->visible(['id','name']);
}
$list = collection($list)->toArray();
$result = array("total" => $total, "rows" => $list);
return json($result);
}
return $this->view->fetch();
}
/**
* 添加
*/
public function add()
{
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
if ($params) {
$params = $this->preExcludeFields($params);
if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
$params[$this->dataLimitField] = $this->auth->id;
}
$result = false;
Db::startTrans();
try {
//是否采用模型验证
if ($this->modelValidate) {
$name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate;
$this->model->validateFailException(true)->validate($validate);
}
$result = $this->model->allowField(true)->save($params);
Db::commit();
} catch (ValidateException $e) {
Db::rollback();
$this->error($e->getMessage());
} catch (PDOException $e) {
Db::rollback();
$this->error($e->getMessage());
} catch (Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
if ($result !== false) {
$this->success();
} else {
$this->error(__('No rows were inserted'));
}
}
$this->error(__('Parameter %s can not be empty', ''));
}
return $this->view->fetch();
}
/**
* 编辑
*/
public function edit($ids = null)
{
$row = $this->model->get($ids);
if (!$row) {
$this->error(__('No Results were found'));
}
$adminIds = $this->getDataLimitAdminIds();
if (is_array($adminIds)) {
if (!in_array($row[$this->dataLimitField], $adminIds)) {
$this->error(__('You have no permission'));
}
}
if ($this->request->isPost()) {
$params = $this->request->post("row/a");
if ($params) {
$params = $this->preExcludeFields($params);
$result = false;
Db::startTrans();
try {
//是否采用模型验证
if ($this->modelValidate) {
$name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate;
$row->validateFailException(true)->validate($validate);
}
$result = $row->allowField(true)->save($params);
Db::commit();
} catch (ValidateException $e) {
Db::rollback();
$this->error($e->getMessage());
} catch (PDOException $e) {
Db::rollback();
$this->error($e->getMessage());
} catch (Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
if ($result !== false) {
$this->success();
} else {
$this->error(__('No rows were updated'));
}
}
$this->error(__('Parameter %s can not be empty', ''));
}
$this->view->assign("row", $row);
return $this->view->fetch();
}
}
三、新建模型:
MyCeshi.php
:
<?php
namespace app\index\model;
use think\Model;
class MyCeshi extends Model
{
// 表名
protected $name = 'my_ceshi';
// 自动写入时间戳字段
protected $autoWriteTimestamp = false;
// 定义时间戳字段名
protected $createTime = false;
protected $updateTime = false;
protected $deleteTime = false;
// 追加属性
protected $append = [
];
}
四、新建视图:
index.html
:
<div id="content-container" class="container1">
<div class="row">
<div class="col-md-2">
{include file="common/sidenav" /}
</div>
<div class="col-md-10">
<div class="panel panel-default panel-intro" style="padding-top: 6px">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
<a href="javascript:;" class="btn btn-primary btn-refresh" title="刷新" ><i class="fa fa-refresh"></i> </a>
<a href="javascript:;" class="btn btn-success btn-add" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
<a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled " title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
</div>
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
data-operate-edit="true"
data-operate-del="true"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
其余的add.html和edit.html只需要完全模仿后台,修改字段即可
五、新建js
my_ceshi.js:
define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
var Controller = {
index: function () {
// 初始化表格参数配置
Table.api.init({
extend: {
index_url: 'my_ceshi/index' + location.search,
add_url: 'my_ceshi/add',
edit_url: 'my_ceshi/edit',
del_url: 'my_ceshi/del',
multi_url: 'my_ceshi/multi',
table: 'my_ceshi',
}
});
var table = $("#table");
// 初始化表格
table.bootstrapTable({
url: $.fn.bootstrapTable.defaults.extend.index_url,
pk: 'id',
sortName: 'id',
columns: [
[
{
checkbox: true},
{
field: 'id', title: __('Id')},
{
field: 'name', title: __('Name')},
{
field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
]
]
});
// 为表格绑定事件
Table.api.bindevent(table);
},
add: function () {
Controller.api.bindevent();
},
edit: function () {
Controller.api.bindevent();
},
api: {
bindevent: function () {
Form.api.bindevent($("form[role=form]"));
}
}
};
return Controller;
});
扫描二维码关注公众号,回复:
13119435 查看本文章
js文件修改指定的字段就行
六、修改Frontend文件:
<?php
namespace app\common\controller;
use app\common\library\Auth;
use think\Config;
use think\Controller;
use think\Hook;
use think\Lang;
use think\Loader;
/**
* 前台控制器基类
*/
class Frontend extends Controller
{
/**
* 布局模板
* @var string
*/
protected $layout = 'default';
/**
* 无需登录的方法,同时也就不需要鉴权了
* @var array
*/
protected $noNeedLogin = [];
/**
* 无需鉴权的方法,但需要登录
* @var array
*/
protected $noNeedRight = ['*'];
/**
* 权限控制类
* @var Auth
*/
protected $auth = null;
/**
* 模型对象
* @var \think\Model
*/
protected $model = null;
/**
* 快速搜索时执行查找的字段
*/
protected $searchFields = 'id';
/**
* 是否是关联查询
*/
protected $relationSearch = false;
/**
* 是否开启数据限制
* 支持auth/personal
* 表示按权限判断/仅限个人
* 默认为禁用,若启用请务必保证表中存在admin_id字段
*/
protected $dataLimit = false;
/**
* 数据限制字段
*/
protected $dataLimitField = 'admin_id';
/**
* 数据限制开启时自动填充限制字段值
*/
protected $dataLimitFieldAutoFill = true;
/**
* 是否开启Validate验证
*/
protected $modelValidate = false;
/**
* 是否开启模型场景验证
*/
protected $modelSceneValidate = false;
/**
* Multi方法可批量修改的字段
*/
protected $multiFields = 'status';
/**
* Selectpage可显示的字段
*/
protected $selectpageFields = '*';
/**
* 前台提交过来,需要排除的字段数据
*/
protected $excludeFields = "";
/**
* 导入文件首行类型
* 支持comment/name
* 表示注释或字段名
*/
protected $importHeadType = 'comment';
use \app\admin\library\traits\Backend;
public function _initialize()
{
//移除HTML标签
$this->request->filter('trim,strip_tags,htmlspecialchars');
$modulename = $this->request->module();
$controllername = Loader::parseName($this->request->controller());
$actionname = strtolower($this->request->action());
// 定义是否Dialog请求
!defined('IS_DIALOG') && define('IS_DIALOG', input("dialog") ? true : false);
// 如果有使用模板布局
if ($this->layout) {
$this->view->engine->layout('layout/' . $this->layout);
}
$this->auth = Auth::instance();
// token
$token = $this->request->server('HTTP_TOKEN', $this->request->request('token', \think\Cookie::get('token')));
$path = str_replace('.', '/', $controllername) . '/' . $actionname;
// 设置当前请求的URI
$this->auth->setRequestUri($path);
// 检测是否需要验证登录
if (!$this->auth->match($this->noNeedLogin)) {
//初始化
$this->auth->init($token);
//检测是否登录
if (!$this->auth->isLogin()) {
$this->error(__('Please login first'), 'index/user/login');
}
// 判断是否需要验证权限
if (!$this->auth->match($this->noNeedRight)) {
// 判断控制器和方法判断是否有对应权限
if (!$this->auth->check($path)) {
$this->error(__('You have no permission'));
}
}
} else {
// 如果有传递token才验证是否登录状态
if ($token) {
$this->auth->init($token);
}
}
$this->view->assign('user', $this->auth->getUser());
// 语言检测
$lang = strip_tags($this->request->langset());
$site = Config::get("site");
$upload = \app\common\model\Config::upload();
// 上传信息配置后
Hook::listen("upload_config_init", $upload);
// 配置信息
$config = [
'site' => array_intersect_key($site, array_flip(['name', 'cdnurl', 'version', 'timezone', 'languages'])),
'upload' => $upload,
'modulename' => $modulename,
'controllername' => $controllername,
'actionname' => $actionname,
'jsname' => 'frontend/' . str_replace('.', '/', $controllername),
'moduleurl' => rtrim(url("/{
$modulename}", '', false), '/'),
'language' => $lang
];
$config = array_merge($config, Config::get("view_replace_str"));
Config::set('upload', array_merge(Config::get('upload'), $upload));
// 配置信息后
Hook::listen("config_init", $config);
// 加载当前控制器语言包
$this->loadlang($controllername);
$this->assign('site', $site);
$this->assign('config', $config);
}
/**
* 加载语言文件
* @param string $name
*/
protected function loadlang($name)
{
Lang::load(APP_PATH . $this->request->module() . '/lang/' . $this->request->langset() . '/' . str_replace('.', '/', $name) . '.php');
}
/**
* 渲染配置信息
* @param mixed $name 键名或数组
* @param mixed $value 值
*/
protected function assignconfig($name, $value = '')
{
$this->view->config = array_merge($this->view->config ? $this->view->config : [], is_array($name) ? $name : [$name => $value]);
}
/**
* 生成查询所需要的条件,排序方式
* @param mixed $searchfields 快速查询的字段
* @param boolean $relationSearch 是否关联查询
* @return array
*/
protected function buildparams($searchfields = null, $relationSearch = null)
{
$searchfields = is_null($searchfields) ? $this->searchFields : $searchfields;
$relationSearch = is_null($relationSearch) ? $this->relationSearch : $relationSearch;
$search = $this->request->get("search", '');
$filter = $this->request->get("filter", '');
$op = $this->request->get("op", '', 'trim');
$sort = $this->request->get("sort", !empty($this->model) && $this->model->getPk() ? $this->model->getPk() : 'id');
$order = $this->request->get("order", "DESC");
$offset = $this->request->get("offset", 0);
$limit = $this->request->get("limit", 0);
$filter = (array)json_decode($filter, true);
$op = (array)json_decode($op, true);
$filter = $filter ? $filter : [];
$where = [];
$tableName = '';
if ($relationSearch) {
if (!empty($this->model)) {
$name = \think\Loader::parseName(basename(str_replace('\\', '/', get_class($this->model))));
$tableName = $name . '.';
}
$sortArr = explode(',', $sort);
foreach ($sortArr as $index => & $item) {
$item = stripos($item, ".") === false ? $tableName . trim($item) : $item;
}
unset($item);
$sort = implode(',', $sortArr);
}
if ($search) {
$searcharr = is_array($searchfields) ? $searchfields : explode(',', $searchfields);
foreach ($searcharr as $k => &$v) {
$v = stripos($v, ".") === false ? $tableName . $v : $v;
}
unset($v);
$where[] = [implode("|", $searcharr), "LIKE", "%{
$search}%"];
}
foreach ($filter as $k => $v) {
$sym = isset($op[$k]) ? $op[$k] : '=';
if (stripos($k, ".") === false) {
$k = $tableName . $k;
}
$v = !is_array($v) ? trim($v) : $v;
$sym = strtoupper(isset($op[$k]) ? $op[$k] : $sym);
switch ($sym) {
case '=':
case '<>':
$where[] = [$k, $sym, (string)$v];
break;
case 'LIKE':
case 'NOT LIKE':
case 'LIKE %...%':
case 'NOT LIKE %...%':
$where[] = [$k, trim(str_replace('%...%', '', $sym)), "%{
$v}%"];
break;
case '>':
case '>=':
case '<':
case '<=':
$where[] = [$k, $sym, intval($v)];
break;
case 'FINDIN':
case 'FINDINSET':
case 'FIND_IN_SET':
$where[] = "FIND_IN_SET('{
$v}', " . ($relationSearch ? $k : '`' . str_replace('.', '`.`', $k) . '`') . ")";
break;
case 'IN':
case 'IN(...)':
case 'NOT IN':
case 'NOT IN(...)':
$where[] = [$k, str_replace('(...)', '', $sym), is_array($v) ? $v : explode(',', $v)];
break;
case 'BETWEEN':
case 'NOT BETWEEN':
$arr = array_slice(explode(',', $v), 0, 2);
if (stripos($v, ',') === false || !array_filter($arr)) {
continue 2;
}
//当出现一边为空时改变操作符
if ($arr[0] === '') {
$sym = $sym == 'BETWEEN' ? '<=' : '>';
$arr = $arr[1];
} elseif ($arr[1] === '') {
$sym = $sym == 'BETWEEN' ? '>=' : '<';
$arr = $arr[0];
}
$where[] = [$k, $sym, $arr];
break;
case 'RANGE':
case 'NOT RANGE':
$v = str_replace(' - ', ',', $v);
$arr = array_slice(explode(',', $v), 0, 2);
if (stripos($v, ',') === false || !array_filter($arr)) {
continue 2;
}
//当出现一边为空时改变操作符
if ($arr[0] === '') {
$sym = $sym == 'RANGE' ? '<=' : '>';
$arr = $arr[1];
} elseif ($arr[1] === '') {
$sym = $sym == 'RANGE' ? '>=' : '<';
$arr = $arr[0];
}
$where[] = [$k, str_replace('RANGE', 'BETWEEN', $sym) . ' time', $arr];
break;
case 'LIKE':
case 'LIKE %...%':
$where[] = [$k, 'LIKE', "%{
$v}%"];
break;
case 'NULL':
case 'IS NULL':
case 'NOT NULL':
case 'IS NOT NULL':
$where[] = [$k, strtolower(str_replace('IS ', '', $sym))];
break;
default:
break;
}
}
$where = function ($query) use ($where) {
foreach ($where as $k => $v) {
if (is_array($v)) {
call_user_func_array([$query, 'where'], $v);
} else {
$query->where($v);
}
}
};
return [$where, $sort, $order, $offset, $limit];
}
/**
* 获取数据限制的管理员ID
* 禁用数据限制时返回的是null
* @return mixed
*/
protected function getDataLimitAdminIds()
{
if (!$this->dataLimit) {
return null;
}
if ($this->auth->isSuperAdmin()) {
return null;
}
$adminIds = [];
if (in_array($this->dataLimit, ['auth', 'personal'])) {
$adminIds = $this->dataLimit == 'auth' ? $this->auth->getChildrenAdminIds(true) : [$this->auth->id];
}
return $adminIds;
}
}
默认情况下Frontend文件里缺少Backend文件里的一些方法,从Backend里面引入即可
注意事项
修改好后的单页面会有头部和底部,只需要将其屏蔽掉即可
修改视图文件中的layout/default
:
<!DOCTYPE html>
<html>
<head>
{include file="common/meta" /}
<link href="__CDN__/assets/css/user.css?v={$Think.config.site.version}" rel="stylesheet">
{if IS_DIALOG}
<link href="__CDN__/assets/css/backend.css?v={$Think.config.site.version}" rel="stylesheet">
<style>
.is-dialog {
padding-top: 0
}
</style>
{/if}
</head>
<body class="inside-header inside-aside {:defined('IS_DIALOG') && IS_DIALOG ? 'is-dialog' : ''}">
{if !IS_DIALOG}
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#header-navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<!--<a class="navbar-brand" href="{:url('/')}" style="padding:6px 15px;"><img src="__CDN__/assets/img/logo.png" style="height:40px;" alt=""></a>-->
<a class="navbar-brand" href="{:url('/')}" style="padding-top:16px;font-size: 32px;color: #fff;">育儿家</a>
</div>
<div class="collapse navbar-collapse" id="header-navbar">
<ul class="nav navbar-nav navbar-right">
<li><a href="/" target="_blank">{:__('Home')}</a></li>
<li class="dropdown">
{if $user}
<a href="{:url('user/index')}" class="dropdown-toggle" data-toggle="dropdown"
style="padding-top: 10px;height: 50px;">
<span class="avatar-img"><img src="{$user.avatar|cdnurl}" alt=""></span>
</a>
{else /}
<a href="{:url('user/index')}" class="dropdown-toggle" data-toggle="dropdown">{:__('User center')}
<b class="caret"></b></a>
{/if}
<ul class="dropdown-menu">
{if $user}
<li><a href="{:url('user/index')}"><i class="fa fa-user-circle fa-fw"></i>{:__('User center')}</a></li>
<!--<li><a href="{:url('user/profile')}"><i class="fa fa-user-o fa-fw"></i>{:__('Profile')}</a></li>-->
<!--<li><a href="{:url('user/changepwd')}"><i class="fa fa-key fa-fw"></i>{:__('Change password')}</a></li>-->
<li><a href="{:url('user/logout')}"><i class="fa fa-sign-out fa-fw"></i>{:__('Sign out')}</a></li>
{else /}
<li><a href="{:url('user/login')}"><i class="fa fa-sign-in fa-fw"></i> {:__('Sign in')}</a></li>
<!--<li><a href="{:url('user/register')}"><i class="fa fa-user-o fa-fw"></i> {:__('Sign up')}</a></li>-->
{/if}
</ul>
</li>
</ul>
</div>
</div>
</nav>
{/if}
<main class="content">
{__CONTENT__}
</main>
{if !IS_DIALOG}
<footer class="footer" style="clear:both">
<p class="copyright">Copyright © 2019 All Rights {$site.name} {:__('Copyrights')} <a href="http://www.miibeian.gov.cn" target="_blank">{$site.beian}</a></p>
</footer>
{/if}
{include file="common/script" /}
</body>
</html>