更新组件

This commit is contained in:
15090180611 2024-11-04 15:00:20 +08:00
parent cfbb40ac63
commit 3a600110c0
205 changed files with 122009 additions and 43 deletions

View File

@ -0,0 +1,39 @@
<?php
namespace app\admin\controller;
use app\common\controller\Backend;
use think\Config;
class Epay extends Backend
{
protected $noNeedRight = ['upload'];
/**
* 上传本地证书
* @return void
*/
public function upload()
{
Config::set('default_return_type', 'json');
$certname = $this->request->post('certname', '');
$certPathArr = [
'cert_client' => '/addons/epay/certs/apiclient_cert.pem', //微信支付api
'cert_key' => '/addons/epay/certs/apiclient_key.pem', //微信支付api
'app_cert_public_key' => '/addons/epay/certs/appCertPublicKey.crt',//应用公钥证书路径
'alipay_root_cert' => '/addons/epay/certs/alipayRootCert.crt', //支付宝根证书路径
'ali_public_key' => '/addons/epay/certs/alipayCertPublicKey.crt', //支付宝公钥证书路径
];
if (!isset($certPathArr[$certname])) {
$this->error("证书错误");
}
$url = $certPathArr[$certname];
$file = $this->request->file('file');
if (!$file) {
$this->error("未上传文件");
}
$file->move(dirname(ROOT_PATH . $url), basename(ROOT_PATH . $url), true);
$this->success(__('上传成功'), '', ['url' => $url]);
}
}

View File

@ -0,0 +1,246 @@
<?php
namespace app\admin\controller\manystore;
use app\common\controller\Backend;
use think\Config;
use think\console\Input;
use think\Db;
use think\Exception;
/**
* 在线命令管理
*
* @icon fa fa-circle-o
*/
class Command extends Backend
{
/**
* Command模型对象
*/
protected $model = null;
protected $noNeedRight = ['get_controller_list', 'get_field_list'];
public function _initialize()
{
parent::_initialize();
$this->model = model('ManystoreCommand');
$this->view->assign("statusList", $this->model->getStatusList());
}
/**
* 添加
*/
public function add()
{
$tableList = [];
$list = \think\Db::query("SHOW TABLES");
foreach ($list as $key => $row) {
$tableList[reset($row)] = reset($row);
}
$this->view->assign("tableList", $tableList);
return $this->view->fetch();
}
/**
* 获取字段列表
* @internal
*/
public function get_field_list()
{
$dbname = Config::get('database.database');
$prefix = Config::get('database.prefix');
$table = $this->request->request('table');
//从数据库中获取表字段信息
$sql = "SELECT * FROM `information_schema`.`columns` "
. "WHERE TABLE_SCHEMA = ? AND table_name = ? "
. "ORDER BY ORDINAL_POSITION";
//加载主表的列
$columnList = Db::query($sql, [$dbname, $table]);
$fieldlist = [];
foreach ($columnList as $index => $item) {
$fieldlist[] = $item['COLUMN_NAME'];
}
$this->success("", null, ['fieldlist' => $fieldlist]);
}
/**
* 获取控制器列表
* @internal
*/
public function get_controller_list()
{
//搜索关键词,客户端输入以空格分开,这里接收为数组
$word = (array)$this->request->request("q_word/a");
$word = implode('', $word);
$manystorePath = dirname(__DIR__) . DS;
$manystorePathArray = explode(DS,$manystorePath);
$manystorePathArray[count($manystorePathArray) - 3] = 'manystore';
$controllerDir = implode(DS,$manystorePathArray);
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($controllerDir), \RecursiveIteratorIterator::LEAVES_ONLY
);
$list = [];
foreach ($files as $name => $file) {
if (!$file->isDir()) {
$filePath = $file->getRealPath();
$name = str_replace($controllerDir, '', $filePath);
$name = str_replace(DS, "/", $name);
if (!preg_match("/(.*)\.php\$/", $name)) {
continue;
}
if (!$word || stripos($name, $word) !== false) {
$list[] = ['id' => $name, 'name' => $name];
}
}
}
$pageNumber = $this->request->request("pageNumber");
$pageSize = $this->request->request("pageSize");
return json(['list' => array_slice($list, ($pageNumber - 1) * $pageSize, $pageSize), 'total' => count($list)]);
}
/**
* 详情
*/
public function detail($ids)
{
$row = $this->model->get($ids);
if (!$row) {
$this->error(__('No Results were found'));
}
$this->view->assign("row", $row);
return $this->view->fetch();
}
/**
* 执行
*/
public function execute($ids)
{
$row = $this->model->get($ids);
if (!$row) {
$this->error(__('No Results were found'));
}
$result = $this->doexecute($row['type'], json_decode($row['params'], true));
$this->success("", null, ['result' => $result]);
}
/**
* 执行命令
*/
public function command($action = '')
{
$commandtype = $this->request->request("commandtype");
$params = $this->request->request();
$allowfields = [
'crud' => 'table,controller,model,fields,force,local,delete,menu',
'menu' => 'controller,delete',
'min' => 'module,resource,optimize',
'api' => 'url,module,output,template,force,title,author,class,language',
];
$argv = [];
$allowfields = isset($allowfields[$commandtype]) ? explode(',', $allowfields[$commandtype]) : [];
$allowfields = array_filter(array_intersect_key($params, array_flip($allowfields)));
if (isset($params['local']) && !$params['local']) {
$allowfields['local'] = $params['local'];
} else {
unset($allowfields['local']);
}
foreach ($allowfields as $key => $param) {
$argv[] = "--{$key}=" . (is_array($param) ? implode(',', $param) : $param);
}
if ($commandtype == 'crud') {
$extend = 'setcheckboxsuffix,enumradiosuffix,imagefield,filefield,intdatesuffix,switchsuffix,citysuffix,selectpagesuffix,selectpagessuffix,ignorefields,sortfield,editorsuffix,headingfilterfield';
$extendArr = explode(',', $extend);
foreach ($params as $index => $item) {
if (in_array($index, $extendArr)) {
foreach (explode(',', $item) as $key => $value) {
if ($value) {
$argv[] = "--{$index}={$value}";
}
}
}
}
$isrelation = (int)$this->request->request('isrelation');
if ($isrelation && isset($params['relation'])) {
foreach ($params['relation'] as $index => $relation) {
foreach ($relation as $key => $value) {
$argv[] = "--{$key}=" . (is_array($value) ? implode(',', $value) : $value);
}
}
}
} else {
if ($commandtype == 'menu') {
if (isset($params['allcontroller']) && $params['allcontroller']) {
$argv[] = "--controller=all-controller";
} else {
foreach (explode(',', $params['controllerfile']) as $index => $param) {
if ($param) {
$argv[] = "--controller=" . substr($param, 0, -4);
}
}
}
} else {
if ($commandtype == 'min') {
} else {
if ($commandtype == 'api') {
} else {
}
}
}
}
if ($action == 'execute') {
if (stripos(implode(' ', $argv), '--controller=all-controller') !== false) {
$this->error("只允许在命令行执行该命令,执行前请做好菜单规则备份!!!");
}
if (config('app_debug')) {
$result = $this->doexecute($commandtype, $argv);
$this->success("", null, ['result' => $result]);
} else {
$this->error("只允许在开发环境下执行命令");
}
} else {
$this->success("", null, ['command' => "php think {$commandtype} " . implode(' ', $argv)]);
}
return;
}
protected function doexecute($commandtype, $argv)
{
$commandName = "\\app\\admin\\manystore_command\\" . ucfirst($commandtype);
$input = new Input($argv);
$output = new \addons\manystore\library\Output();
$command = new $commandName($commandtype);
$data = [
'type' => $commandtype,
'params' => json_encode($argv),
'command' => "php think {$commandtype} " . implode(' ', $argv),
'executetime' => time(),
];
$this->model->save($data);
try {
$command->run($input, $output);
$result = implode("\n", $output->getMessage());
$this->model->status = 'successed';
} catch (Exception $e) {
$result = implode("\n", $output->getMessage()) . "\n";
$result .= $e->getMessage();
$this->model->status = 'failured';
}
$result = trim($result);
$this->model->content = $result;
$this->model->save();
return $result;
}
}

View File

@ -0,0 +1,232 @@
<?php
namespace app\admin\controller\manystore;
use app\common\controller\Backend;
use app\common\model\ManystoreConfigGroup;
use app\common\model\Config as ConfigModel;
use think\Db;
use think\Exception;
/**
* 商家系统配置管理
*
* @icon fa fa-circle-o
*/
class Config extends Backend
{
/**
* ManystoreConfig模型对象
* @var \app\common\model\ManystoreConfig
*/
protected $model = null;
protected $noNeedRight = ['check', 'rulelist', 'get_fields_list'];
public function _initialize()
{
parent::_initialize();
$this->model = new \app\common\model\ManystoreConfig;
$manystoreConfigGroup = new ManystoreConfigGroup();
$group_data = $manystoreConfigGroup->getGroupData();
$this->view->assign('typeList', ConfigModel::getTypeList());
$this->view->assign('ruleList', ConfigModel::getRegexList());
$this->view->assign('groupList', $group_data);
}
/**
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
*/
/**
* 添加
*/
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;
}
foreach ($params as $k => &$v) {
$v = is_array($v) && $k !== 'setting' ? implode(',', $v) : $v;
}
if (in_array($params['type'], ['select', 'selects', 'checkbox', 'radio', 'array'])) {
$params['content'] = json_encode(ConfigModel::decode($params['content']), JSON_UNESCAPED_UNICODE);
} else {
$params['content'] = '';
}
$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 (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);
$exists = $this->model->where(['name' => $params['name']])->where(['id' => ['neq', $row['id']]])->find();
if ($exists) {
$this->error(__('Name already exist'));
}
foreach ($params as $k => &$v) {
$v = is_array($v) && $k !== 'setting' ? implode(',', $v) : $v;
}
if (in_array($params['type'], ['select', 'selects', 'checkbox', 'radio', 'array'])) {
$params['content'] = json_encode(ConfigModel::decode($params['content']), JSON_UNESCAPED_UNICODE);
} else {
$params['content'] = '';
}
$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 (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', ''));
}
$row['content'] = ConfigModel::encode(json_decode($row['content'],true));
$this->view->assign("row", $row);
return $this->view->fetch();
}
/**
* 检测配置项是否存在
* @internal
*/
public function check()
{
$params = $this->request->post("row/a");
if ($params) {
$config = $this->model->get($params);
if (!$config) {
$this->success();
} else {
$this->error(__('Name already exist'));
}
} else {
$this->error(__('Invalid parameters'));
}
}
/**
* 规则列表
* @internal
*/
public function rulelist()
{
//主键
$primarykey = $this->request->request("keyField");
//主键值
$keyValue = $this->request->request("keyValue", "");
$keyValueArr = array_filter(explode(',', $keyValue));
$regexList = \app\common\model\Config::getRegexList();
$list = [];
foreach ($regexList as $k => $v) {
if ($keyValueArr) {
if (in_array($k, $keyValueArr)) {
$list[] = ['id' => $k, 'name' => $v];
}
} else {
$list[] = ['id' => $k, 'name' => $v];
}
}
return json(['list' => $list]);
}
/**
* 获取表列表
* @internal
*/
public function get_table_list()
{
$tableList = [];
$dbname = \think\Config::get('database.database');
$tableList = \think\Db::query("SELECT `TABLE_NAME` AS `name`,`TABLE_COMMENT` AS `title` FROM `information_schema`.`TABLES` where `TABLE_SCHEMA` = '{$dbname}';");
$this->success('', null, ['tableList' => $tableList]);
}
/**
* 获取表字段列表
* @internal
*/
public function get_fields_list()
{
$table = $this->request->request('table');
$dbname = \think\Config::get('database.database');
//从数据库中获取表字段信息
$sql = "SELECT `COLUMN_NAME` AS `name`,`COLUMN_COMMENT` AS `title`,`DATA_TYPE` AS `type` FROM `information_schema`.`columns` WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION";
//加载主表的列
$fieldList = Db::query($sql, [$dbname, $table]);
$this->success("", null, ['fieldList' => $fieldList]);
}
}

View File

@ -0,0 +1,178 @@
<?php
namespace app\admin\controller\manystore;
use app\common\controller\Backend;
use think\Cache;
use think\Db;
use think\Exception;
use think\exception\PDOException;
use think\exception\ValidateException;
/**
*
*
* @icon fa fa-circle-o
*/
class ConfigGroup extends Backend
{
/**
* ManystoreConfigGroup模型对象
* @var \app\common\model\ManystoreConfigGroup
*/
protected $model = null;
public function _initialize()
{
parent::_initialize();
$this->model = new \app\common\model\ManystoreConfigGroup;
}
/**
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
*/
/**
* 添加
*/
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();
Cache::rm('manystore_config_data');
} 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();
Cache::rm('manystore_config_data');
} 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();
}
/**
* 删除
*/
public function del($ids = "")
{
if ($ids) {
$pk = $this->model->getPk();
$adminIds = $this->getDataLimitAdminIds();
if (is_array($adminIds)) {
$this->model->where($this->dataLimitField, 'in', $adminIds);
}
$list = $this->model->where($pk, 'in', $ids)->select();
$count = 0;
Db::startTrans();
try {
foreach ($list as $k => $v) {
$count += $v->delete();
}
Db::commit();
Cache::rm('manystore_config_data');
} catch (PDOException $e) {
Db::rollback();
$this->error($e->getMessage());
} catch (Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
if ($count) {
$this->success();
} else {
$this->error(__('No rows were deleted'));
}
}
$this->error(__('Parameter %s can not be empty', 'ids'));
}
}

View File

@ -0,0 +1,255 @@
<?php
namespace app\admin\controller\manystore;
use app\manystore\model\Manystore;
use app\manystore\model\ManystoreShop;
use app\manystore\model\ManystoreAuthGroup;
use app\manystore\model\ManystoreAuthGroupAccess;
use app\common\controller\Backend;
use fast\Random;
use fast\Tree;
use think\Exception;
use think\Validate;
/**
* 管理员管理
*
* @icon fa fa-users
* @remark 一个管理员可以有多个角色组,左侧的菜单根据管理员所拥有的权限进行生成
*/
class Index extends Backend
{
/**
* @var \app\manystore\model\Manystore
*/
protected $model = null;
protected $shopModel = null;
protected $selectpageFields = 'id,username,nickname,avatar';
protected $searchFields = 'id,username,nickname';
protected $childrenGroupIds = [];
protected $childrenAdminIds = [];
public function _initialize()
{
parent::_initialize();
$this->model = new Manystore();
$this->shopModel = new ManystoreShop();
}
/**
* 查看
*/
public function index()
{
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)
->where(array('is_main'=>1))
->order($sort, $order)
->count();
$list = $this->model
->where($where)
->where(array('is_main'=>1))
->field(['password', 'salt', 'token'], true)
->order($sort, $order)
->limit($offset, $limit)
->select();
$result = array("total" => $total, "rows" => $list);
return json($result);
}
return $this->view->fetch();
}
/**
* 添加
*/
public function add()
{
if ($this->request->isPost()) {
$this->token();
$params = $this->request->post("row/a");
$shop = $this->request->post("shop/a");
if ($params) {
if (!Validate::is($params['password'], '\S{6,16}')) {
$this->error(__("Please input correct password"));
}
db()->startTrans();
try{
$shop_info = $this->shopModel->save($shop);
if($shop_info === false){
$this->error($this->shopModel->getError());
}
$params['shop_id'] = $this->shopModel->id;
$params['salt'] = Random::alnum();
$params['password'] = md5(md5($params['password']) . $params['salt']);
$params['avatar'] = '/assets/img/avatar.png'; //设置新管理员默认头像。
$params['is_main'] = 1;
$name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate;
$result = $this->model->validate($validate)->save($params);
if ($result === false) {
$this->error($this->model->getError());
}
$manystoreAuthGroupModel = new ManystoreAuthGroup();
$group = [];
$group['shop_id'] = $this->shopModel->id;
$group['name'] = '超级管理员';
$group['rules'] = '*';
$group['createtime'] = time();
$group['updatetime'] = time();
$group_id = $manystoreAuthGroupModel->insertGetId($group);
if(!$group_id){
$this->error('添加失败');
}
$manystoreAuthGroupAccessModel = new ManystoreAuthGroupAccess();
$group_access = [];
$group_access['uid'] = $this->model->id;
$group_access['group_id'] = $group_id;
$manystoreAuthGroupAccessModel->insert($group_access);
db()->commit();
$this->success();
}catch (Exception $e){
db()->rollback();
$this->error($e->getMessage());
}
}
$this->error();
}
return $this->view->fetch();
}
/**
* 编辑
*/
public function edit($ids = null)
{
$row = $this->model->get(['id' => $ids,'is_main'=>1]);
if (!$row) {
$this->error(__('No Results were found'));
}
$shop_info = $this->shopModel->get(array('id'=>$row['shop_id']));
if(!$shop_info){
$this->error(__('商家信息资料不存在'));
}
if ($this->request->isPost()) {
$this->token();
$params = $this->request->post("row/a");
$shop = $this->request->post("shop/a");
if ($params) {
$result = $shop_info->save($shop);
if($result === false){
$this->error(__("修改商家信息资料失败"));
}
if ($params['password']) {
if (!Validate::is($params['password'], '\S{6,16}')) {
$this->error(__("Please input correct password"));
}
$params['salt'] = Random::alnum();
$params['password'] = md5(md5($params['password']) . $params['salt']);
} else {
unset($params['password'], $params['salt']);
}
//这里需要针对username和email做唯一验证
$name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate;
$manystoreValidate = \think\Loader::validate($validate);
$manystoreValidate->rule([
'username' => 'regex:\w{3,12}|unique:manystore,username,' . $row->id,
'email' => 'require|email|unique:manystore,email,' . $row->id,
'password' => 'regex:\S{32}',
]);
$result = $row->validate($validate)->save($params);
if ($result === false) {
$this->error($row->getError());
}
$this->success();
}
$this->error();
}
$grouplist = $this->auth->getGroups($row['id']);
$groupids = [];
foreach ($grouplist as $k => $v) {
$groupids[] = $v['id'];
}
$this->view->assign("row", $row);
$this->view->assign("shop", $shop_info);
$this->view->assign("groupids", $groupids);
return $this->view->fetch();
}
/**
* 删除
*/
public function del($ids = "")
{
if ($ids) {
$row = $this->model->get(['id' => $ids,'is_main'=>1]);
if(!$row){
$this->error(__('No Results were found'));
}
db()->startTrans();
try{
$result = $row->delete();
if(!$result){
exception('账号信息删除失败');
}
$result = $this->shopModel->where(array('id'=>$row['shop_id']))->delete();
if(!$result){
exception('商家信息删除失败');
}
db()->commit();
$this->success('删除成功');
}catch (Exception $e){
db()->rollback();
$this->error($e->getMessage());
}
}
$this->error(__('You have no permission'));
}
/**
* 批量更新
* @internal
*/
public function multi($ids = "")
{
// 管理员禁止批量操作
$this->error();
}
/**
* 下拉搜索
*/
public function selectpage()
{
$this->dataLimit = 'auth';
$this->dataLimitField = 'id';
return parent::selectpage();
}
}

View File

@ -0,0 +1,149 @@
<?php
namespace app\admin\controller\manystore;
use app\manystore\model\ManystoreAuthRule;
use app\common\controller\Backend;
use fast\Tree;
use think\Cache;
/**
* 规则管理
*
* @icon fa fa-list
* @remark 规则通常对应一个控制器的方法,同时左侧的菜单栏数据也从规则中体现,通常建议通过控制台进行生成规则节点
*/
class Rule extends Backend
{
/**
* @var \app\manystore\model\ManystoreAuthRule
*/
protected $model = null;
protected $rulelist = [];
protected $multiFields = 'ismenu,status';
public function _initialize()
{
parent::_initialize();
$this->model = new ManystoreAuthRule();
// 必须将结果集转换为数组
$ruleList = collection($this->model->order('weigh', 'desc')->order('id', 'asc')->select())->toArray();
foreach ($ruleList as $k => &$v) {
$v['title'] = __($v['title']);
$v['remark'] = __($v['remark']);
}
unset($v);
Tree::instance()->init($ruleList);
$this->rulelist = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0), 'title');
$ruledata = [0 => __('None')];
foreach ($this->rulelist as $k => &$v) {
if (!$v['ismenu']) {
continue;
}
$ruledata[$v['id']] = $v['title'];
}
unset($v);
$this->view->assign('ruledata', $ruledata);
}
/**
* 查看
*/
public function index()
{
if ($this->request->isAjax()) {
$list = $this->rulelist;
$total = count($this->rulelist);
$result = array("total" => $total, "rows" => $list);
return json($result);
}
return $this->view->fetch();
}
/**
* 添加
*/
public function add()
{
if ($this->request->isPost()) {
$this->token();
$params = $this->request->post("row/a", [], 'strip_tags');
if ($params) {
if (!$params['ismenu'] && !$params['pid']) {
$this->error(__('The non-menu rule must have parent'));
}
$result = $this->model->validate()->save($params);
if ($result === false) {
$this->error($this->model->getError());
}
Cache::rm('__menu__');
$this->success();
}
$this->error();
}
return $this->view->fetch();
}
/**
* 编辑
*/
public function edit($ids = null)
{
$row = $this->model->get(['id' => $ids]);
if (!$row) {
$this->error(__('No Results were found'));
}
if ($this->request->isPost()) {
$this->token();
$params = $this->request->post("row/a", [], 'strip_tags');
if ($params) {
if (!$params['ismenu'] && !$params['pid']) {
$this->error(__('The non-menu rule must have parent'));
}
if ($params['pid'] != $row['pid']) {
$childrenIds = Tree::instance()->init(collection(ManystoreAuthRule::select())->toArray())->getChildrenIds($row['id']);
if (in_array($params['pid'], $childrenIds)) {
$this->error(__('Can not change the parent to child'));
}
}
//这里需要针对name做唯一验证
$ruleValidate = \think\Loader::validate('ManystoreAuthRule');
$ruleValidate->rule([
'name' => 'require|format|unique:ManystoreAuthRule,name,' . $row->id,
]);
$result = $row->validate()->save($params);
if ($result === false) {
$this->error($row->getError());
}
Cache::rm('__manystore_menu__');
$this->success();
}
$this->error();
}
$this->view->assign("row", $row);
return $this->view->fetch();
}
/**
* 删除
*/
public function del($ids = "")
{
if ($ids) {
$delIds = [];
foreach (explode(',', $ids) as $k => $v) {
$delIds = array_merge($delIds, Tree::instance()->getChildrenIds($v, true));
}
$delIds = array_unique($delIds);
$count = $this->model->where('id', 'in', $delIds)->delete();
if ($count) {
Cache::rm('__manystore_menu__');
$this->success();
}
}
$this->error();
}
}

View File

@ -0,0 +1,16 @@
<?php
return [
'Id' => 'ID',
'Type' => '类型',
'Params' => '参数',
'Command' => '命令',
'Content' => '返回结果',
'Executetime' => '执行时间',
'Createtime' => '创建时间',
'Updatetime' => '更新时间',
'Execute again' => '再次执行',
'Successed' => '成功',
'Failured' => '失败',
'Status' => '状态'
];

View File

@ -0,0 +1,77 @@
<?php
return [
'Name' => '变量名',
'Tip' => '提示信息',
'Group' => '分组',
'Type' => '类型',
'Title' => '变量标题',
'Value' => '变量值',
'Basic' => '基础配置',
'Email' => '邮件配置',
'Attachment' => '附件配置',
'Dictionary' => '字典配置',
'User' => '会员配置',
'Example' => '示例分组',
'Extend' => '扩展属性',
'String' => '字符',
'Text' => '文本',
'Editor' => '编辑器',
'Number' => '数字',
'Date' => '日期',
'Time' => '时间',
'Datetime' => '日期时间',
'Datetimerange' => '日期时间区间',
'Image' => '图片',
'Images' => '图片(多)',
'File' => '文件',
'Files' => '文件(多)',
'Select' => '列表',
'Selects' => '列表(多选)',
'Switch' => '开关',
'Checkbox' => '复选',
'Radio' => '单选',
'Array' => '数组',
'Array key' => '键名',
'Array value' => '键值',
'City' => '城市地区',
'Selectpage' => '关联表',
'Selectpages' => '关联表(多选)',
'Custom' => '自定义',
'Please select table' => '关联表',
'Selectpage table' => '关联表',
'Selectpage primarykey' => '存储字段',
'Selectpage field' => '显示字段',
'Selectpage conditions' => '筛选条件',
'Field title' => '字段名',
'Field value' => '字段值',
'Content' => '数据列表',
'Rule' => '校验规则',
'Site name' => '站点名称',
'Beian' => '备案号',
'Cdn url' => 'CDN地址',
'Version' => '版本号',
'Timezone' => '时区',
'Forbidden ip' => '禁止IP',
'Languages' => '语言',
'Fixed page' => '后台固定页',
'Category type' => '分类类型',
'Config group' => '配置分组',
'Rule tips' => '校验规则使用请参考Nice-validator文档',
'Extend tips' => '扩展属性支持{id}、{name}、{group}、{title}、{value}、{content}、{rule}替换',
'Mail type' => '邮件发送方式',
'Mail smtp host' => 'SMTP服务器',
'Mail smtp port' => 'SMTP端口',
'Mail smtp user' => 'SMTP用户名',
'Mail smtp password' => 'SMTP密码',
'Mail vertify type' => 'SMTP验证方式',
'Mail from' => '发件人邮箱',
'Site name incorrect' => '网站名称错误',
'Name already exist' => '变量名称已经存在',
'Add new config' => '点击添加新的配置',
'Send a test message' => '发送测试邮件',
'This is a test mail content' => '这是一封来自FastAdmin校验邮件,用于校验邮件配置是否正常!',
'This is a test mail' => '这是一封来自FastAdmin的邮件',
'Please input your email' => '请输入测试接收者邮箱',
'Please input correct email' => '请输入正确的邮箱地址',
];

View File

@ -0,0 +1,6 @@
<?php
return [
'Unique' => '字符唯一标识',
'Name' => '名称'
];

View File

@ -0,0 +1,35 @@
<?php
return [
'Group' => '所属组别',
'Loginfailure' => '登录失败次数',
'Login time' => '最后登录',
'Please input correct username' => '用户名只能由3-12位数字、字母、下划线组合',
'Please input correct password' => '密码长度必须在6-16位之间不能包含空格',
'Please input correct nickname' => '昵称仅支持输入中文、英文字母(大小写)、数字、下划线',
'Please input length nickname' => '昵称请最多填写10个字符',
'Logo' => '商家Logo',
'Name' => '店铺名称',
'Image' => '店铺封面图',
'Images' => '店铺环境图片',
'Address_city' => '城市选择',
'Province' => '省编号',
'City' => '市编号',
'District' => '县区编号',
'Address' => '店铺地址',
'Address_detail' => '店铺详细地址',
'Longitude' => '经度',
'Latitude' => '纬度',
'Yyzzdm' => '营业执照',
'Yyzz_images' => '营业执照照片',
'Tel' => '服务电话',
'Content' => '店铺详情',
'Status' => '审核状态',
'Status 0' => '待审核',
'Status 1' => '审核通过',
'Status 2' => '审核失败',
'Reason' => '审核不通过原因',
'Create_time' => '创建时间',
'Update_time' => '修改时间'
];

View File

@ -0,0 +1,20 @@
<?php
return [
'Toggle all' => '显示全部',
'Condition' => '规则条件',
'Remark' => '备注',
'Icon' => '图标',
'Alert' => '警告',
'Name' => '规则',
'Controller/Action' => '控制器名/方法名',
'Ismenu' => '菜单',
'Search icon' => '搜索图标',
'Toggle menu visible' => '点击切换菜单显示',
'Toggle sub menu' => '点击切换子菜单',
'Menu tips' => '父级菜单无需匹配控制器和方法,子级菜单请使用控制器名',
'Node tips' => '控制器/方法名,如果有目录请使用 目录名/控制器名/方法名',
'The non-menu rule must have parent' => '非菜单规则节点必须有父级',
'Can not change the parent to child' => '父组别不能是它的子组别',
'Name only supports letters, numbers, underscore and slash' => 'URL规则只能是小写字母、数字、下划线和/组成',
];

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
{%addList%}
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,40 @@
<?php
namespace {%controllerNamespace%};
use app\common\controller\ManystoreBase;
/**
* {%tableComment%}
*
* @icon {%iconName%}
*/
class {%controllerName%} extends ManystoreBase
{
/**
* {%modelName%}模型对象
* @var \{%modelNamespace%}\{%modelName%}
*/
protected $model = null;
public function _initialize()
{
parent::_initialize();
$this->model = new \{%modelNamespace%}\{%modelName%};
{%controllerAssignList%}
}
public function import()
{
parent::import();
}
/**
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
*/
{%controllerIndex%}
}

View File

@ -0,0 +1,34 @@
/**
* 查看
*/
public function index()
{
//当前是否为关联查询
$this->relationSearch = {%relationSearch%};
//设置过滤方法
$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();
$list = $this->model
{%relationWithList%}
->where($where)
->order($sort, $order)
->paginate($limit);
foreach ($list as $row) {
{%visibleFieldList%}
{%relationVisibleFieldList%}
}
$result = array("total" => $list->total(), "rows" => $list->items());
return json($result);
}
return $this->view->fetch();
}

View File

@ -0,0 +1,11 @@
<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
{%editList%}
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,6 @@
<div class="checkbox">
{foreach name="{%fieldList%}" item="vo"}
<label for="{%fieldName%}-{$key}"><input id="{%fieldName%}-{$key}" name="{%fieldName%}" type="checkbox" value="{$key}" {in name="key" value="{%selectedValue%}"}checked{/in} /> {$vo}</label>
{/foreach}
</div>

View File

@ -0,0 +1,10 @@
<dl class="fieldlist" data-name="{%fieldName%}">
<dd>
<ins>{:__('{%itemKey%}')}</ins>
<ins>{:__('{%itemValue%}')}</ins>
</dd>
<dd><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
<textarea name="{%fieldName%}" class="form-control hide" cols="30" rows="5">{%fieldValue%}</textarea>
</dl>

View File

@ -0,0 +1,10 @@
<div class="panel-heading">
{:build_heading(null,FALSE)}
<ul class="nav nav-tabs" data-field="{%field%}">
<li class="{:$Think.get.{%field%} === null ? 'active' : ''}"><a href="#t-all" data-value="" data-toggle="tab">{:__('All')}</a></li>
{foreach name="{%fieldName%}List" item="vo"}
<li class="{:$Think.get.{%field%} === (string)$key ? 'active' : ''}"><a href="#t-{$key}" data-value="{$key}" data-toggle="tab">{$vo}</a></li>
{/foreach}
</ul>
</div>

View File

@ -0,0 +1,6 @@
<div class="radio">
{foreach name="{%fieldList%}" item="vo"}
<label for="{%fieldName%}-{$key}"><input id="{%fieldName%}-{$key}" name="{%fieldName%}" type="radio" value="{$key}" {in name="key" value="{%selectedValue%}"}checked{/in} /> {$vo}</label>
{/foreach}
</div>

View File

@ -0,0 +1 @@
<a class="btn btn-success btn-recyclebin btn-dialog {:$auth->check('{%controllerUrl%}/recyclebin')?'':'hide'}" href="{%controllerUrl%}/recyclebin" title="{:__('Recycle bin')}"><i class="fa fa-recycle"></i> {:__('Recycle bin')}</a>

View File

@ -0,0 +1,6 @@
<select {%attrStr%}>
{foreach name="{%fieldList%}" item="vo"}
<option value="{$key}" {in name="key" value="{%selectedValue%}"}selected{/in}>{$vo}</option>
{/foreach}
</select>

View File

@ -0,0 +1,5 @@
<input {%attrStr%} name="{%fieldName%}" type="hidden" value="{%fieldValue%}">
<a href="javascript:;" data-toggle="switcher" class="btn-switcher" data-input-id="c-{%field%}" data-yes="{%fieldYes%}" data-no="{%fieldNo%}" >
<i class="fa fa-toggle-on text-success {%fieldSwitchClass%} fa-2x"></i>
</a>

View File

@ -0,0 +1,34 @@
<div class="panel panel-default panel-intro">
{%headingHtml%}
<div class="panel-body">
<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="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
<a href="javascript:;" class="btn btn-success btn-add {:$auth->check('{%controllerUrl%}/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
<a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('{%controllerUrl%}/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('{%controllerUrl%}/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
<div class="dropdown btn-group {:$auth->check('{%controllerUrl%}/multi')?'':'hide'}">
<a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
<ul class="dropdown-menu text-left" role="menu">
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
</ul>
</div>
{%recyclebinHtml%}
</div>
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
data-operate-edit="{:$auth->check('{%controllerUrl%}/edit')}"
data-operate-del="{:$auth->check('{%controllerUrl%}/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,48 @@
define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
var Controller = {
index: function () {
// 初始化表格参数配置
Table.api.init({
extend: {
index_url: '{%controllerUrl%}/index' + location.search,
add_url: '{%controllerUrl%}/add',
edit_url: '{%controllerUrl%}/edit',
del_url: '{%controllerUrl%}/del',
multi_url: '{%controllerUrl%}/multi',
import_url: '{%controllerUrl%}/import',
table: '{%table%}',
}
});
var table = $("#table");
// 初始化表格
table.bootstrapTable({
url: $.fn.bootstrapTable.defaults.extend.index_url,
pk: '{%pk%}',
sortName: '{%order%}',
columns: [
[
{%javascriptList%}
]
]
});
// 为表格绑定事件
Table.api.bindevent(table);
},{%recyclebinJs%}
add: function () {
Controller.api.bindevent();
},
edit: function () {
Controller.api.bindevent();
},
api: {
bindevent: function () {
Form.api.bindevent($("form[role=form]"));
}
}
};
return Controller;
});

View File

@ -0,0 +1,5 @@
<?php
return [
{%langList%}
];

View File

@ -0,0 +1,8 @@
public function {%methodName%}($value, $data)
{
$value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : '');
$valueArr = explode(',', $value);
$list = $this->{%listMethodName%}();
return implode(',', array_intersect_key($list, array_flip($valueArr)));
}

View File

@ -0,0 +1,6 @@
public function {%methodName%}($value, $data)
{
$value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : '');
return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
}

View File

@ -0,0 +1,8 @@
protected static function init()
{
self::afterInsert(function ($row) {
$pk = $row->getPk();
$row->getQuery()->where($pk, $row[$pk])->update(['{%order%}' => $row[$pk]]);
});
}

View File

@ -0,0 +1,5 @@
public function {%relationMethod%}()
{
return $this->{%relationMode%}('{%relationClassName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}', [], 'LEFT')->setEagerlyType(0);
}

View File

@ -0,0 +1,8 @@
public function {%methodName%}($value, $data)
{
$value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : '');
$valueArr = explode(',', $value);
$list = $this->{%listMethodName%}();
return implode(',', array_intersect_key($list, array_flip($valueArr)));
}

View File

@ -0,0 +1,7 @@
public function {%methodName%}($value, $data)
{
$value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : '');
$list = $this->{%listMethodName%}();
return isset($list[$value]) ? $list[$value] : '';
}

View File

@ -0,0 +1,60 @@
recyclebin: function () {
// 初始化表格参数配置
Table.api.init({
extend: {
'dragsort_url': ''
}
});
var table = $("#table");
// 初始化表格
table.bootstrapTable({
url: '{%controllerUrl%}/recyclebin' + location.search,
pk: 'id',
sortName: 'id',
columns: [
[
{checkbox: true},
{field: 'id', title: __('Id')},{%recyclebinTitleJs%}
{
field: 'deletetime',
title: __('Deletetime'),
operate: 'RANGE',
addclass: 'datetimerange',
formatter: Table.api.formatter.datetime
},
{
field: 'operate',
width: '130px',
title: __('Operate'),
table: table,
events: Table.api.events.operate,
buttons: [
{
name: 'Restore',
text: __('Restore'),
classname: 'btn btn-xs btn-info btn-ajax btn-restoreit',
icon: 'fa fa-rotate-left',
url: '{%controllerUrl%}/restore',
refresh: true
},
{
name: 'Destroy',
text: __('Destroy'),
classname: 'btn btn-xs btn-danger btn-ajax btn-destroyit',
icon: 'fa fa-times',
url: '{%controllerUrl%}/destroy',
refresh: true
}
],
formatter: Table.api.formatter.operate
}
]
]
});
// 为表格绑定事件
Table.api.bindevent(table);
},

View File

@ -0,0 +1,7 @@
public function {%methodName%}($value, $data)
{
$value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : '');
$list = $this->{%listMethodName%}();
return isset($list[$value]) ? $list[$value] : '';
}

View File

@ -0,0 +1,40 @@
<?php
namespace {%modelNamespace%};
use think\Model;
{%sofeDeleteClassPath%}
class {%modelName%} extends Model
{
{%softDelete%}
{%modelConnection%}
// 表名
protected ${%modelTableType%} = '{%modelTableTypeName%}';
// 自动写入时间戳字段
protected $autoWriteTimestamp = {%modelAutoWriteTimestamp%};
// 定义时间戳字段名
protected $createTime = {%createTime%};
protected $updateTime = {%updateTime%};
protected $deleteTime = {%deleteTime%};
// 追加属性
protected $append = [
{%appendAttrList%}
];
{%modelInit%}
{%getEnumList%}
{%getAttrList%}
{%setAttrList%}
{%relationMethodList%}
}

View File

@ -0,0 +1,25 @@
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<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">
{:build_toolbar('refresh')}
<a class="btn btn-info btn-multi btn-disabled disabled {:$auth->check('{%controllerUrl%}/restore')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/restore" data-action="restore"><i class="fa fa-rotate-left"></i> {:__('Restore')}</a>
<a class="btn btn-danger btn-multi btn-disabled disabled {:$auth->check('{%controllerUrl%}/destroy')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/destroy" data-action="destroy"><i class="fa fa-times"></i> {:__('Destroy')}</a>
<a class="btn btn-success btn-restoreall {:$auth->check('{%controllerUrl%}/restore')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/restore" title="{:__('Restore all')}"><i class="fa fa-rotate-left"></i> {:__('Restore all')}</a>
<a class="btn btn-danger btn-destroyall {:$auth->check('{%controllerUrl%}/destroy')?'':'hide'}" href="javascript:;" data-url="{%controllerUrl%}/destroy" title="{:__('Destroy all')}"><i class="fa fa-times"></i> {:__('Destroy all')}</a>
</div>
<table id="table" class="table table-striped table-bordered table-hover"
data-operate-restore="{:$auth->check('{%controllerUrl%}/restore')}"
data-operate-destroy="{:$auth->check('{%controllerUrl%}/destroy')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,12 @@
<?php
namespace {%modelNamespace%};
use think\Model;
class {%relationName%} extends Model
{
// 表名
protected ${%relationTableType%} = '{%relationTableTypeName%}';
}

View File

@ -0,0 +1,27 @@
<?php
namespace {%validateNamespace%};
use think\Validate;
class {%validateName%} extends Validate
{
/**
* 验证规则
*/
protected $rule = [
];
/**
* 提示消息
*/
protected $message = [
];
/**
* 验证场景
*/
protected $scene = [
'add' => [],
'edit' => [],
];
}

View File

@ -0,0 +1,331 @@
<?php
namespace app\admin\manystore_command;
use app\manystore\model\ManystoreAuthRule;
use ReflectionClass;
use ReflectionMethod;
use think\Cache;
use think\Config;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\Exception;
use think\Loader;
class Menu extends Command
{
protected $model = null;
protected function configure()
{
$this
->setName('menu')
->addOption('controller', 'c', Option::VALUE_REQUIRED | Option::VALUE_IS_ARRAY, 'controller name,use \'all-controller\' when build all menu', null)
->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete the specified menu', '')
->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force delete menu,without tips', null)
->addOption('equal', 'e', Option::VALUE_OPTIONAL, 'the controller must be equal', null)
->setDescription('Build auth menu from controller');
//要执行的controller必须一样不适用模糊查询
}
protected function execute(Input $input, Output $output)
{
$this->model = new ManystoreAuthRule();
$manystorePath = dirname(__DIR__) . DS;
$manystorePathArray = explode(DS,$manystorePath);
$manystorePathArray[count($manystorePathArray) - 2] = 'manystore';
$manystorePath = implode(DS,$manystorePathArray);
//控制器名
$controller = $input->getOption('controller') ?: '';
if (!$controller) {
throw new Exception("please input controller name");
}
$force = $input->getOption('force');
//是否为删除模式
$delete = $input->getOption('delete');
//是否控制器完全匹配
$equal = $input->getOption('equal');
if ($delete) {
if (in_array('all-controller', $controller)) {
throw new Exception("could not delete all menu");
}
$ids = [];
$list = $this->model->where(function ($query) use ($controller, $equal) {
foreach ($controller as $index => $item) {
if (stripos($item, '_') !== false) {
$item = Loader::parseName($item, 1);
}
if (stripos($item, '/') !== false) {
$controllerArr = explode('/', $item);
end($controllerArr);
$key = key($controllerArr);
$controllerArr[$key] = Loader::parseName($controllerArr[$key]);
} else {
$controllerArr = [Loader::parseName($item)];
}
$item = str_replace('_', '\_', implode('/', $controllerArr));
if ($equal) {
$query->whereOr('name', 'eq', $item);
} else {
$query->whereOr('name', 'like', strtolower($item) . "%");
}
}
})->select();
foreach ($list as $k => $v) {
$output->warning($v->name);
$ids[] = $v->id;
}
if (!$ids) {
throw new Exception("There is no menu to delete");
}
if (!$force) {
$output->info("Are you sure you want to delete all those menu? Type 'yes' to continue: ");
$line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r'));
if (trim($line) != 'yes') {
throw new Exception("Operation is aborted!");
}
}
ManystoreAuthRule::destroy($ids);
Cache::rm("__manystore_menu__");
$output->info("Delete Successed");
return;
}
if (!in_array('all-controller', $controller)) {
foreach ($controller as $index => $item) {
if (stripos($item, '_') !== false) {
$item = Loader::parseName($item, 1);
}
if (stripos($item, '/') !== false) {
$controllerArr = explode('/', $item);
end($controllerArr);
$key = key($controllerArr);
$controllerArr[$key] = ucfirst($controllerArr[$key]);
} else {
$controllerArr = [ucfirst($item)];
}
$manystorePath = $manystorePath . 'controller' . DS . implode(DS, $controllerArr) . '.php';
if (!is_file($manystorePath)) {
$output->error("controller not found");
return;
}
$this->importRule($item);
}
} else {
$authRuleList = AuthRule::select();
//生成权限规则备份文件
file_put_contents(RUNTIME_PATH . 'authrule.json', json_encode(collection($authRuleList)->toArray()));
$this->model->where('id', '>', 0)->delete();
$controllerDir = $manystorePath . 'controller' . DS;
// 扫描新的节点信息并导入
$treelist = $this->import($this->scandir($controllerDir));
}
Cache::rm("__manystore_menu__");
$output->info("Build Successed!");
}
/**
* 递归扫描文件夹
* @param string $dir
* @return array
*/
public function scandir($dir)
{
$result = [];
$cdir = scandir($dir);
foreach ($cdir as $value) {
if (!in_array($value, array(".", ".."))) {
if (is_dir($dir . DS . $value)) {
$result[$value] = $this->scandir($dir . DS . $value);
} else {
$result[] = $value;
}
}
}
return $result;
}
/**
* 导入规则节点
* @param array $dirarr
* @param array $parentdir
* @return array
*/
public function import($dirarr, $parentdir = [])
{
$menuarr = [];
foreach ($dirarr as $k => $v) {
if (is_array($v)) {
//当前是文件夹
$nowparentdir = array_merge($parentdir, [$k]);
$this->import($v, $nowparentdir);
} else {
//只匹配PHP文件
if (!preg_match('/^(\w+)\.php$/', $v, $matchone)) {
continue;
}
//导入文件
$controller = ($parentdir ? implode('/', $parentdir) . '/' : '') . $matchone[1];
$this->importRule($controller);
}
}
return $menuarr;
}
protected function importRule($controller)
{
$controller = str_replace('\\', '/', $controller);
if (stripos($controller, '/') !== false) {
$controllerArr = explode('/', $controller);
end($controllerArr);
$key = key($controllerArr);
$controllerArr[$key] = ucfirst($controllerArr[$key]);
} else {
$key = 0;
$controllerArr = [ucfirst($controller)];
}
$classSuffix = Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : '';
$className = "\\app\\admin\\controller\\manystore\\" . implode("\\", $controllerArr) . $classSuffix;
$pathArr = $controllerArr;
array_unshift($pathArr, '', 'application', 'manystore', 'controller');
$classFile = ROOT_PATH . implode(DS, $pathArr) . $classSuffix . ".php";
$classContent = file_get_contents($classFile);
$uniqueName = uniqid("FastAdmin") . $classSuffix;
$classContent = str_replace("class " . $controllerArr[$key] . $classSuffix . " ", 'class ' . $uniqueName . ' ', $classContent);
$classContent = preg_replace("/namespace\s(.*);/", 'namespace ' . __NAMESPACE__ . ";", $classContent);
//临时的类文件
$tempClassFile = __DIR__ . DS . $uniqueName . ".php";
file_put_contents($tempClassFile, $classContent);
$className = "\\app\\admin\\manystore_command\\" . $uniqueName;
//删除临时文件
register_shutdown_function(function () use ($tempClassFile) {
if ($tempClassFile) {
//删除临时文件
@unlink($tempClassFile);
}
});
//反射机制调用类的注释和方法名
$reflector = new ReflectionClass($className);
//只匹配公共的方法
$methods = $reflector->getMethods(ReflectionMethod::IS_PUBLIC);
$classComment = $reflector->getDocComment();
//判断是否有启用软删除
$softDeleteMethods = ['destroy', 'restore', 'recyclebin'];
$withSofeDelete = false;
$modelRegexArr = ["/\\\$this\->model\s*=\s*model\(['|\"](\w+)['|\"]\);/", "/\\\$this\->model\s*=\s*new\s+([a-zA-Z\\\]+);/"];
$modelRegex = preg_match($modelRegexArr[0], $classContent) ? $modelRegexArr[0] : $modelRegexArr[1];
preg_match_all($modelRegex, $classContent, $matches);
if (isset($matches[1]) && isset($matches[1][0]) && $matches[1][0]) {
\think\Request::instance()->module('admin');
$model = model($matches[1][0]);
if (in_array('trashed', get_class_methods($model))) {
$withSofeDelete = true;
}
}
//忽略的类
if (stripos($classComment, "@internal") !== false) {
return;
}
preg_match_all('#(@.*?)\n#s', $classComment, $annotations);
$controllerIcon = 'fa fa-circle-o';
$controllerRemark = '';
//判断注释中是否设置了icon值
if (isset($annotations[1])) {
foreach ($annotations[1] as $tag) {
if (stripos($tag, '@icon') !== false) {
$controllerIcon = substr($tag, stripos($tag, ' ') + 1);
}
if (stripos($tag, '@remark') !== false) {
$controllerRemark = substr($tag, stripos($tag, ' ') + 1);
}
}
}
//过滤掉其它字符
$controllerTitle = trim(preg_replace(array('/^\/\*\*(.*)[\n\r\t]/u', '/[\s]+\*\//u', '/\*\s@(.*)/u', '/[\s|\*]+/u'), '', $classComment));
//导入中文语言包
\think\Lang::load(dirname(__DIR__) . DS . 'lang/zh-cn.php');
//先导入菜单的数据
$pid = 0;
foreach ($controllerArr as $k => $v) {
$key = $k + 1;
//驼峰转下划线
$controllerNameArr = array_slice($controllerArr, 0, $key);
foreach ($controllerNameArr as &$val) {
$val = strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $val), "_"));
}
unset($val);
$name = implode('/', $controllerNameArr);
$title = (!isset($controllerArr[$key]) ? $controllerTitle : '');
$icon = (!isset($controllerArr[$key]) ? $controllerIcon : 'fa fa-list');
$remark = (!isset($controllerArr[$key]) ? $controllerRemark : '');
$title = $title ? $title : $v;
$rulemodel = $this->model->get(['name' => $name]);
if (!$rulemodel) {
$this->model
->data(['pid' => $pid, 'name' => $name, 'title' => $title, 'icon' => $icon, 'remark' => $remark, 'ismenu' => 1, 'status' => 'normal'])
->isUpdate(false)
->save();
$pid = $this->model->id;
} else {
$pid = $rulemodel->id;
}
}
$ruleArr = [];
foreach ($methods as $m => $n) {
//过滤特殊的类
if (substr($n->name, 0, 2) == '__' || $n->name == '_initialize') {
continue;
}
//未启用软删除时过滤相关方法
if (!$withSofeDelete && in_array($n->name, $softDeleteMethods)) {
continue;
}
//只匹配符合的方法
if (!preg_match('/^(\w+)' . Config::get('action_suffix') . '/', $n->name, $matchtwo)) {
unset($methods[$m]);
continue;
}
$comment = $reflector->getMethod($n->name)->getDocComment();
//忽略的方法
if (stripos($comment, "@internal") !== false) {
continue;
}
//过滤掉其它字符
$comment = preg_replace(array('/^\/\*\*(.*)[\n\r\t]/u', '/[\s]+\*\//u', '/\*\s@(.*)/u', '/[\s|\*]+/u'), '', $comment);
$title = $comment ? $comment : ucfirst($n->name);
//获取主键作为AuthRule更新依据
$id = $this->getAuthRulePK($name . "/" . strtolower($n->name));
$ruleArr[] = array('id' => $id, 'pid' => $pid, 'name' => $name . "/" . strtolower($n->name), 'icon' => 'fa fa-circle-o', 'title' => $title, 'ismenu' => 0, 'status' => 'normal');
}
$this->model->isUpdate(false)->saveAll($ruleArr);
}
//获取主键
protected function getAuthRulePK($name)
{
if (!empty($name)) {
$id = $this->model
->where('name', $name)
->value('id');
return $id ? $id : null;
}
}
}

View File

@ -0,0 +1,162 @@
<?php
namespace app\admin\manystore_command;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\Exception;
class Min extends Command
{
/**
* 路径和文件名配置
*/
protected $options = [
'cssBaseUrl' => 'public/assets/css/',
'cssBaseName' => '{module}',
'jsBaseUrl' => 'public/assets/js/',
'jsBaseName' => 'require-{module}',
];
protected function configure()
{
$this
->setName('min')
->addOption('module', 'm', Option::VALUE_REQUIRED, 'module name(frontend or manystore),use \'all\' when build all modules', null)
->addOption('resource', 'r', Option::VALUE_REQUIRED, 'resource name(js or css),use \'all\' when build all resources', null)
->addOption('optimize', 'o', Option::VALUE_OPTIONAL, 'optimize type(uglify|closure|none)', 'none')
->setDescription('Compress js and css file');
}
protected function execute(Input $input, Output $output)
{
$module = $input->getOption('module') ?: '';
$resource = $input->getOption('resource') ?: '';
$optimize = $input->getOption('optimize') ?: 'none';
if (!$module || !in_array($module, ['frontend', 'manystore', 'all'])) {
throw new Exception('Please input correct module name');
}
if (!$resource || !in_array($resource, ['js', 'css', 'all'])) {
throw new Exception('Please input correct resource name');
}
$moduleArr = $module == 'all' ? ['frontend', 'manystore'] : [$module];
$resourceArr = $resource == 'all' ? ['js', 'css'] : [$resource];
$minPath = __DIR__ . DS . 'Min' . DS;
$publicPath = ROOT_PATH . 'public' . DS;
$tempFile = $minPath . 'temp.js';
$nodeExec = '';
if (!$nodeExec) {
if (IS_WIN) {
// Winsows下请手动配置配置该值,一般将该值配置为 '"C:\Program Files\nodejs\node.exe"'除非你的Node安装路径有变更
$nodeExec = 'C:\Program Files\nodejs\node.exe';
if (file_exists($nodeExec)) {
$nodeExec = '"' . $nodeExec . '"';
} else {
// 如果 '"C:\Program Files\nodejs\node.exe"' 不存在可能是node安装路径有变更
// 但安装node会自动配置环境变量直接执行 '"node.exe"' 提高第一次使用压缩打包的成功率
$nodeExec = '"node.exe"';
}
} else {
try {
$nodeExec = exec("which node");
if (!$nodeExec) {
throw new Exception("node environment not found!please install node first!");
}
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
}
foreach ($moduleArr as $mod) {
foreach ($resourceArr as $res) {
$data = [
'publicPath' => $publicPath,
'jsBaseName' => str_replace('{module}', $mod, $this->options['jsBaseName']),
'jsBaseUrl' => $this->options['jsBaseUrl'],
'cssBaseName' => str_replace('{module}', $mod, $this->options['cssBaseName']),
'cssBaseUrl' => $this->options['cssBaseUrl'],
'jsBasePath' => str_replace(DS, '/', ROOT_PATH . $this->options['jsBaseUrl']),
'cssBasePath' => str_replace(DS, '/', ROOT_PATH . $this->options['cssBaseUrl']),
'optimize' => $optimize,
'ds' => DS,
];
//源文件
$from = $data["{$res}BasePath"] . $data["{$res}BaseName"] . '.' . $res;
if (!is_file($from)) {
$output->error("{$res} source file not found!file:{$from}");
continue;
}
if ($res == "js") {
$content = file_get_contents($from);
preg_match("/require\.config\(\{[\r\n]?[\n]?+(.*?)[\r\n]?[\n]?}\);/is", $content, $matches);
if (!isset($matches[1])) {
$output->error("js config not found!");
continue;
}
$config = preg_replace("/(urlArgs|baseUrl):(.*)\n/", '', $matches[1]);
$data['config'] = $config;
}
// 生成压缩文件
$this->writeToFile($res, $data, $tempFile);
$output->info("Compress " . $data["{$res}BaseName"] . ".{$res}");
// 执行压缩
$command = "{$nodeExec} \"{$minPath}r.js\" -o \"{$tempFile}\" >> \"{$minPath}node.log\"";
if ($output->isDebug()) {
$output->warning($command);
}
echo exec($command);
}
}
if (!$output->isDebug()) {
@unlink($tempFile);
}
$output->info("Build Successed!");
}
/**
* 写入到文件
* @param string $name
* @param array $data
* @param string $pathname
* @return mixed
*/
protected function writeToFile($name, $data, $pathname)
{
$search = $replace = [];
foreach ($data as $k => $v) {
$search[] = "{%{$k}%}";
$replace[] = $v;
}
$stub = file_get_contents($this->getStub($name));
$content = str_replace($search, $replace, $stub);
if (!is_dir(dirname($pathname))) {
mkdir(strtolower(dirname($pathname)), 0755, true);
}
return file_put_contents($pathname, $content);
}
/**
* 获取基础模板
* @param string $name
* @return string
*/
protected function getStub($name)
{
return __DIR__ . DS . 'Min' . DS . 'stubs' . DS . $name . '.stub';
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,6 @@
({
cssIn: "{%cssBasePath%}{%cssBaseName%}.css",
out: "{%cssBasePath%}{%cssBaseName%}.min.css",
optimizeCss: "default",
optimize: "{%optimize%}"
})

View File

@ -0,0 +1,11 @@
({
{%config%}
,
optimizeCss: "standard",
optimize: "{%optimize%}", //可使用uglify|closure|none
preserveLicenseComments: false,
removeCombined: false,
baseUrl: "{%jsBasePath%}", //JS文件所在的基础目录
name: "{%jsBaseName%}", //来源文件,不包含后缀
out: "{%jsBasePath%}{%jsBaseName%}.min.js" //目标文件
});

View File

@ -0,0 +1,59 @@
<?php
namespace app\admin\model;
use think\Model;
class ManystoreCommand extends Model
{
// 表名
protected $name = 'manystore_command';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
// 追加属性
protected $append = [
'executetime_text',
'type_text',
'status_text'
];
public function getStatusList()
{
return ['successed' => __('Successed'), 'failured' => __('Failured')];
}
public function getExecutetimeTextAttr($value, $data)
{
$value = $value ? $value : $data['executetime'];
return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
}
public function getTypeTextAttr($value, $data)
{
$value = $value ? $value : $data['type'];
$list = ['crud' => '一键生成CRUD', 'menu' => '一键生成菜单', 'min' => '一键压缩打包', 'api' => '一键生成文档'];
return isset($list[$value]) ? $list[$value] : '';
}
public function getStatusTextAttr($value, $data)
{
$value = $value ? $value : $data['status'];
$list = $this->getStatusList();
return isset($list[$value]) ? $list[$value] : '';
}
protected function setExecutetimeAttr($value)
{
return $value && !is_numeric($value) ? strtotime($value) : $value;
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace app\admin\validate;
use think\Validate;
class ManystoreAuthRule extends Validate
{
/**
* 正则
*/
protected $regex = ['format' => '[a-z0-9_\/]+'];
/**
* 验证规则
*/
protected $rule = [
'name' => 'require|format|unique:ManystoreAuthRule',
'title' => 'require',
];
/**
* 提示消息
*/
protected $message = [
'name.format' => 'URL规则只能是小写字母、数字、下划线和/组成'
];
/**
* 字段描述
*/
protected $field = [
];
/**
* 验证场景
*/
protected $scene = [
];
public function __construct(array $rules = [], $message = [], $field = [])
{
$this->field = [
'name' => __('Name'),
'title' => __('Title'),
];
$this->message['name.format'] = __('Name only supports letters, numbers, underscore and slash');
parent::__construct($rules, $message, $field);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace app\admin\validate\manystore;
use think\Validate;
class Command extends Validate
{
/**
* 验证规则
*/
protected $rule = [
];
/**
* 提示消息
*/
protected $message = [
];
/**
* 验证场景
*/
protected $scene = [
'add' => [],
'edit' => [],
];
}

View File

@ -0,0 +1,55 @@
<?php
namespace app\admin\validate\manystore;
use think\Validate;
class Index extends Validate
{
/**
* 验证规则
*/
protected $rule = [
'username' => 'require|regex:\w{3,12}|unique:manystore',
'nickname' => 'require',
'password' => 'require|regex:\S{32}',
'email' => 'require|email|unique:manystore,email',
];
/**
* 提示消息
*/
protected $message = [
];
/**
* 字段描述
*/
protected $field = [
];
/**
* 验证场景
*/
protected $scene = [
'add' => ['username', 'email', 'nickname', 'password'],
'edit' => ['email', 'nickname', 'password'],
];
public function __construct(array $rules = [], $message = [], $field = [])
{
$this->field = [
'username' => __('Username'),
'nickname' => __('Nickname'),
'password' => __('Password'),
'email' => __('Email'),
];
$this->message = array_merge($this->message, [
'username.regex' => __('Please input correct username'),
'password.regex' => __('Please input correct password')
]);
parent::__construct($rules, $message, $field);
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace app\admin\validate\manystore;
use think\Validate;
class Rule extends Validate
{
/**
* 正则
*/
protected $regex = ['format' => '[a-z0-9_\/]+'];
/**
* 验证规则
*/
protected $rule = [
'name' => 'require|format|unique:Rule',
'title' => 'require',
];
/**
* 提示消息
*/
protected $message = [
'name.format' => 'URL规则只能是小写字母、数字、下划线和/组成'
];
/**
* 字段描述
*/
protected $field = [
];
/**
* 验证场景
*/
protected $scene = [
];
public function __construct(array $rules = [], $message = [], $field = [])
{
$this->field = [
'name' => __('Name'),
'title' => __('Title'),
];
$this->message['name.format'] = __('Name only supports letters, numbers, underscore and slash');
parent::__construct($rules, $message, $field);
}
}

View File

@ -0,0 +1,326 @@
<style>
.relation-item {margin-top:10px;}
legend {padding-bottom:5px;font-size:14px;font-weight:600;}
label {font-weight:normal;}
.form-control{padding:6px 8px;}
#extend-zone .col-xs-2 {margin-top:10px;padding-right:0;}
#extend-zone .col-xs-2:nth-child(6n+0) {padding-right:15px;}
</style>
<div class="panel panel-default panel-intro">
<div class="panel-heading">
<ul class="nav nav-tabs">
<li class="active"><a href="#crud" data-toggle="tab">{:__('一键生成CRUD')}</a></li>
<li><a href="#menu" data-toggle="tab">{:__('一键生成菜单')}</a></li>
<li><a href="#min" data-toggle="tab">{:__('一键压缩打包')}</a></li>
</ul>
</div>
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="crud">
<div class="row">
<div class="col-xs-12">
<form role="form">
<input type="hidden" name="commandtype" value="crud" />
<div class="form-group">
<div class="row">
<div class="col-xs-3">
<input checked="" name="isrelation" type="hidden" value="0">
<label class="control-label" data-toggle="tooltip" title="当前只支持生成1对1关联模型,选中后请配置关联表和字段">
<input name="isrelation" type="checkbox" value="1">
关联模型
</label>
</div>
<div class="col-xs-3">
<input checked="" name="local" type="hidden" value="1">
<label class="control-label" data-toggle="tooltip" title="默认模型生成在application/manystore/model目录下,选中后将生成在application/common/model目录下">
<input name="local" type="checkbox" value="0"> 全局模型类
</label>
</div>
<div class="col-xs-3">
<input checked="" name="delete" type="hidden" value="0">
<label class="control-label" data-toggle="tooltip" title="删除CRUD生成的相关文件">
<input name="delete" type="checkbox" value="1"> 删除模式
</label>
</div>
<div class="col-xs-3">
<input checked="" name="force" type="hidden" value="0">
<label class="control-label" data-toggle="tooltip" title="选中后,如果已经存在同名文件将被覆盖。如果是删除将不再提醒">
<input name="force" type="checkbox" value="1">
强制覆盖模式
</label>
</div>
<!--
<div class="col-xs-3">
<input checked="" name="menu" type="hidden" value="0">
<label class="control-label" data-toggle="tooltip" title="选中后,将同时生成后台菜单规则">
<input name="menu" type="checkbox" value="1">
生成菜单
</label>
</div>
-->
</div>
</div>
<div class="form-group">
<legend>主表设置</legend>
<div class="row">
<div class="col-xs-3">
<label>请选择主表</label>
{:build_select('table',$tableList,null,['class'=>'form-control selectpicker']);}
</div>
<div class="col-xs-3">
<label>自定义控制器名</label>
<input type="text" class="form-control" name="controller" data-toggle="tooltip" title="默认根据表名自动生成,如果需要放在二级目录请手动填写" placeholder="支持目录层级,以/分隔">
</div>
<div class="col-xs-3">
<label>自定义模型名</label>
<input type="text" class="form-control" name="model" data-toggle="tooltip" title="默认根据表名自动生成" placeholder="不支持目录层级">
</div>
<div class="col-xs-3">
<label>请选择显示字段(默认全部)</label>
<select name="fields[]" id="fields" multiple style="height:30px;" class="form-control selectpicker"></select>
</div>
</div>
</div>
<div class="form-group hide" id="relation-zone">
<legend>关联表设置</legend>
<div class="row" style="margin-top:15px;">
<div class="col-xs-12">
<a href="javascript:;" class="btn btn-primary btn-sm btn-newrelation" data-index="1">追加关联模型</a>
</div>
</div>
</div>
<hr>
<div class="form-group" id="extend-zone">
<legend>字段识别设置 <span style="font-size:12px;font-weight: normal;">(与之匹配的字段都将生成相应组件)</span></legend>
<div class="row">
<div class="col-xs-2">
<label>复选框后缀</label>
<input type="text" class="form-control" name="setcheckboxsuffix" placeholder="默认为set类型" />
</div>
<div class="col-xs-2">
<label>单选框后缀</label>
<input type="text" class="form-control" name="enumradiosuffix" placeholder="默认为enum类型" />
</div>
<div class="col-xs-2">
<label>图片类型后缀</label>
<input type="text" class="form-control" name="imagefield" placeholder="默认为image,images,avatar,avatars" />
</div>
<div class="col-xs-2">
<label>文件类型后缀</label>
<input type="text" class="form-control" name="filefield" placeholder="默认为file,files" />
</div>
<div class="col-xs-2">
<label>日期时间后缀</label>
<input type="text" class="form-control" name="intdatesuffix" placeholder="默认为time" />
</div>
<div class="col-xs-2">
<label>开关后缀</label>
<input type="text" class="form-control" name="switchsuffix" placeholder="默认为switch" />
</div>
<div class="col-xs-2">
<label>城市选择后缀</label>
<input type="text" class="form-control" name="citysuffix" placeholder="默认为city" />
</div>
<div class="col-xs-2">
<label>动态下拉后缀(单)</label>
<input type="text" class="form-control" name="selectpagesuffix" placeholder="默认为_id" />
</div>
<div class="col-xs-2">
<label>动态下拉后缀(多)</label>
<input type="text" class="form-control" name="selectpagessuffix" placeholder="默认为_ids" />
</div>
<div class="col-xs-2">
<label>忽略的字段</label>
<input type="text" class="form-control" name="ignorefields" placeholder="默认无" />
</div>
<div class="col-xs-2">
<label>排序字段</label>
<input type="text" class="form-control" name="sortfield" placeholder="默认为weigh" />
</div>
<div class="col-xs-2">
<label>富文本编辑器</label>
<input type="text" class="form-control" name="editorsuffix" placeholder="默认为content" />
</div>
<div class="col-xs-2">
<label>选项卡过滤字段</label>
<input type="text" class="form-control" name="headingfilterfield" placeholder="默认为status" />
</div>
</div>
</div>
<div class="form-group">
<legend>生成命令行</legend>
<textarea class="form-control" data-toggle="tooltip" title="如果在线执行命令失败,可以将命令复制到命令行进行执行" rel="command" rows="1" placeholder="请点击生成命令行"></textarea>
</div>
<div class="form-group">
<legend>返回结果</legend>
<textarea class="form-control" rel="result" rows="5" placeholder="请点击立即执行"></textarea>
</div>
<div class="form-group">
<button type="button" class="btn btn-info btn-embossed btn-command">{:__('生成命令行')}</button>
<button type="button" class="btn btn-success btn-embossed btn-execute">{:__('立即执行')}</button>
</div>
</form>
</div>
</div>
</div>
<div class="tab-pane fade" id="menu">
<div class="row">
<div class="col-xs-12">
<form role="form">
<input type="hidden" name="commandtype" value="menu" />
<div class="form-group">
<div class="row">
<div class="col-xs-3">
<input checked="" name="allcontroller" type="hidden" value="0">
<label class="control-label">
<input name="allcontroller" data-toggle="collapse" data-target="#controller" type="checkbox" value="1"> 一键生成全部控制器
</label>
</div>
<div class="col-xs-3">
<input checked="" name="delete" type="hidden" value="0">
<label class="control-label">
<input name="delete" type="checkbox" value="1"> 删除模式
</label>
</div>
<div class="col-xs-3">
<input checked="" name="force" type="hidden" value="0">
<label class="control-label">
<input name="force" type="checkbox" value="1"> 强制覆盖模式
</label>
</div>
</div>
</div>
<div class="form-group in" id="controller">
<legend>控制器设置</legend>
<div class="row" style="margin-top:15px;">
<div class="col-xs-12">
<input type="text" name="controllerfile" class="form-control selectpage" style="width:720px;" data-source="manystore/command/get_controller_list" data-multiple="true" name="controller" placeholder="请选择控制器" />
</div>
</div>
</div>
<div class="form-group">
<legend>生成命令行</legend>
<textarea class="form-control" rel="command" rows="1" placeholder="请点击生成命令行"></textarea>
</div>
<div class="form-group">
<legend>返回结果</legend>
<textarea class="form-control" rel="result" rows="5" placeholder="请点击立即执行"></textarea>
</div>
<div class="form-group">
<button type="button" class="btn btn-info btn-embossed btn-command">{:__('生成命令行')}</button>
<button type="button" class="btn btn-success btn-embossed btn-execute">{:__('立即执行')}</button>
</div>
</form>
</div>
</div>
</div>
<div class="tab-pane fade" id="min">
<div class="row">
<div class="col-xs-12">
<form role="form">
<input type="hidden" name="commandtype" value="min" />
<div class="form-group">
<legend>基础设置</legend>
<div class="row">
<div class="col-xs-3">
<label>请选择压缩模块</label>
<select name="module" class="form-control selectpicker">
<option value="manystore">商家Manystore</option>
</select>
</div>
<div class="col-xs-3">
<label>请选择压缩资源</label>
<select name="resource" class="form-control selectpicker">
<option value="all" selected>全部</option>
<option value="js">JS</option>
<option value="css">CSS</option>
</select>
</div>
<div class="col-xs-3">
<label>请选择压缩模式</label>
<select name="optimize" class="form-control selectpicker">
<option value=""></option>
<option value="uglify">uglify</option>
<option value="closure">closure</option>
</select>
</div>
</div>
</div>
<div class="form-group in">
<legend>控制器设置</legend>
<div class="row" style="margin-top:15px;">
<div class="col-xs-12">
</div>
</div>
</div>
<div class="form-group">
<legend>生成命令行</legend>
<textarea class="form-control" rel="command" rows="1" placeholder="请点击生成命令行"></textarea>
</div>
<div class="form-group">
<legend>返回结果</legend>
<textarea class="form-control" rel="result" rows="5" placeholder="请点击立即执行"></textarea>
</div>
<div class="form-group">
<button type="button" class="btn btn-info btn-embossed btn-command">{:__('生成命令行')}</button>
<button type="button" class="btn btn-success btn-embossed btn-execute">{:__('立即执行')}</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script id="relationtpl" type="text/html">
<div class="row relation-item">
<div class="col-xs-2">
<label>请选择关联表</label>
<select name="relation[<%=index%>][relation]" class="form-control relationtable"></select>
</div>
<div class="col-xs-2">
<label>请选择关联类型</label>
<select name="relation[<%=index%>][relationmode]" class="form-control relationmode"></select>
</div>
<div class="col-xs-2">
<label>关联外键</label>
<select name="relation[<%=index%>][relationforeignkey]" class="form-control relationforeignkey"></select>
</div>
<div class="col-xs-2">
<label>关联主键</label>
<select name="relation[<%=index%>][relationprimarykey]" class="form-control relationprimarykey"></select>
</div>
<div class="col-xs-2">
<label>请选择显示字段</label>
<select name="relation[<%=index%>][relationfields][]" multiple class="form-control relationfields"></select>
</div>
<div class="col-xs-2">
<label>&nbsp;</label>
<a href="javascript:;" class="btn btn-danger btn-block btn-removerelation">移除</a>
</div>
</div>
</script>

View File

@ -0,0 +1,42 @@
<table class="table table-striped">
<thead>
<tr>
<th>{:__('Title')}</th>
<th>{:__('Content')}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{:__('Type')}</td>
<td>{$row.type}({$row.type_text})</td>
</tr>
<tr>
<td>{:__('Params')}</td>
<td>{$row.params}</td>
</tr>
<tr>
<td>{:__('Command')}</td>
<td>{$row.command}</td>
</tr>
<tr>
<td>{:__('Content')}</td>
<td>
<textarea class="form-control" name="" id="" cols="60" rows="10">{$row.content}</textarea>
</td>
</tr>
<tr>
<td>{:__('Executetime')}</td>
<td>{$row.executetime|datetime}</td>
</tr>
<tr>
<td>{:__('Status')}</td>
<td>{$row.status_text}</td>
</tr>
</tbody>
</table>
<div class="hide layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="reset" class="btn btn-primary btn-embossed btn-close" onclick="Layer.closeAll();">{:__('Close')}</button>
</div>
</div>

View File

@ -0,0 +1,25 @@
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<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="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
<a href="javascript:;" class="btn btn-success btn-add {:$auth->check('manystore_command/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('manystore_command/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
</div>
<table id="table" class="table table-striped table-bordered table-hover"
data-operate-detail="{:$auth->check('manystore_command/detail')}"
data-operate-execute="{:$auth->check('manystore_command/execute')}"
data-operate-del="{:$auth->check('manystore_command/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,133 @@
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
{:token()}
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Group')}:</label>
<div class="col-xs-12 col-sm-8">
<select name="row[group]" class="form-control selectpicker" data-rule="required">
{foreach name="groupList" item="vo"}
<option value="{$key}" >{$vo}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
<div class="col-xs-12 col-sm-8">
<select name="row[type]" id="c-type" class="form-control selectpicker">
{foreach name="typeList" item="vo"}
<option value="{$key}" {in name="key" value="string" }selected{/in}>{$vo}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group">
<label for="name" class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="name" name="row[name]" value="" data-rule="required; length(3~30); remote(manystore/config/check)"/>
</div>
</div>
<div class="form-group">
<label for="title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="title" name="row[title]" value="" data-rule="required"/>
</div>
</div>
<div class="form-group hidden tf tf-selectpage tf-selectpages">
<label for="c-selectpage-table" class="control-label col-xs-12 col-sm-2">{:__('Selectpage table')}:</label>
<div class="col-xs-12 col-sm-8">
<select id="c-selectpage-table" name="row[setting][table]" class="form-control selectpicker" data-live-search="true">
<option value="">{:__('Please select table')}</option>
</select>
</div>
</div>
<div class="form-group hidden tf tf-selectpage tf-selectpages">
<label for="c-selectpage-primarykey" class="control-label col-xs-12 col-sm-2">{:__('Selectpage primarykey')}:</label>
<div class="col-xs-12 col-sm-8">
<select name="row[setting][primarykey]" class="form-control selectpicker" id="c-selectpage-primarykey"></select>
</div>
</div>
<div class="form-group hidden tf tf-selectpage tf-selectpages">
<label for="c-selectpage-field" class="control-label col-xs-12 col-sm-2">{:__('Selectpage field')}:</label>
<div class="col-xs-12 col-sm-8">
<select name="row[setting][field]" class="form-control selectpicker" id="c-selectpage-field"></select>
</div>
</div>
<div class="form-group hidden tf tf-selectpage tf-selectpages">
<label class="control-label col-xs-12 col-sm-2">{:__('Selectpage conditions')}:</label>
<div class="col-xs-12 col-sm-8">
<dl class="fieldlist" data-name="row[setting][conditions]">
<dd>
<ins>{:__('Field title')}</ins>
<ins>{:__('Field value')}</ins>
</dd>
<dd><a href="javascript:;" class="append btn btn-sm btn-success"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
<textarea name="row[setting][conditions]" class="form-control hide" cols="30" rows="5"></textarea>
</dl>
</div>
</div>
<div class="form-group hidden tf tf-array">
<label for="c-array-key" class="control-label col-xs-12 col-sm-2">{:__('Array key')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[setting][key]" value="" class="form-control" id="c-array-key">
</div>
</div>
<div class="form-group hidden tf tf-array">
<label for="c-array-value" class="control-label col-xs-12 col-sm-2">{:__('Array value')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[setting][value]" value="" class="form-control" id="c-array-value">
</div>
</div>
<div class="form-group hide" id="add-content-container">
<label for="content" class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea name="row[content]" id="content" cols="30" rows="5" class="form-control" data-rule="required(content)">value1|title1
value2|title2</textarea>
</div>
</div>
<div class="form-group">
<label for="tip" class="control-label col-xs-12 col-sm-2">{:__('Tip')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="tip" name="row[tip]" value="" data-rule=""/>
</div>
</div>
<div class="form-group">
<label for="rule" class="control-label col-xs-12 col-sm-2">{:__('Rule')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group pull-left">
<input type="text" class="form-control" id="rule" name="row[rule]" value="" data-tip="{:__('Rule tips')}"/>
<span class="input-group-btn">
<button class="btn btn-primary dropdown-toggle" data-toggle="dropdown" type="button">{:__('Choose')}</button>
<ul class="dropdown-menu pull-right rulelist">
{volist name="ruleList" id="item"}
<li><a href="javascript:;" data-value="{$key}">{$item}<span class="text-muted">({$key})</span></a></li>
{/volist}
</ul>
</span>
</div>
<span class="msg-box n-right" for="rule"></span>
</div>
</div>
<div class="form-group">
<label for="extend" class="control-label col-xs-12 col-sm-2">{:__('Extend')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea name="row[extend]" id="extend" cols="30" rows="5" class="form-control" data-tip="{:__('Extend tips')}" data-rule="required(extend)" data-msg-extend="当类型为自定义时,扩展属性不能为空"></textarea>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Default')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-default" class="form-control" name="row[default]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,131 @@
<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
{:token()}
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Group')}:</label>
<div class="col-xs-12 col-sm-8">
<select id="c-group" class="form-control" data-rule="required" name="row[group]" >
{volist id="vo" name="groupList"}
<option value="{$key}" {in name="key" value="$row.group" }selected{/in} >{$vo}</option>
{/volist}
</select>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Type')}:</label>
<div class="col-xs-12 col-sm-8">
<select name="row[type]" id="c-type" class="form-control selectpicker">
{foreach name="typeList" item="vo"}
<option value="{$key}" {in name="key" value="$row.type" }selected{/in}>{$vo}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group">
<label for="name" class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="name" name="row[name]" value="{$row.name|htmlentities}" data-rule="required; length(3~30)"/>
</div>
</div>
<div class="form-group">
<label for="title" class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="title" name="row[title]" value="{$row.title|htmlentities}" data-rule="required"/>
</div>
</div>
<div class="form-group hidden tf tf-selectpage tf-selectpages">
<label for="c-selectpage-table" class="control-label col-xs-12 col-sm-2">{:__('Selectpage table')}:</label>
<div class="col-xs-12 col-sm-8">
<select id="c-selectpage-table" name="row[setting][table]" data-value="{:empty($row.setting.table) ? '' : $row.setting.table}" class="form-control selectpicker" data-live-search="true">
<option value="" >{:__('Please select table')}</option>
</select>
</div>
</div>
<div class="form-group hidden tf tf-selectpage tf-selectpages">
<label for="c-selectpage-primarykey" class="control-label col-xs-12 col-sm-2">{:__('Selectpage primarykey')}:</label>
<div class="col-xs-12 col-sm-8">
<select name="row[setting][primarykey]" class="form-control selectpicker" id="c-selectpage-primarykey" data-value="{:empty($row.setting.primarykey) ? '' : $row.setting.primarykey}" ></select>
</div>
</div>
<div class="form-group hidden tf tf-selectpage tf-selectpages">
<label for="c-selectpage-field" class="control-label col-xs-12 col-sm-2">{:__('Selectpage field')}:</label>
<div class="col-xs-12 col-sm-8">
<select name="row[setting][field]" class="form-control selectpicker" id="c-selectpage-field" data-value="{:empty($row.setting.field) ? '' : $row.setting.field}" ></select>
</div>
</div>
<div class="form-group hidden tf tf-selectpage tf-selectpages">
<label class="control-label col-xs-12 col-sm-2">{:__('Selectpage conditions')}:</label>
<div class="col-xs-12 col-sm-8">
<dl class="fieldlist" data-name="row[setting][conditions]">
<dd>
<ins>{:__('Field title')}</ins>
<ins>{:__('Field value')}</ins>
</dd>
<dd><a href="javascript:;" class="append btn btn-sm btn-success"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
<textarea name="row[setting][conditions]" class="form-control hide" cols="30" rows="5">{:empty($row.setting.conditions) ? '' : $row.setting.conditions}</textarea>
</dl>
</div>
</div>
<div class="form-group hidden tf tf-array">
<label for="c-array-key" class="control-label col-xs-12 col-sm-2">{:__('Array key')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[setting][key]" value="{$row.setting.key}" class="form-control" id="c-array-key">
</div>
</div>
<div class="form-group hidden tf tf-array">
<label for="c-array-value" class="control-label col-xs-12 col-sm-2">{:__('Array value')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" name="row[setting][value]" value="{$row.setting.value}" class="form-control" id="c-array-value">
</div>
</div>
<div class="form-group hide" id="add-content-container">
<label class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea name="row[content]" id="content" cols="30" rows="5" class="form-control" data-rule="required(content)">{$row.content|htmlentities}</textarea>
</div>
</div>
<div class="form-group">
<label for="tip" class="control-label col-xs-12 col-sm-2">{:__('Tip')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="tip" name="row[tip]" value="{$row.tip|htmlentities}" data-rule=""/>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Rule')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group pull-left">
<input type="text" class="form-control" id="rule" name="row[rule]" value="{$row.rule|htmlentities}" data-tip="{:__('Rule tips')}"/>
<span class="input-group-btn">
<button class="btn btn-primary dropdown-toggle" data-toggle="dropdown" type="button">{:__('Choose')}</button>
<ul class="dropdown-menu pull-right rulelist">
{volist name="ruleList" id="item"}
<li><a href="javascript:;" data-value="{$key}">{$item}<span class="text-muted">({$key})</span></a></li>
{/volist}
</ul>
</span>
</div>
<span class="msg-box n-right" for="rule"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Extend')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea name="row[extend]" id="extend" cols="30" rows="5" class="form-control" data-tip="{:__('Extend tips')}" data-rule="required(extend)" data-msg-extend="当类型为自定义时,扩展属性不能为空">{$row.extend|htmlentities}</textarea>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Default')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-default" class="form-control" name="row[default]" type="text" value="{$row.default}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,25 @@
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<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="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
<a href="javascript:;" class="btn btn-success btn-add {:$auth->check('manystore/config/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
<a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('manystore/config/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('manystore/config/del')?'':'hide'}" 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="{:$auth->check('manystore/config/edit')}"
data-operate-del="{:$auth->check('manystore/config/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,22 @@
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Unique')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-unique" data-rule="required" class="form-control" name="row[unique]" type="text">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text">
</div>
</div>
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,22 @@
<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Unique')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-unique" data-rule="required" class="form-control" name="row[unique]" type="text" value="{$row.unique|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="{$row.name|htmlentities}">
</div>
</div>
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,25 @@
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<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="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
<a href="javascript:;" class="btn btn-success btn-add {:$auth->check('manystore/config/group/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
<a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('manystore/config/group/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('manystore/config/group/del')?'':'hide'}" 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="{:$auth->check('manystore/config/group/edit')}"
data-operate-del="{:$auth->check('manystore/config/group/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,202 @@
<form id="add-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
{:token()}
<div class="row">
<div class="col-md-12 col-sm-12">
<div class="panel panel-default panel-intro">
<div class="panel-heading">
<ul class="nav nav-tabs">
<li class="active"><a href="#basic" data-toggle="tab">基础信息</a></li>
</ul>
</div>
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="basic">
<div class="form-group">
<label for="username" class="control-label col-xs-12 col-sm-2">{:__('Username')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="username" name="row[username]" value="" data-rule="required;username" placeholder="请输入{:__('Username')},用于登录。格式:英文或数字或组合" />
</div>
</div>
<div class="form-group">
<label for="email" class="control-label col-xs-12 col-sm-2">{:__('Email')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="email" class="form-control" id="email" name="row[email]" value="" data-rule="required;email" placeholder="请输入{:__('Email')}" />
</div>
</div>
<div class="form-group">
<label for="nickname" class="control-label col-xs-12 col-sm-2">{:__('Nickname')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="nickname" name="row[nickname]" autocomplete="off" value="" data-rule="required;length(~10)" placeholder="请输入{:__('Nickname')}" />
</div>
</div>
<div class="form-group">
<label for="password" class="control-label col-xs-12 col-sm-2">{:__('Password')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="password" class="form-control" id="password" name="row[password]" autocomplete="new-password" value="" data-rule="required;password" placeholder="请输入{:__('Password')}" />
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-12 col-sm-12">
<div class="panel panel-default panel-intro">
<div class="panel-heading">
<ul class="nav nav-tabs">
<li class="active"><a href="#basic" data-toggle="tab">商家信息</a></li>
</ul>
</div>
<div class="panel-body">
<div id="storeContent" class="tab-content">
<div class="tab-pane fade active in" id="shop_basic">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-name" data-rule="required" class="form-control" name="shop[name]" type="text" value="" placeholder="请输入{:__('Name')}" >
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">
{:__('Logo')}:
<p style="margin-top: 20px;">建议40*40</p>
</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-logo" class="form-control" size="50" name="shop[logo]" type="text" value="" placeholder="请上传{:__('logo')}" >
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="plupload-logo" class="btn btn-danger plupload cropper" data-input-id="c-logo" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="false" data-preview-id="p-logo"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-logo" class="btn btn-primary fachoose" data-input-id="c-logo" data-mimetype="image/*" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-logo"></span>
</div>
<ul class="row list-inline plupload-preview" id="p-logo"></ul>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Image')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-image" data-rule="required" class="form-control" size="50" name="shop[image]" type="text" value="" placeholder="请上传{:__('Image')}" >
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="plupload-image" class="btn btn-danger plupload cropper" data-input-id="c-image" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="false" data-preview-id="p-image"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-image" class="btn btn-primary fachoose" data-input-id="c-image" data-mimetype="image/*" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-image"></span>
</div>
<ul class="row list-inline plupload-preview" id="p-image"></ul>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Images')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-images" class="form-control" name="shop[images]" type="text" value="" placeholder="请上传{:__('Images')}">
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="plupload-images" class="btn btn-danger plupload cropper" data-input-id="c-images" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="true" data-preview-id="p-images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-imagess" class="btn btn-primary fachoose" data-input-id="c-images" data-mimetype="image/*" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-images"></span>
</div>
<ul class="row list-inline plupload-preview" id="p-images"></ul>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Address_city')}:</label>
<div class="col-xs-12 col-sm-8">
<div class='control-relative'>
<input id="c-address_city" data-rule="required" class="form-control form-control" data-toggle="city-picker" name="shop[address_city]" value="" type="text">
</div>
<input type="hidden" id="province" name="shop[province]" value="" >
<input type="hidden" id="city" name="shop[city]" value="" >
<input type="hidden" id="district" name="shop[district]" value="" >
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Address')}:</label>
<div class="col-xs-12 col-sm-8">
<div class='control-relative'>
<input id="c-address" data-rule="required" class="form-control form-control"
data-lat-id="c-latitude" data-lng-id="c-longitude" readonly data-input-id="c-address" data-toggle="addresspicker" name="shop[address]" value="" type="text" placeholder="请地图选址。如调起地图失败请检查插件《地图位置(经纬度)选择》是否安装">
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Address_detail')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-address_detail" class="form-control" name="shop[address_detail]" type="text" value="" placeholder="请输入{:__('Address_detail')}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Longitude')}:</label>
<div class="col-xs-12 col-sm-3">
<input id="c-longitude" data-rule="required" readonly class="form-control" name="shop[longitude]" type="text" value="">
</div>
<label class="control-label col-xs-12 col-sm-2">{:__('Latitude')}:</label>
<div class="col-xs-12 col-sm-3">
<input id="c-latitude" data-rule="required" readonly class="form-control" name="shop[latitude]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Yyzzdm')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-yyzzdm" data-rule="required" class="form-control" name="shop[yyzzdm]" type="text" value="" placeholder="请输入{:__('Yyzzdm')}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Yyzz_images')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-yyzz_images" data-rule="required" class="form-control" size="50" name="shop[yyzz_images]" type="text" value="" placeholder="请输入{:__('Yyzz_images')}">
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="plupload-yyzz_images" class="btn btn-danger plupload" data-input-id="c-yyzz_images" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="true" data-preview-id="p-yyzz_images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-yyzz_images" class="btn btn-primary fachoose" data-input-id="c-yyzz_images" data-mimetype="image/*" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-yyzz_images"></span>
</div>
<ul class="row list-inline plupload-preview" id="p-yyzz_images"></ul>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Tel')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-tel" class="form-control" name="shop[tel]" type="text" value="" placeholder="请输入{:__('Tel')}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea id="c-content" class="form-control editor" rows="5" name="shop[content]" cols="50" placeholder="请输入{:__('Content')}"></textarea>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('shop[status]', ['0'=>__('Status 0'), '1'=>__('Status 1'), '2'=>__('Status 2')],1)}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="form-group hidden layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,210 @@
<form id="edit-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
{:token()}
<div class="row">
<div class="col-md-12 col-sm-12">
<div class="panel panel-default panel-intro">
<div class="panel-heading">
<ul class="nav nav-tabs">
<li class="active"><a href="#basic" data-toggle="tab">基础信息</a></li>
</ul>
</div>
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="basic">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Username')}:</label>
<div class="col-xs-12 col-sm-8" style="margin-top:7px;">
{$row.username|htmlentities}
</div>
</div>
<div class="form-group">
<label for="email" class="control-label col-xs-12 col-sm-2">{:__('Email')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="email" class="form-control" id="email" name="row[email]" value="{$row.email|htmlentities}" data-rule="required;email" placeholder="请输入{:__('Email')}" />
</div>
</div>
<div class="form-group">
<label for="nickname" class="control-label col-xs-12 col-sm-2">{:__('Nickname')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="nickname" name="row[nickname]" autocomplete="off" value="{$row.nickname|htmlentities}" data-rule="required;length(~10)" placeholder="请输入{:__('Nickname')}" />
</div>
</div>
<div class="form-group">
<label for="password" class="control-label col-xs-12 col-sm-2">{:__('Password')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="password" class="form-control" id="password" name="row[password]" autocomplete="new-password" value="" data-rule="password" placeholder="不修改则留空" />
</div>
</div>
<div class="form-group">
<label for="loginfailure" class="control-label col-xs-12 col-sm-2">{:__('Loginfailure')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="number" class="form-control" id="loginfailure" name="row[loginfailure]" value="{$row.loginfailure}" data-rule="required" />
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-12 col-sm-12">
<div class="panel panel-default panel-intro">
<div class="panel-heading">
<ul class="nav nav-tabs">
<li class="active"><a href="#basic" data-toggle="tab">商家信息</a></li>
</ul>
</div>
<div class="panel-body">
<div id="storeContent" class="tab-content">
<div class="tab-pane fade active in" id="shop_basic">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-name" data-rule="required" class="form-control" name="shop[name]" type="text" value="{$shop.name}" placeholder="请输入{:__('Name')}" >
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">
{:__('Logo')}:
<p style="margin-top: 20px;">建议40*40</p>
</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-logo" class="form-control" size="50" name="shop[logo]" type="text" value="{$shop.logo}" placeholder="请上传{:__('logo')}" >
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="plupload-logo" class="btn btn-danger plupload cropper" data-input-id="c-logo" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="false" data-preview-id="p-logo"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-logo" class="btn btn-primary fachoose" data-input-id="c-logo" data-mimetype="image/*" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-logo"></span>
</div>
<ul class="row list-inline plupload-preview" id="p-logo"></ul>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Image')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-image" data-rule="required" class="form-control" size="50" name="shop[image]" type="text" value="{$shop.image}" placeholder="请上传{:__('Image')}" >
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="plupload-image" class="btn btn-danger plupload cropper" data-input-id="c-image" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="false" data-preview-id="p-image"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-image" class="btn btn-primary fachoose" data-input-id="c-image" data-mimetype="image/*" data-multiple="false"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-image"></span>
</div>
<ul class="row list-inline plupload-preview" id="p-image"></ul>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Images')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-images" class="form-control" name="shop[images]" type="text" value="{$shop.images}" placeholder="请上传{:__('Images')}">
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="plupload-images" class="btn btn-danger plupload cropper" data-input-id="c-images" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="true" data-preview-id="p-images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-imagess" class="btn btn-primary fachoose" data-input-id="c-images" data-mimetype="image/*" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-images"></span>
</div>
<ul class="row list-inline plupload-preview" id="p-images"></ul>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Address_city')}:</label>
<div class="col-xs-12 col-sm-8">
<div class='control-relative'>
<input id="c-address_city" data-rule="required" class="form-control form-control" data-toggle="city-picker" name="shop[address_city]" value="{$shop.address_city}" type="text">
</div>
<input type="hidden" id="province" name="shop[province]" value="" >
<input type="hidden" id="city" name="shop[city]" value="" >
<input type="hidden" id="district" name="shop[district]" value="" >
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Address')}:</label>
<div class="col-xs-12 col-sm-8">
<div class='control-relative'>
<input id="c-address" data-rule="required" class="form-control form-control"
data-lat-id="c-latitude" data-lng-id="c-longitude" readonly data-input-id="c-address" data-toggle="addresspicker" name="shop[address]" value="{$shop.address}" type="text" placeholder="请地图选址。如调起地图失败请检查插件《地图位置(经纬度)选择》是否安装">
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Address_detail')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-address_detail" class="form-control" name="shop[address_detail]" type="text" value="{$shop.address_detail}" placeholder="请输入{:__('Address_detail')}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Longitude')}:</label>
<div class="col-xs-12 col-sm-3">
<input id="c-longitude" data-rule="required" readonly class="form-control" name="shop[longitude]" type="text" value="{$shop.longitude}">
</div>
<label class="control-label col-xs-12 col-sm-2">{:__('Latitude')}:</label>
<div class="col-xs-12 col-sm-3">
<input id="c-latitude" data-rule="required" readonly class="form-control" name="shop[latitude]" type="text" value="{$shop.latitude}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Yyzzdm')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-yyzzdm" data-rule="required" class="form-control" name="shop[yyzzdm]" type="text" value="{$shop.yyzzdm}" placeholder="请输入{:__('Yyzzdm')}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Yyzz_images')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group">
<input id="c-yyzz_images" data-rule="required" class="form-control" size="50" name="shop[yyzz_images]" type="text" value="{$shop.yyzz_images}" placeholder="请输入{:__('Yyzz_images')}">
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="plupload-yyzz_images" class="btn btn-danger plupload" data-input-id="c-yyzz_images" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp" data-multiple="true" data-preview-id="p-yyzz_images"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
<span><button type="button" id="fachoose-yyzz_images" class="btn btn-primary fachoose" data-input-id="c-yyzz_images" data-mimetype="image/*" data-multiple="true"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
</div>
<span class="msg-box n-right" for="c-yyzz_images"></span>
</div>
<ul class="row list-inline plupload-preview" id="p-yyzz_images"></ul>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Tel')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-tel" class="form-control" name="shop[tel]" type="text" value="{$shop.tel}" placeholder="请输入{:__('Tel')}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea id="c-content" class="form-control editor" rows="5" name="shop[content]" cols="50" placeholder="请输入{:__('Content')}">{$shop.content}</textarea>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('shop[status]', ['0'=>__('Status 0'), '1'=>__('Status 1'), '2'=>__('Status 2')], $shop['status'])}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="form-group hidden layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,20 @@
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<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">
{:build_toolbar('refresh,add')}
</div>
<table id="table" class="table table-striped table-bordered table-hover"
data-operate-edit="{:$auth->check('manystore/index/edit')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,68 @@
<form id="add-form" class="form-horizontal form-ajax" role="form" data-toggle="validator" method="POST" action="">
{:token()}
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Ismenu')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')])}
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Parent')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_select('row[pid]', $ruledata, null, ['class'=>'form-control', 'required'=>''])}
</div>
</div>
<div class="form-group">
<label for="name" class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="name" name="row[name]" data-placeholder-node="{:__('Node tips')}" data-placeholder-menu="{:__('Menu tips')}" value="" data-rule="required" />
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="title" name="row[title]" value="" data-rule="required" />
</div>
</div>
<div class="form-group">
<label for="icon" class="control-label col-xs-12 col-sm-2">{:__('Icon')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group input-groupp-md">
<input type="text" class="form-control" id="icon" name="row[icon]" value="fa fa-circle-o" />
<a href="javascript:;" class="btn-search-icon input-group-addon">{:__('Search icon')}</a>
</div>
</div>
</div>
<div class="form-group">
<label for="weigh" class="control-label col-xs-12 col-sm-2">{:__('Weigh')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="weigh" name="row[weigh]" value="0" data-rule="required" />
</div>
</div>
<div class="form-group">
<label for="remark" class="control-label col-xs-12 col-sm-2">{:__('Condition')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea class="form-control" id="condition" name="row[condition]"></textarea>
</div>
</div>
<div class="form-group">
<label for="remark" class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea class="form-control" id="remark" name="row[remark]"></textarea>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])}
</div>
</div>
<div class="form-group hidden layer-footer">
<div class="col-xs-2"></div>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
{include file="auth/rule/tpl" /}

View File

@ -0,0 +1,68 @@
<form id="edit-form" class="form-horizontal form-ajax" role="form" method="POST" action="">
{:token()}
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Ismenu')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')], $row['ismenu'])}
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Parent')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_select('row[pid]', $ruledata, $row['pid'], ['class'=>'form-control', 'required'=>''])}
</div>
</div>
<div class="form-group">
<label for="name" class="control-label col-xs-12 col-sm-2">{:__('Name')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="name" name="row[name]" data-placeholder-node="{:__('Node tips')}" data-placeholder-menu="{:__('Menu tips')}" value="{$row.name|htmlentities}" data-rule="required" />
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Title')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="title" name="row[title]" value="{$row.title|htmlentities}" data-rule="required" />
</div>
</div>
<div class="form-group">
<label for="icon" class="control-label col-xs-12 col-sm-2">{:__('Icon')}:</label>
<div class="col-xs-12 col-sm-8">
<div class="input-group input-groupp-md">
<input type="text" class="form-control" id="icon" name="row[icon]" value="{$row.icon}" />
<a href="javascript:;" class="btn-search-icon input-group-addon">{:__('Search icon')}</a>
</div>
</div>
</div>
<div class="form-group">
<label for="weigh" class="control-label col-xs-12 col-sm-2">{:__('Weigh')}:</label>
<div class="col-xs-12 col-sm-8">
<input type="text" class="form-control" id="weigh" name="row[weigh]" value="{$row.weigh}" data-rule="required" />
</div>
</div>
<div class="form-group">
<label for="remark" class="control-label col-xs-12 col-sm-2">{:__('Condition')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea class="form-control" id="condition" name="row[condition]">{$row.condition|htmlentities}</textarea>
</div>
</div>
<div class="form-group">
<label for="remark" class="control-label col-xs-12 col-sm-2">{:__('Remark')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea class="form-control" id="remark" name="row[remark]">{$row.remark|htmlentities}</textarea>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Status')}:</label>
<div class="col-xs-12 col-sm-8">
{:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])}
</div>
</div>
<div class="form-group hidden layer-footer">
<div class="col-xs-2"></div>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-success btn-embossed disabled">{:__('OK')}</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>
{include file="auth/rule/tpl" /}

View File

@ -0,0 +1,35 @@
<style>
.bootstrap-table tr td .text-muted {color:#888;}
</style>
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<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="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
<a href="javascript:;" class="btn btn-success btn-add {:$auth->check('manystore/rule/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
<a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('manystore/rule/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('manystore/rule/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
<div class="dropdown btn-group {:$auth->check('manystore/rule/multi')?'':'hide'}">
<a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
<ul class="dropdown-menu text-left" role="menu">
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
</ul>
</div>
<a href="javascript:;" class="btn btn-danger btn-toggle-all"><i class="fa fa-plus"></i> {:__('Toggle all')}</a>
</div>
<table id="table" class="table table-bordered table-hover"
data-operate-edit="{:$auth->check('manystore/rule/edit')}"
data-operate-del="{:$auth->check('manystore/rule/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,43 @@
<style>
#chooseicon {
margin:10px;
}
#chooseicon ul {
margin:5px 0 0 0;
}
#chooseicon ul li{
width:41px;height:42px;
line-height:42px;
border:1px solid #efefef;
padding:1px;
margin:1px;
text-align: center;
font-size:18px;
}
#chooseicon ul li:hover{
border:1px solid #2c3e50;
cursor:pointer;
}
</style>
<script id="chooseicontpl" type="text/html">
<div id="chooseicon">
<div>
<form onsubmit="return false;">
<div class="input-group input-groupp-md">
<div class="input-group-addon">{:__('Search icon')}</div>
<input class="js-icon-search form-control" type="text" placeholder="">
</div>
</form>
</div>
<div>
<ul class="list-inline">
<% for(var i=0; i<iconlist.length; i++){ %>
<li data-font="<%=iconlist[i]%>" data-toggle="tooltip" title="<%=iconlist[i]%>">
<i class="fa fa-<%=iconlist[i]%>"></i>
</li>
<% } %>
</ul>
</div>
</div>
</script>

View File

@ -0,0 +1,611 @@
<?php
namespace app\common\controller;
use app\manystore\library\Auth;
use app\common\model\ManystoreConfig;
use think\Config;
use think\Controller;
use think\Hook;
use think\Lang;
use think\Loader;
use think\Session;
use fast\Tree;
use think\Validate;
/**
* 后台控制器基类
*/
class ManystoreBase extends Controller
{
/**
* 无需登录的方法,同时也就不需要鉴权了
* @var array
*/
protected $noNeedLogin = [];
/**
* 无需鉴权的方法,但需要登录
* @var array
*/
protected $noNeedRight = [];
/**
* 布局模板
* @var string
*/
protected $layout = 'default';
/**
* 权限控制类
* @var Auth
*/
protected $auth = null;
/**
* 模型对象
* @var \think\Model
*/
protected $model = null;
/**
* 快速搜索时执行查找的字段
*/
protected $searchFields = 'id';
/**
* 是否是关联查询
*/
protected $relationSearch = false;
/**
* 是否开启Validate验证
*/
protected $modelValidate = false;
/**
* 是否开启模型场景验证
*/
protected $modelSceneValidate = false;
/**
* Multi方法可批量修改的字段
*/
protected $multiFields = 'status';
/**
* Selectpage可显示的字段
*/
protected $selectpageFields = '*';
/**
* 前台提交过来,需要排除的字段数据
*/
protected $excludeFields = "";
/**
* 导入文件首行类型
* 支持comment/name
* 表示注释或字段名
*/
protected $importHeadType = 'comment';
/**
* 判断是否数据关联shop_id
*/
protected $storeIdFieldAutoFill = null;
/**
* 判断是否数据关联store_id
*/
protected $shopIdAutoCondition = null;
/**
* 控制器前置方法
*/
protected $beforeActionList = [
'setShopAutoRelation',
];
/**
* 引入后台控制器的traits
*/
use \app\manystore\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());
$path = str_replace('.', '/', $controllername) . '/' . $actionname;
// 定义是否Addtabs请求
!defined('IS_ADDTABS') && define('IS_ADDTABS', input("addtabs") ? true : false);
// 定义是否Dialog请求
!defined('IS_DIALOG') && define('IS_DIALOG', input("dialog") ? true : false);
// 定义是否AJAX请求
!defined('IS_AJAX') && define('IS_AJAX', $this->request->isAjax());
$this->auth = Auth::instance();
// 设置当前请求的URI
$this->auth->setRequestUri($path);
// 检测是否需要验证登录
if (!$this->auth->match($this->noNeedLogin)) {
//检测是否登录
if (!$this->auth->isLogin()) {
Hook::listen('manystore_nologin', $this);
$url = Session::get('referer');
$url = $url ? $url : $this->request->url();
if ($url == '/') {
$this->redirect('index/login', [], 302, ['referer' => $url]);
exit;
}
$this->error(__('Please login first'), url('index/login', ['url' => $url]));
}
// 判断是否需要验证权限
if (!$this->auth->match($this->noNeedRight)) {
// 判断控制器和方法判断是否有对应权限
if (!$this->auth->check($path)) {
Hook::listen('manystore_nopermission', $this);
$this->error(__('You have no permission'), '');
}
}
}
if(!defined('SHOP_ID')){
define('SHOP_ID', $this->auth->shop_id);
}
if(!defined('STORE_ID')) {
define('STORE_ID', $this->auth->id);
}
// 非选项卡时重定向
if (!$this->request->isPost() && !IS_AJAX && !IS_ADDTABS && !IS_DIALOG && input("ref") == 'addtabs') {
$url = preg_replace_callback("/([\?|&]+)ref=addtabs(&?)/i", function ($matches) {
return $matches[2] == '&' ? $matches[1] : '';
}, $this->request->url());
if (Config::get('url_domain_deploy')) {
if (stripos($url, $this->request->server('SCRIPT_NAME')) === 0) {
$url = substr($url, strlen($this->request->server('SCRIPT_NAME')));
}
$url = url($url, '', false);
}
$this->redirect('index/index', [], 302, ['referer' => $url]);
exit;
}
// 设置面包屑导航数据
$breadcrumb = $this->auth->getBreadCrumb($path);
array_pop($breadcrumb);
$this->view->breadcrumb = $breadcrumb;
// 如果有使用模板布局
if ($this->layout) {
$this->view->engine->layout('layout/' . $this->layout);
}
$manystoreConfig = new ManystoreConfig();
config('manystore_config',$manystoreConfig->manystore_config());
// 语言检测
$lang = $this->request->langset();
$lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn';
$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', 'indexurl', 'cdnurl', 'version', 'timezone', 'languages'])),
'upload' => $upload,
'modulename' => $modulename,
'controllername' => $controllername,
'actionname' => $actionname,
'jsname' => 'manystore/' . str_replace('.', '/', $controllername),
'moduleurl' => rtrim(url("/{$modulename}", '', false), '/'),
'language' => $lang,
'referer' => Session::get("referer")
];
$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);
//渲染权限对象
$this->assign('auth', $this->auth);
//渲染管理员对象
$this->assign('manystore', Session::get('manystore'));
}
/**
* 加载语言文件
* @param string $name
*/
protected function loadlang($name)
{
$name = Loader::parseName($name);
$name = preg_match("/^([a-zA-Z0-9_\.\/]+)\$/i", $name) ? $name : 'index';
$lang = $this->request->langset();
$lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn';
Lang::load(APP_PATH . $this->request->module() . '/lang/' . $lang . '/' . 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/d", 0);
$limit = $this->request->get("limit/d", 999999);
//新增自动计算页码
$page = $limit ? intval($offset / $limit) + 1 : 1;
if ($this->request->has("page")) {
$page = $this->request->get("page/d", 1);
}
$this->request->get([config('paginate.var_page') => $page]);
$filter = (array)json_decode($filter, true);
$op = (array)json_decode($op, true);
$filter = $filter ? $filter : [];
$where = [];
$alias = [];
$bind = [];
$name = '';
$aliasName = '';
if (!empty($this->model) && $this->relationSearch) {
$name = $this->model->getTable();
$alias[$name] = Loader::parseName(basename(str_replace('\\', '/', get_class($this->model))));
$aliasName = $alias[$name] . '.';
}
$sortArr = explode(',', $sort);
foreach ($sortArr as $index => & $item) {
$item = stripos($item, ".") === false ? $aliasName . trim($item) : $item;
}
unset($item);
$sort = implode(',', $sortArr);
if($this->shopIdAutoCondition){
$where[] = [$aliasName.'shop_id','eq',SHOP_ID];
}
if ($search) {
$searcharr = is_array($searchfields) ? $searchfields : explode(',', $searchfields);
foreach ($searcharr as $k => &$v) {
$v = stripos($v, ".") === false ? $aliasName . $v : $v;
}
unset($v);
$where[] = [implode("|", $searcharr), "LIKE", "%{$search}%"];
}
$index = 0;
foreach ($filter as $k => $v) {
if (!preg_match('/^[a-zA-Z0-9_\-\.]+$/', $k)) {
continue;
}
$sym = isset($op[$k]) ? $op[$k] : '=';
if (stripos($k, ".") === false) {
$k = $aliasName . $k;
}
$v = !is_array($v) ? trim($v) : $v;
$sym = strtoupper(isset($op[$k]) ? $op[$k] : $sym);
//null和空字符串特殊处理
if (!is_array($v)) {
if (in_array(strtoupper($v), ['NULL', 'NOT NULL'])) {
$sym = strtoupper($v);
}
if (in_array($v, ['""', "''"])) {
$v = '';
$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':
$v = is_array($v) ? $v : explode(',', str_replace(' ', ',', $v));
$findArr = array_values($v);
foreach ($findArr as $idx => $item) {
$bindName = "item_" . $index . "_" . $idx;
$bind[$bindName] = $item;
$where[] = "FIND_IN_SET(:{$bindName}, `" . 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];
}
$tableArr = explode('.', $k);
if (count($tableArr) > 1 && $tableArr[0] != $name && !in_array($tableArr[0], $alias) && !empty($this->model)) {
//修复关联模型下时间无法搜索的BUG
$relation = Loader::parseName($tableArr[0], 1, false);
$alias[$this->model->$relation()->getTable()] = $tableArr[0];
}
$where[] = [$k, str_replace('RANGE', 'BETWEEN', $sym) . ' TIME', $arr];
break;
case 'NULL':
case 'IS NULL':
case 'NOT NULL':
case 'IS NOT NULL':
$where[] = [$k, strtolower(str_replace('IS ', '', $sym))];
break;
default:
break;
}
$index++;
}
if (!empty($this->model)) {
$this->model->alias($alias);
}
$model = $this->model;
$where = function ($query) use ($where, $alias, $bind, &$model) {
if (!empty($model)) {
$model->alias($alias);
$model->bind($bind);
}
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, $page, $alias, $bind];
}
/**
* Selectpage的实现方法
*
* 当前方法只是一个比较通用的搜索匹配,请按需重载此方法来编写自己的搜索逻辑,$where按自己的需求写即可
* 这里示例了所有的参数,所以比较复杂,实现上自己实现只需简单的几行即可
*
*/
protected function selectpage()
{
//设置过滤方法
$this->request->filter(['trim', 'strip_tags', 'htmlspecialchars']);
//搜索关键词,客户端输入以空格分开,这里接收为数组
$word = (array)$this->request->request("q_word/a");
//当前页
$page = $this->request->request("pageNumber");
//分页大小
$pagesize = $this->request->request("pageSize");
//搜索条件
$andor = $this->request->request("andOr", "and", "strtoupper");
//排序方式
$orderby = (array)$this->request->request("orderBy/a");
//显示的字段
$field = $this->request->request("showField");
//主键
$primarykey = $this->request->request("keyField");
//主键值
$primaryvalue = $this->request->request("keyValue");
//搜索字段
$searchfield = (array)$this->request->request("searchField/a");
//自定义搜索条件
$custom = (array)$this->request->request("custom/a");
//是否返回树形结构
$istree = $this->request->request("isTree", 0);
$ishtml = $this->request->request("isHtml", 0);
if ($istree) {
$word = [];
$pagesize = 999999;
}
$order = [];
foreach ($orderby as $k => $v) {
$order[$v[0]] = $v[1];
}
$field = $field ? $field : 'name';
//如果有primaryvalue,说明当前是初始化传值
if ($primaryvalue !== null) {
$where = [$primarykey => ['in', $primaryvalue]];
$pagesize = 999999;
} else {
$where = function ($query) use ($word, $andor, $field, $searchfield, $custom) {
$logic = $andor == 'AND' ? '&' : '|';
$searchfield = is_array($searchfield) ? implode($logic, $searchfield) : $searchfield;
$searchfield = str_replace(',', $logic, $searchfield);
$word = array_filter(array_unique($word));
if (count($word) == 1) {
$query->where($searchfield, "like", "%" . reset($word) . "%");
} else {
$query->where(function ($query) use ($word, $searchfield) {
foreach ($word as $index => $item) {
$query->whereOr(function ($query) use ($item, $searchfield) {
$query->where($searchfield, "like", "%{$item}%");
});
}
});
}
if ($custom && is_array($custom)) {
foreach ($custom as $k => $v) {
if (is_array($v) && 2 == count($v)) {
$query->where($k, trim($v[0]), $v[1]);
} else {
$query->where($k, '=', $v);
}
}
}
};
}
if($this->shopIdAutoCondition){
$this->model->where(array('shop_id'=>SHOP_ID));
}
$list = [];
$total = $this->model->where($where)->count();
if ($total > 0) {
if($this->shopIdAutoCondition){
$this->model->where(array('shop_id'=>SHOP_ID));
}
$fields = is_array($this->selectpageFields) ? $this->selectpageFields : ($this->selectpageFields && $this->selectpageFields != '*' ? explode(',', $this->selectpageFields) : []);
//如果有primaryvalue,说明当前是初始化传值,按照选择顺序排序
if ($primaryvalue !== null && preg_match("/^[a-z0-9_\-]+$/i", $primarykey)) {
$primaryvalue = array_unique(is_array($primaryvalue) ? $primaryvalue : explode(',', $primaryvalue));
$primaryvalue = implode(',', $primaryvalue);
$this->model->orderRaw("FIELD(`{$primarykey}`, {$primaryvalue})");
} else {
$this->model->order($order);
}
$datalist = $this->model->where($where)
->page($page, $pagesize)
->select();
foreach ($datalist as $index => $item) {
unset($item['password'], $item['salt']);
if ($this->selectpageFields == '*') {
$result = [
$primarykey => isset($item[$primarykey]) ? $item[$primarykey] : '',
$field => isset($item[$field]) ? $item[$field] : '',
];
} else {
$result = array_intersect_key(($item instanceof Model ? $item->toArray() : (array)$item), array_flip($fields));
}
$result['pid'] = isset($item['pid']) ? $item['pid'] : (isset($item['parent_id']) ? $item['parent_id'] : 0);
$list[] = $result;
}
if ($istree && !$primaryvalue) {
$tree = Tree::instance();
$tree->init(collection($list)->toArray(), 'pid');
$list = $tree->getTreeList($tree->getTreeArray(0), $field);
if (!$ishtml) {
foreach ($list as &$item) {
$item = str_replace('&nbsp;', ' ', $item);
}
unset($item);
}
}
}
//这里一定要返回有list这个字段,total是可选的,如果total<=list的数量,则会隐藏分页按钮
return json(['list' => $list, 'total' => $total]);
}
/**
* 刷新Token
*/
protected function token()
{
$token = $this->request->post('__token__');
//验证Token
if (!Validate::is($token, "token", ['__token__' => $token])) {
$this->error(__('Token verification error'), '', ['__token__' => $this->request->token()]);
}
//刷新Token
$this->request->token();
}
/**
* 设置商家关联关系
*/
protected function setShopAutoRelation(){
if($this->model){
$fields = $this->model->getQuery()->getTableInfo('','fields');
if(!isset($this->storeIdFieldAutoFill)){
$this->storeIdFieldAutoFill = in_array("store_id",$fields);
}
if(!isset($this->shopIdAutoCondition)){
$this->shopIdAutoCondition = in_array("shop_id",$fields);
}
}
}
}

View File

@ -0,0 +1,130 @@
<?php
namespace app\common\library;
use app\manystore\model\ManystoreAuthRule;
use fast\Tree;
use think\Exception;
use think\exception\PDOException;
class ManystoreMenu
{
/**
* 创建菜单
* @param array $menu
* @param mixed $parent 父类的name或pid
*/
public static function create($menu, $parent = 0)
{
if (!is_numeric($parent)) {
$parentRule = ManystoreAuthRule::getByName($parent);
$pid = $parentRule ? $parentRule['id'] : 0;
} else {
$pid = $parent;
}
$allow = array_flip(['file', 'name', 'title', 'icon', 'condition', 'remark', 'ismenu']);
foreach ($menu as $k => $v) {
$hasChild = isset($v['sublist']) && $v['sublist'] ? true : false;
$data = array_intersect_key($v, $allow);
$data['ismenu'] = isset($data['ismenu']) ? $data['ismenu'] : ($hasChild ? 1 : 0);
$data['icon'] = isset($data['icon']) ? $data['icon'] : ($hasChild ? 'fa fa-list' : 'fa fa-circle-o');
$data['pid'] = $pid;
$data['status'] = 'normal';
try {
$menu = ManystoreAuthRule::create($data);
if ($hasChild) {
self::create($v['sublist'], $menu->id);
}
} catch (PDOException $e) {
throw new Exception($e->getMessage());
}
}
}
/**
* 删除菜单
* @param string $name 规则name
* @return boolean
*/
public static function delete($name)
{
$ids = self::getAuthRuleIdsByName($name);
if (!$ids) {
return false;
}
ManystoreAuthRule::destroy($ids);
return true;
}
/**
* 启用菜单
* @param string $name
* @return boolean
*/
public static function enable($name)
{
$ids = self::getAuthRuleIdsByName($name);
if (!$ids) {
return false;
}
ManystoreAuthRule::where('id', 'in', $ids)->update(['status' => 'normal']);
return true;
}
/**
* 禁用菜单
* @param string $name
* @return boolean
*/
public static function disable($name)
{
$ids = self::getAuthRuleIdsByName($name);
if (!$ids) {
return false;
}
ManystoreAuthRule::where('id', 'in', $ids)->update(['status' => 'hidden']);
return true;
}
/**
* 导出指定名称的菜单规则
* @param string $name
* @return array
*/
public static function export($name)
{
$ids = self::getAuthRuleIdsByName($name);
if (!$ids) {
return [];
}
$menuList = [];
$menu = ManystoreAuthRule::getByName($name);
if ($menu) {
$ruleList = collection(ManystoreAuthRule::where('id', 'in', $ids)->select())->toArray();
$menuList = Tree::instance()->init($ruleList)->getTreeArray($menu['id']);
}
return $menuList;
}
/**
* 根据名称获取规则IDS
* @param string $name
* @return array
*/
public static function getAuthRuleIdsByName($name)
{
$ids = [];
$menu = ManystoreAuthRule::getByName($name);
if ($menu) {
// 必须将结果集转换为数组
$ruleList = collection(ManystoreAuthRule::order('weigh', 'desc')->field('id,pid,name')->select())->toArray();
// 构造菜单数据
$ids = Tree::instance()->init($ruleList)->getChildrenIds($menu['id'], true);
}
return $ids;
}
}

View File

@ -0,0 +1,144 @@
<?php
namespace app\common\model;
use think\Model;
/**
* 基础扩展模型
*/
class BaseModel extends Model
{
public $timeKey = 'createtime';
public static $staticTimeKey = 'createtime';
/**得到基础条件
* @param $status
* @param null $model
* @param string $alisa
*/
public static function getBaseWhere($whereData = [], $model = null, $alisa = '',$with = false)
{
if (!$model) {
$model = new static;
if ($alisa&&!$with) $model = $model->alias($alisa);
}
if ($alisa) $alisa = $alisa . '.';
//$model = $model->where($alisa . 'status', '1');
$tableFields = (new static)->getTableFields();
foreach ($tableFields as $fields)
{
if (isset($whereData[$fields]) && $whereData[$fields]) $model = $model->where("{$alisa}{$fields}", '=', $whereData[$fields]);
}
return $model;
}
/**
* 基础列表
*/
public function getBaseList($whereData = [], $page = 0, $limit = 0, $sort = '',$field =[],$where=[],$toArray = true)
{
$alisa = $this->getWithAlisaName();
if($field){
//如果是一维数组
if(is_array($field)&&count($field) == count($field,1)) $field = ['base'=>$field];
//如果是字符串
if(is_string($field)) $field = ['base'=>explode(',',$field)];
}
$this->withTable = array_keys($field);
$base_i = array_search("base",$this->withTable);
if($base_i!==false)unset($this->withTable[$base_i]);
if(!$this->withTable)$alisa = '';
$alisa_name = '';
if($alisa)$alisa_name = "{$alisa}.";
if(!$sort)$sort = "{$alisa_name}id asc";
$self = static::getBaseWhere($whereData, null, $alisa,true);
if($this->withTable)$self = $self->with($this->withTable);
if($page&&$limit)$self = $self->orderRaw($sort)->where($where)->page($page, $limit);
$list = $self->select();
foreach ($list as $row) {
if(isset($field['base'])&&$field['base']!=['*']){
$row->visible($field['base']);
}else{
$getTableFields = $this->getTableFields();
if(!empty($this->hidden) && is_array($this->hidden)){
$getTableFields = array_diff($getTableFields,$this->hidden);
}
$row->visible($getTableFields);
}
foreach ($this->withTable as $withName) {
if(isset($field[$withName])&&$field[$withName]!=['*']){
$row->visible([$withName]);
$row->getRelation($withName)->visible($field[$withName]);
}elseif(isset($field[$withName])&&$field[$withName]==['*']){
$row->visible([$withName]);
}
}
}
if($toArray)$list = collection($list)->toArray();
$countSelf = static::getBaseWhere($whereData, null, $alisa,true);
if($this->withTable)$countSelf = $countSelf->with($this->withTable);
$count = $countSelf->where($where)->count();
return compact('list', 'count','page','limit');
}
/**
* 时间段搜索器
* @param Model $query
* @param $value
*/
public function scopeTime($query, $value)
{
$timeKey = $this->timeKey;
if(static::$staticTimeKey)$timeKey =static::$staticTimeKey;
switch ($value) {
case 'today':
case 'week':
case 'month':
case 'year':
case 'yesterday':
case 'last year':
case 'last week':
case 'last month':
$query->whereTime($timeKey, $value);
break;
case 'quarter':
list($startTime, $endTime) = static::getMonth();
$query->whereTime($timeKey, 'between', [$startTime, $endTime]);
break;
case 'lately7':
$query->whereTime($timeKey, 'between', [strtotime("-7 day"), time()]);
break;
case 'lately30':
$query->whereTime($timeKey, 'between', [strtotime("-30 day"), time()]);
break;
default:
if (strstr($value, '---') !== false||strstr($value, '-') !== false) {
if(strstr($value, '---') !== false){
[$startTime, $endTime] = explode('---', $value);
}elseif (strstr($value, '-') !== false){
[$startTime, $endTime] = explode('-', $value);
}
$startTime = trim($startTime);
$endTime = trim($endTime);
if ($startTime && $endTime) {
$query->whereTime($timeKey, 'between', [strtotime($startTime), $startTime == $endTime ? strtotime($endTime) + 86400 : strtotime($endTime)]);
} else if (!$startTime && $endTime) {
$query->whereTime($timeKey, '<', strtotime($endTime) + 86400);
} else if ($startTime && !$endTime) {
$query->whereTime($timeKey, '>=', strtotime($startTime));
}
}
break;
}
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace app\common\model;
use think\Model;
class ManystoreAttachment extends Model
{
// 开启自动写入时间戳字段
protected $autoWriteTimestamp = 'int';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = 'updatetime';
// 定义字段类型
protected $type = [
];
protected $append = [
'thumb_style'
];
public function setUploadtimeAttr($value)
{
return is_numeric($value) ? $value : strtotime($value);
}
/**
* 获取云储存的缩略图样式字符
*/
public function getThumbStyleAttr($value, $data)
{
if (!isset($data['storage']) || $data['storage'] == 'local') {
return '';
} else {
$config = get_addon_config($data['storage']);
if ($config && isset($config['thumbstyle'])) {
return $config['thumbstyle'];
}
}
return '';
}
public static function getMimetypeList()
{
$data = [
"image/*" => __("Image"),
"audio/*" => __("Audio"),
"video/*" => __("Video"),
"text/*" => __("Text"),
"application/*" => __("Application"),
"zip,rar,7z,tar" => __("Zip"),
];
return $data;
}
protected static function init()
{
// 如果已经上传该资源,则不再记录
self::beforeInsert(function ($model) {
if (self::where('url', '=', $model['url'])->where(array('shop_id'=>$model['shop_id']))->where('storage', $model['storage'])->find()) {
return false;
}
});
}
}

View File

@ -0,0 +1,147 @@
<?php
namespace app\common\model;
use think\Cache;
use think\Model;
class ManystoreConfig extends Model
{
// 表名
protected $name = 'manystore_config';
// 自动写入时间戳字段
protected $autoWriteTimestamp = false;
// 定义时间戳字段名
protected $createTime = false;
protected $updateTime = false;
protected $deleteTime = false;
// 追加属性
protected $append = [
];
protected $type = [
'setting' => 'json',
];
public static function manystore_config($shop_id = null){
if(is_null($shop_id)){
if(!defined('SHOP_ID')){
return [];
}
$shop_id = SHOP_ID;
}
$seller_value_model = new ManystoreValue();
$config = cache('ManystoreConfig:'.$shop_id);
if(!$config){
$config_value_data_array = [];
$config_value_data = collection($seller_value_model->where(array('shop_id' => $shop_id))->select())->toArray();
foreach ($config_value_data as $value) {
$config_value_data_array[$value['config_id']] = $value;
}
$config = [];
foreach (self::select() as $k => $v) {
$value = $v->toArray();
$data_value = isset($config_value_data_array[$value['id']]['value']) ? $config_value_data_array[$value['id']]['value'] : $value['default'];
if (in_array($value['type'], ['selects', 'checkbox', 'images', 'files'])) {
$value['value'] = explode(',', $data_value);
} else if ($value['type'] == 'array') {
$value['value'] = (array)json_decode($data_value, TRUE);
} else {
$value['value'] = $data_value;
}
$config[$value['name']] = $value['value'];
}
cache('ManystoreConfig:'.$shop_id,$config,null, 'ShopCacheTag:'.$shop_id);
}
return !empty($config) ? $config : [];
}
public static function getArrayData($data)
{
$fieldarr = $valuearr = [];
$field = isset($data['field']) ? $data['field'] : [];
$value = isset($data['value']) ? $data['value'] : [];
foreach ($field as $m => $n) {
if ($n != '') {
$fieldarr[] = $field[$m];
$valuearr[] = $value[$m];
}
}
return $fieldarr ? array_combine($fieldarr, $valuearr) : [];
}
/**
* 将字符串解析成键值数组
* @param string $text
* @return array
*/
public static function decode($text, $split = "\r\n")
{
$content = explode($split, $text);
$arr = [];
foreach ($content as $k => $v) {
if (stripos($v, "|") !== false) {
$item = explode('|', $v);
$arr[$item[0]] = $item[1];
}
}
return $arr;
}
/**
* 将键值数组转换为字符串
* @param array $array
* @return string
*/
public static function encode($array, $split = "\r\n")
{
$content = '';
if ($array && is_array($array)) {
$arr = [];
foreach ($array as $k => $v) {
$arr[] = "{$k}|{$v}";
}
$content = implode($split, $arr);
}
return $content;
}
/**
* 本地上传配置信息
* @return array
*/
public static function upload()
{
$uploadcfg = config('upload');
$upload = [
'cdnurl' => $uploadcfg['cdnurl'],
'uploadurl' => $uploadcfg['uploadurl'],
'bucket' => 'local',
'maxsize' => $uploadcfg['maxsize'],
'mimetype' => $uploadcfg['mimetype'],
'multipart' => [],
'multiple' => $uploadcfg['multiple'],
];
return $upload;
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace app\common\model;
use think\Cache;
use think\Model;
class ManystoreConfigGroup extends Model
{
// 表名
protected $name = 'manystore_config_group';
// 自动写入时间戳字段
protected $autoWriteTimestamp = false;
// 定义时间戳字段名
protected $createTime = false;
protected $updateTime = false;
protected $deleteTime = false;
// 追加属性
protected $append = [
];
public function getGroupData(){
$store_config_data = Cache::get('manystore_config_data');
if(!$store_config_data){
$data = $this->select();
if(!empty($data)){
foreach ($data as $value){
$store_config_data[$value['unique']] = $value['name'];
}
Cache::set('manystore_config_data',$store_config_data);
}
}
return !empty($store_config_data) ? $store_config_data : [];
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace app\common\model;
use think\Model;
/**
* 配置模型
*/
class ManystoreValue extends Model
{
// 表名,不含前缀
protected $name = 'manystore_value';
// 自动写入时间戳字段
protected $autoWriteTimestamp = false;
// 定义时间戳字段名
protected $createTime = false;
protected $updateTime = false;
// 追加属性
protected $append = [
];
}

View File

@ -0,0 +1,27 @@
<?php
namespace app\common\validate;
use think\Validate;
class ManystoreConfig extends Validate
{
/**
* 验证规则
*/
protected $rule = [
];
/**
* 提示消息
*/
protected $message = [
];
/**
* 验证场景
*/
protected $scene = [
'add' => [],
'edit' => [],
];
}

View File

@ -0,0 +1,27 @@
<?php
namespace app\common\validate;
use think\Validate;
class ManystoreConfigGroup extends Validate
{
/**
* 验证规则
*/
protected $rule = [
];
/**
* 提示消息
*/
protected $message = [
];
/**
* 验证场景
*/
protected $scene = [
'add' => [],
'edit' => [],
];
}

View File

@ -18,7 +18,7 @@ return [
// 应用命名空间 // 应用命名空间
'app_namespace' => 'app', 'app_namespace' => 'app',
// 应用调试模式 // 应用调试模式
'app_debug' => Env::get('app.debug', false), 'app_debug' => Env::get('app.debug', true),
// 应用Trace // 应用Trace
'app_trace' => Env::get('app.trace', false), 'app_trace' => Env::get('app.trace', false),
// 应用模式状态 // 应用模式状态
@ -159,7 +159,7 @@ return [
// 错误显示信息,非调试模式有效 // 错误显示信息,非调试模式有效
'error_message' => '你所浏览的页面暂时无法访问', 'error_message' => '你所浏览的页面暂时无法访问',
// 显示错误信息 // 显示错误信息
'show_error_msg' => false, 'show_error_msg' => true,
// 异常处理handle类 留空使用 \think\exception\Handle // 异常处理handle类 留空使用 \think\exception\Handle
'exception_handle' => '', 'exception_handle' => '',
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------

View File

@ -14,6 +14,7 @@ return [
], ],
'app_init' => [ 'app_init' => [
'barcode', 'barcode',
'manystore',
'qrcode', 'qrcode',
'xilufitness', 'xilufitness',
], ],
@ -32,10 +33,17 @@ return [
'action_begin' => [ 'action_begin' => [
'clicaptcha', 'clicaptcha',
'csmtable', 'csmtable',
'epay',
], ],
'captcha_mode' => [ 'captcha_mode' => [
'clicaptcha', 'clicaptcha',
], ],
'epay_config_init' => [
'epay',
],
'addon_action_begin' => [
'epay',
],
'upgrade' => [ 'upgrade' => [
'famysql', 'famysql',
'xilufitness', 'xilufitness',
@ -43,6 +51,9 @@ return [
'admin_login_init' => [ 'admin_login_init' => [
'loginbg', 'loginbg',
], ],
'upload_config_checklogin' => [
'manystore',
],
], ],
'route' => [ 'route' => [
'/barcode$' => 'barcode/index/index', '/barcode$' => 'barcode/index/index',

View File

@ -4,7 +4,7 @@ return array (
'name' => '多样青春夜校', 'name' => '多样青春夜校',
'beian' => '', 'beian' => '',
'cdnurl' => '', 'cdnurl' => '',
'version' => '1.0.2', 'version' => '1.0.3',
'timezone' => 'Asia/Shanghai', 'timezone' => 'Asia/Shanghai',
'forbiddenip' => '', 'forbiddenip' => '',
'languages' => 'languages' =>
@ -12,21 +12,22 @@ return array (
'backend' => 'zh-cn', 'backend' => 'zh-cn',
'frontend' => 'zh-cn', 'frontend' => 'zh-cn',
), ),
'fixedpage' => 'dashboard', 'fixedpage' => 'xilufitness/analyse/index',
'categorytype' => 'categorytype' =>
array ( array (
'default' => 'Default', 'default' => '默认',
'page' => 'Page', 'page' => '单页',
'article' => 'Article', 'article' => '文章',
'test' => 'Test', 'test' => 'Test',
), ),
'configgroup' => 'configgroup' =>
array ( array (
'basic' => 'Basic', 'basic' => '基础配置',
'email' => 'Email', 'email' => '邮件配置',
'dictionary' => 'Dictionary', 'dictionary' => '字典配置',
'user' => 'User', 'user' => '会员配置',
'example' => 'Example', 'example' => '示例分组',
'wx_miniapp' => '微信小程序配置',
), ),
'mail_type' => '1', 'mail_type' => '1',
'mail_smtp_host' => 'smtp.qq.com', 'mail_smtp_host' => 'smtp.qq.com',
@ -37,8 +38,10 @@ return array (
'mail_from' => '', 'mail_from' => '',
'attachmentcategory' => 'attachmentcategory' =>
array ( array (
'category1' => 'Category1', 'category1' => '分类一',
'category2' => 'Category2', 'category2' => '分类二',
'custom' => 'Custom', 'custom' => '自定义',
), ),
'wx_miniapp_id' => 'wxd7e2deffbaa22254',
'wx_miniapp_secret' => '573964aee57c334619396d4b6c05497d',
); );

View File

@ -17,11 +17,11 @@ return [
/** /**
* 最大可上传大小 * 最大可上传大小
*/ */
'maxsize' => '10mb', 'maxsize' => '100mb',
/** /**
* 可上传的文件类型 * 可上传的文件类型
*/ */
'mimetype' => 'jpg,png,bmp,jpeg,gif,webp,zip,rar,wav,mp4,mp3,webm', 'mimetype' => 'jpg,png,bmp,jpeg,gif,webp,zip,rar,wav,mp4,mp3,webm,p12,pem',
/** /**
* 是否支持批量上传 * 是否支持批量上传
*/ */

View File

@ -0,0 +1,16 @@
<?php
return array (
0 =>
array (
'mini_appid' => 'wxd7e2deffbaa22254',
'mini_appsecret' => '573964aee57c334619396d4b6c05497d',
'mini_mch_id' => '1692577572',
'mini_mch_key' => 'SbgWV9UEt4MymmKJ6ubvln6Z4LKrRGMR',
'mini_mch_p12' => '/uploads/20241104/926a281428b02f7303d5089483daac3d.p12',
'mini_mch_pem_key' => '/uploads/20241104/f8eec0e43cdf602cf0705f3f66d55b0b.pem',
'mini_mch_pem_cert' => '/uploads/20241104/dc08598134dc25504fde79e629886e2a.pem',
'mini_ios_switch' => '0',
'mini_mch_key_three' => 'KUaJjRWcVQ6vNkWhzqb3kXww9DfocsGg',
),
);

View File

@ -0,0 +1,13 @@
<?php
namespace app\manystore\behavior;
class ManystoreLog
{
public function run(&$params)
{
if (request()->isPost() && config('fastadmin.auto_record_log')) {
\app\manystore\model\ManystoreLog::record();
}
}
}

View File

@ -0,0 +1,226 @@
<?php
use app\common\model\Category;
use fast\Form;
use fast\Tree;
use think\Db;
if (!function_exists('build_select')) {
/**
* 生成下拉列表
* @param string $name
* @param mixed $options
* @param mixed $selected
* @param mixed $attr
* @return string
*/
function build_select($name, $options, $selected = [], $attr = [])
{
$options = is_array($options) ? $options : explode(',', $options);
$selected = is_array($selected) ? $selected : explode(',', $selected);
return Form::select($name, $options, $selected, $attr);
}
}
if (!function_exists('build_radios')) {
/**
* 生成单选按钮组
* @param string $name
* @param array $list
* @param mixed $selected
* @return string
*/
function build_radios($name, $list = [], $selected = null)
{
$html = [];
$selected = is_null($selected) ? key($list) : $selected;
$selected = is_array($selected) ? $selected : explode(',', $selected);
foreach ($list as $k => $v) {
$html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::radio($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"]));
}
return '<div class="radio">' . implode(' ', $html) . '</div>';
}
}
if (!function_exists('build_checkboxs')) {
/**
* 生成复选按钮组
* @param string $name
* @param array $list
* @param mixed $selected
* @return string
*/
function build_checkboxs($name, $list = [], $selected = null)
{
$html = [];
$selected = is_null($selected) ? [] : $selected;
$selected = is_array($selected) ? $selected : explode(',', $selected);
foreach ($list as $k => $v) {
$html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::checkbox($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"]));
}
return '<div class="checkbox">' . implode(' ', $html) . '</div>';
}
}
if (!function_exists('build_category_select')) {
/**
* 生成分类下拉列表框
* @param string $name
* @param string $type
* @param mixed $selected
* @param array $attr
* @param array $header
* @return string
*/
function build_category_select($name, $type, $selected = null, $attr = [], $header = [])
{
$tree = Tree::instance();
$tree->init(Category::getCategoryArray($type), 'pid');
$categorylist = $tree->getTreeList($tree->getTreeArray(0), 'name');
$categorydata = $header ? $header : [];
foreach ($categorylist as $k => $v) {
$categorydata[$v['id']] = $v['name'];
}
$attr = array_merge(['id' => "c-{$name}", 'class' => 'form-control selectpicker'], $attr);
return build_select($name, $categorydata, $selected, $attr);
}
}
if (!function_exists('build_toolbar')) {
/**
* 生成表格操作按钮栏
* @param array $btns 按钮组
* @param array $attr 按钮属性值
* @return string
*/
function build_toolbar($btns = null, $attr = [])
{
$auth = \app\manystore\library\Auth::instance();
$controller = str_replace('.', '/', strtolower(think\Request::instance()->controller()));
$btns = $btns ? $btns : ['refresh', 'add', 'edit', 'del', 'import'];
$btns = is_array($btns) ? $btns : explode(',', $btns);
$index = array_search('delete', $btns);
if ($index !== false) {
$btns[$index] = 'del';
}
$btnAttr = [
'refresh' => ['javascript:;', 'btn btn-primary btn-refresh', 'fa fa-refresh', '', __('Refresh')],
'add' => ['javascript:;', 'btn btn-success btn-add', 'fa fa-plus', __('Add'), __('Add')],
'edit' => ['javascript:;', 'btn btn-success btn-edit btn-disabled disabled', 'fa fa-pencil', __('Edit'), __('Edit')],
'del' => ['javascript:;', 'btn btn-danger btn-del btn-disabled disabled', 'fa fa-trash', __('Delete'), __('Delete')],
'import' => ['javascript:;', 'btn btn-info btn-import', 'fa fa-upload', __('Import'), __('Import')],
];
$btnAttr = array_merge($btnAttr, $attr);
$html = [];
foreach ($btns as $k => $v) {
//如果未定义或没有权限
if (!isset($btnAttr[$v]) || ($v !== 'refresh' && !$auth->check("{$controller}/{$v}"))) {
continue;
}
list($href, $class, $icon, $text, $title) = $btnAttr[$v];
//$extend = $v == 'import' ? 'id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"' : '';
//$html[] = '<a href="' . $href . '" class="' . $class . '" title="' . $title . '" ' . $extend . '><i class="' . $icon . '"></i> ' . $text . '</a>';
if ($v == 'import') {
$template = str_replace('/', '_', $controller);
$download = '';
if (file_exists("./template/{$template}.xlsx")) {
$download .= "<li><a href=\"/template/{$template}.xlsx\" target=\"_blank\">XLSX模版</a></li>";
}
if (file_exists("./template/{$template}.xls")) {
$download .= "<li><a href=\"/template/{$template}.xls\" target=\"_blank\">XLS模版</a></li>";
}
if (file_exists("./template/{$template}.csv")) {
$download .= empty($download) ? '' : "<li class=\"divider\"></li>";
$download .= "<li><a href=\"/template/{$template}.csv\" target=\"_blank\">CSV模版</a></li>";
}
$download .= empty($download) ? '' : "\n ";
if (!empty($download)) {
$html[] = <<<EOT
<div class="btn-group">
<button type="button" href="{$href}" class="btn btn-info btn-import" title="{$title}" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="{$icon}"></i> {$text}</button>
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" title="下载批量导入模版">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu" role="menu">{$download}</ul>
</div>
EOT;
} else {
$html[] = '<a href="' . $href . '" class="' . $class . '" title="' . $title . '" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="' . $icon . '"></i> ' . $text . '</a>';
}
} else {
$html[] = '<a href="' . $href . '" class="' . $class . '" title="' . $title . '"><i class="' . $icon . '"></i> ' . $text . '</a>';
}
}
return implode(' ', $html);
}
}
if (!function_exists('build_heading')) {
/**
* 生成页面Heading
*
* @param string $path 指定的path
* @return string
*/
function build_heading($path = null, $container = true)
{
$title = $content = '';
if (is_null($path)) {
$action = request()->action();
$controller = str_replace('.', '/', request()->controller());
$path = strtolower($controller . ($action && $action != 'index' ? '/' . $action : ''));
}
// 根据当前的URI自动匹配父节点的标题和备注
$data = Db::name('auth_rule')->where('name', $path)->field('title,remark')->find();
if ($data) {
$title = __($data['title']);
$content = __($data['remark']);
}
if (!$content) {
return '';
}
$result = '<div class="panel-lead"><em>' . $title . '</em>' . $content . '</div>';
if ($container) {
$result = '<div class="panel-heading">' . $result . '</div>';
}
return $result;
}
}
if (!function_exists('build_suffix_image')) {
/**
* 生成文件后缀图片
* @param string $suffix 后缀
* @param null $background
* @return string
*/
function build_suffix_image($suffix, $background = null)
{
$suffix = mb_substr(strtoupper($suffix), 0, 4);
$total = unpack('L', hash('adler32', $suffix, true))[1];
$hue = $total % 360;
list($r, $g, $b) = hsv2rgb($hue / 360, 0.3, 0.9);
$background = $background ? $background : "rgb({$r},{$g},{$b})";
$icon = <<<EOT
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.6,14.4,32,32,32h320c17.6,0,32-14.4,32-32V128L352,0H128z"/>
<path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
<polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
<path style="fill:{$background};" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16 V416z"/>
<path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
<g><text><tspan x="220" y="380" font-size="124" font-family="Verdana, Helvetica, Arial, sans-serif" fill="white" text-anchor="middle">{$suffix}</tspan></text></g>
</svg>
EOT;
return $icon;
}
}

View File

@ -0,0 +1,8 @@
<?php
//配置文件
return [
'url_common_param' => true,
'url_html_suffix' => '',
'controller_auto_search' => true,
];

View File

@ -0,0 +1,284 @@
<?php
namespace app\manystore\controller;
use app\common\controller\ManystoreBase;
use fast\Random;
use think\addons\Service;
use think\Cache;
use think\Config;
use think\Db;
use think\Lang;
/**
* Ajax异步请求接口
* @internal
*/
class Ajax extends ManystoreBase
{
protected $noNeedLogin = ['lang'];
protected $noNeedRight = ['*'];
protected $layout = '';
public function _initialize()
{
parent::_initialize();
//设置过滤方法
$this->request->filter(['strip_tags', 'htmlspecialchars']);
}
/**
* 加载语言包
*/
public function lang()
{
header('Content-Type: application/javascript');
$controllername = input("controllername");
//默认只加载了控制器对应的语言名,你还根据控制器名来加载额外的语言包
$this->loadlang($controllername);
return jsonp(Lang::get(), 200, [], ['json_encode_param' => JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE]);
}
/**
* 上传文件
*/
public function upload()
{
Config::set('default_return_type', 'json');
$file = $this->request->file('file');
if (empty($file)) {
$this->error(__('No file upload or server upload limit exceeded'));
}
//判断是否已经存在附件
$sha1 = $file->hash();
$extparam = $this->request->post();
$upload = Config::get('upload');
preg_match('/(\d+)(\w+)/', $upload['maxsize'], $matches);
$type = strtolower($matches[2]);
$typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3];
$size = (int)$upload['maxsize'] * pow(1024, isset($typeDict[$type]) ? $typeDict[$type] : 0);
$fileInfo = $file->getInfo();
$suffix = strtolower(pathinfo($fileInfo['name'], PATHINFO_EXTENSION));
$suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file';
$fileInfo['suffix'] = $suffix;
$mimetypeArr = explode(',', strtolower($upload['mimetype']));
$typeArr = explode('/', $fileInfo['type']);
//禁止上传PHP和HTML文件
if (in_array($fileInfo['type'], ['text/x-php', 'text/html']) || in_array($suffix, ['php', 'html', 'htm', 'phar', 'phtml']) || preg_match("/^php(.*)/i", $fileInfo['suffix'])) {
$this->error(__('Uploaded file format is limited'));
}
//Mimetype值不正确
if (stripos($fileInfo['type'], '/') === false) {
$this->error(__('Uploaded file format is limited'));
}
//验证文件后缀
if ($upload['mimetype'] !== '*' &&
(
!in_array($suffix, $mimetypeArr)
|| (stripos($typeArr[0] . '/', $upload['mimetype']) !== false && (!in_array($fileInfo['type'], $mimetypeArr) && !in_array($typeArr[0] . '/*', $mimetypeArr)))
)
) {
$this->error(__('Uploaded file format is limited'));
}
//验证是否为图片文件
$imagewidth = $imageheight = 0;
if (in_array($fileInfo['type'], ['image/gif', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/png', 'image/webp']) || in_array($suffix, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'webp'])) {
$imgInfo = getimagesize($fileInfo['tmp_name']);
if (!$imgInfo || !isset($imgInfo[0]) || !isset($imgInfo[1])) {
$this->error(__('Uploaded file is not a valid image'));
}
$imagewidth = isset($imgInfo[0]) ? $imgInfo[0] : $imagewidth;
$imageheight = isset($imgInfo[1]) ? $imgInfo[1] : $imageheight;
}
$replaceArr = [
'{year}' => date("Y"),
'{mon}' => date("m"),
'{day}' => date("d"),
'{hour}' => date("H"),
'{min}' => date("i"),
'{sec}' => date("s"),
'{random}' => Random::alnum(16),
'{random32}' => Random::alnum(32),
'{filename}' => $suffix ? substr($fileInfo['name'], 0, strripos($fileInfo['name'], '.')) : $fileInfo['name'],
'{suffix}' => $suffix,
'{.suffix}' => $suffix ? '.' . $suffix : '',
'{filemd5}' => md5_file($fileInfo['tmp_name']),
];
$savekey = $upload['savekey'];
$savekey = str_replace(array_keys($replaceArr), array_values($replaceArr), $savekey);
$uploadDir = substr($savekey, 0, strripos($savekey, '/') + 1);
$fileName = substr($savekey, strripos($savekey, '/') + 1);
//
$splInfo = $file->validate(['size' => $size])->move(ROOT_PATH . '/public' . $uploadDir, $fileName);
if ($splInfo) {
$params = array(
'shop_id' => (int)SHOP_ID,
'user_id' => 0,
'filesize' => $fileInfo['size'],
'imagewidth' => $imagewidth,
'imageheight' => $imageheight,
'imagetype' => $suffix,
'imageframes' => 0,
'mimetype' => $fileInfo['type'],
'url' => $uploadDir . $splInfo->getSaveName(),
'uploadtime' => time(),
'storage' => 'local',
'sha1' => $sha1,
'extparam' => json_encode($extparam),
);
$attachment = model("ManystoreAttachment");
$attachment->data(array_filter($params));
$attachment->save();
\think\Hook::listen("upload_after", $attachment);
$this->success(__('Upload successful'), null, [
'url' => $uploadDir . $splInfo->getSaveName()
]);
} else {
// 上传失败获取错误信息
$this->error($file->getError());
}
}
/**
* 通用排序
*/
public function weigh()
{
//排序的数组
$ids = $this->request->post("ids");
//拖动的记录ID
$changeid = $this->request->post("changeid");
//操作字段
$field = $this->request->post("field");
//操作的数据表
$table = $this->request->post("table");
//主键
$pk = $this->request->post("pk");
//排序的方式
$orderway = $this->request->post("orderway", "", 'strtolower');
$orderway = $orderway == 'asc' ? 'ASC' : 'DESC';
$sour = $weighdata = [];
$ids = explode(',', $ids);
$prikey = $pk ? $pk : (Db::name($table)->getPk() ?: 'id');
$pid = $this->request->post("pid");
//限制更新的字段
$field = in_array($field, ['weigh']) ? $field : 'weigh';
// 如果设定了pid的值,此时只匹配满足条件的ID,其它忽略
if ($pid !== '') {
$hasids = [];
$list = Db::name($table)->where($prikey, 'in', $ids)->where('pid', 'in', $pid)->field("{$prikey},pid")->select();
foreach ($list as $k => $v) {
$hasids[] = $v[$prikey];
}
$ids = array_values(array_intersect($ids, $hasids));
}
$list = Db::name($table)->field("$prikey,$field")->where($prikey, 'in', $ids)->order($field, $orderway)->select();
foreach ($list as $k => $v) {
$sour[] = $v[$prikey];
$weighdata[$v[$prikey]] = $v[$field];
}
$position = array_search($changeid, $ids);
$desc_id = $sour[$position]; //移动到目标的ID值,取出所处改变前位置的值
$sour_id = $changeid;
$weighids = array();
$temp = array_values(array_diff_assoc($ids, $sour));
foreach ($temp as $m => $n) {
if ($n == $sour_id) {
$offset = $desc_id;
} else {
if ($sour_id == $temp[0]) {
$offset = isset($temp[$m + 1]) ? $temp[$m + 1] : $sour_id;
} else {
$offset = isset($temp[$m - 1]) ? $temp[$m - 1] : $sour_id;
}
}
$weighids[$n] = $weighdata[$offset];
Db::name($table)->where($prikey, $n)->update([$field => $weighdata[$offset]]);
}
$this->success();
}
/**
* 清空系统缓存
*/
public function wipecache()
{
$type = $this->request->request("type");
switch ($type) {
case 'all':
case 'content':
Cache::clear('ShopCacheTag'.SHOP_ID);
if ($type == 'content')
break;
}
\think\Hook::listen("wipecache_after");
$this->success();
}
/**
* 读取分类数据,联动列表
*/
public function category()
{
$type = $this->request->get('type');
$pid = $this->request->get('pid');
$where = ['status' => 'normal'];
$categorylist = null;
if ($pid !== '') {
if ($type) {
$where['type'] = $type;
}
if ($pid) {
$where['pid'] = $pid;
}
$categorylist = Db::name('category')->where($where)->field('id as value,name')->order('weigh desc,id desc')->select();
}
$this->success('', null, $categorylist);
}
/**
* 读取省市区数据,联动列表
*/
public function area()
{
$params = $this->request->get("row/a");
if (!empty($params)) {
$province = isset($params['province']) ? $params['province'] : '';
$city = isset($params['city']) ? $params['city'] : null;
} else {
$province = $this->request->get('province');
$city = $this->request->get('city');
}
$where = ['pid' => 0, 'level' => 1];
$provincelist = null;
if ($province !== '') {
if ($province) {
$where['pid'] = $province;
$where['level'] = 2;
}
if ($city !== '') {
if ($city) {
$where['pid'] = $city;
$where['level'] = 3;
}
$provincelist = Db::name('area')->where($where)->field('id as value,name')->select();
}
}
$this->success('', null, $provincelist);
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace app\manystore\controller;
use app\common\controller\ManystoreBase;
use think\Config;
/**
* 控制台
*
* @icon fa fa-dashboard
* @remark 用于展示当前系统中的统计数据、统计报表及重要实时数据
*/
class Dashboard extends ManystoreBase
{
/**
* 查看
*/
public function index()
{
$seventtime = \fast\Date::unixtime('day', -7);
$paylist = $createlist = [];
for ($i = 0; $i < 7; $i++)
{
$day = date("Y-m-d", $seventtime + ($i * 86400));
$createlist[$day] = mt_rand(20, 200);
$paylist[$day] = mt_rand(1, mt_rand(1, $createlist[$day]));
}
$hooks = config('addons.hooks');
$uploadmode = isset($hooks['upload_config_init']) && $hooks['upload_config_init'] ? implode(',', $hooks['upload_config_init']) : 'local';
$addonComposerCfg = ROOT_PATH . '/vendor/karsonzhang/fastadmin-addons/composer.json';
Config::parse($addonComposerCfg, "json", "composer");
$config = Config::get("composer");
$addonVersion = isset($config['version']) ? $config['version'] : __('Unknown');
$this->view->assign([
'totaluser' => 35200,
'totalviews' => 219390,
'totalorder' => 32143,
'totalorderamount' => 174800,
'todayuserlogin' => 321,
'todayusersignup' => 430,
'todayorder' => 2324,
'unsettleorder' => 132,
'sevendnu' => '80%',
'sevendau' => '32%',
'paylist' => $paylist,
'createlist' => $createlist,
'addonversion' => $addonVersion,
'uploadmode' => $uploadmode
]);
return $this->view->fetch();
}
}

View File

@ -0,0 +1,130 @@
<?php
namespace app\manystore\controller;
use app\manystore\model\ManystoreLog;
use app\common\controller\ManystoreBase;
use think\Config;
use think\Hook;
use think\Validate;
/**
* 后台首页
* @internal
*/
class Index extends ManystoreBase
{
protected $noNeedLogin = ['login'];
protected $noNeedRight = ['index', 'logout'];
protected $layout = '';
public function _initialize()
{
parent::_initialize();
//移除HTML标签
$this->request->filter('trim,strip_tags,htmlspecialchars');
}
/**
* 后台首页
*/
public function index()
{
//左侧菜单
$cookieArr = ['adminskin' => "/^skin\-([a-z\-]+)\$/i", 'multiplenav' => "/^(0|1)\$/", 'multipletab' => "/^(0|1)\$/", 'show_submenu' => "/^(0|1)\$/"];
foreach ($cookieArr as $key => $regex) {
$cookieValue = $this->request->cookie($key);
if (!is_null($cookieValue) && preg_match($regex, $cookieValue)) {
config('fastadmin.' . $key, $cookieValue);
}
}
list($menulist, $navlist, $fixedmenu, $referermenu) = $this->auth->getSidebar([
'dashboard' => 'hot',
'addon' => ['new', 'red', 'badge'],
'auth/rule' => __('Menu'),
'general' => ['new', 'purple'],
], $this->view->site['fixedpage']);
$action = $this->request->request('action');
if ($this->request->isPost()) {
if ($action == 'refreshmenu') {
$this->success('', null, ['menulist' => $menulist, 'navlist' => $navlist]);
}
}
$this->assignconfig('cookie', ['prefix' => config('cookie.prefix')]);
$this->view->assign('menulist', $menulist);
$this->view->assign('navlist', $navlist);
$this->view->assign('fixedmenu', $fixedmenu);
$this->view->assign('referermenu', $referermenu);
$this->view->assign('title', __('Home'));
return $this->view->fetch();
}
/**
* 管理员登录
*/
public function login()
{
$url = $this->request->get('url', 'index/index');
if ($this->auth->isLogin()) {
$this->success(__("You've logged in, do not login again"), $url);
}
if ($this->request->isPost()) {
$username = $this->request->post('username');
$password = $this->request->post('password');
$keeplogin = $this->request->post('keeplogin');
$token = $this->request->post('__token__');
$rule = [
'username' => 'require|length:3,30',
'password' => 'require|length:3,30',
'__token__' => 'require|token',
];
$data = [
'username' => $username,
'password' => $password,
'__token__' => $token,
];
if (Config::get('fastadmin.login_captcha')) {
$rule['captcha'] = 'require|captcha';
$data['captcha'] = $this->request->post('captcha');
}
$validate = new Validate($rule, [], ['username' => __('Username'), 'password' => __('Password'), 'captcha' => __('Captcha')]);
$result = $validate->check($data);
if (!$result) {
$this->error($validate->getError(), $url, ['token' => $this->request->token()]);
}
ManystoreLog::setTitle(__('Login'));
$result = $this->auth->login($username, $password, $keeplogin ? 86400 : 0);
if ($result === true) {
Hook::listen("admin_login_after", $this->request);
$this->success(__('Login successful'), $url, ['url' => $url, 'id' => $this->auth->id, 'username' => $username, 'avatar' => $this->auth->avatar]);
} else {
$msg = $this->auth->getError();
$msg = $msg ? $msg : __('Username or password is incorrect');
$this->error($msg, $url, ['token' => $this->request->token()]);
}
}
// 根据客户端的cookie,判断是否可以自动登录
if ($this->auth->autologin()) {
$this->redirect($url);
}
$background = Config::get('fastadmin.login_background');
$background = stripos($background, 'http') === 0 ? $background : config('site.cdnurl') . $background;
$this->view->assign('background', $background);
$this->view->assign('title', __('Login'));
Hook::listen("admin_login_init", $this->request);
return $this->view->fetch();
}
/**
* 注销登录
*/
public function logout()
{
$this->auth->logout();
Hook::listen("manystore_logout_after", $this->request);
$this->success(__('Logout successful'), 'index/login');
}
}

View File

@ -0,0 +1,299 @@
<?php
namespace app\manystore\controller\auth;
use app\manystore\model\ManystoreAuthGroup;
use app\common\controller\ManystoreBase;
use fast\Tree;
/**
* 角色组
*
* @icon fa fa-group
* @remark 角色组可以有多个,角色有上下级层级关系,如果子角色有角色组和管理员的权限则可以派生属于自己组别下级的角色组或管理员
*/
class Group extends ManystoreBase
{
/**
* @var \app\manystore\model\AuthGroup
*/
protected $model = null;
//当前登录管理员所有子组别
protected $childrenGroupIds = [];
//当前组别列表数据
protected $groupdata = [];
//无需要权限判断的方法
protected $noNeedRight = ['roletree'];
public function _initialize()
{
parent::_initialize();
$this->model = model('ManystoreAuthGroup');
$this->childrenGroupIds = $this->auth->getChildrenGroupIds(true);
$groupList = collection(ManystoreAuthGroup::where('id', 'in', $this->childrenGroupIds)->select())->toArray();
Tree::instance()->init($groupList);
$result = [];
if ($this->auth->isSuperAdmin()) {
$result = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0));
} else {
$groups = $this->auth->getGroups();
foreach ($groups as $m => $n) {
$result = array_merge($result, Tree::instance()->getTreeList(Tree::instance()->getTreeArray($n['pid'])));
}
}
$groupName = [];
foreach ($result as $k => $v) {
$groupName[$v['id']] = $v['name'];
}
$this->groupdata = $groupName;
$this->assignconfig("admin", ['id' => $this->auth->id, 'group_ids' => $this->auth->getGroupIds()]);
$this->view->assign('groupdata', $this->groupdata);
}
/**
* 查看
*/
public function index()
{
if ($this->request->isAjax()) {
$list = ManystoreAuthGroup::all(array_keys($this->groupdata));
$list = collection($list)->toArray();
$groupList = [];
foreach ($list as $k => $v) {
$groupList[$v['id']] = $v;
}
$list = [];
foreach ($this->groupdata as $k => $v) {
if (isset($groupList[$k])) {
$groupList[$k]['name'] = $v;
$list[] = $groupList[$k];
}
}
$total = count($list);
$result = array("total" => $total, "rows" => $list);
return json($result);
}
return $this->view->fetch();
}
/**
* 添加
*/
public function add()
{
if ($this->request->isPost()) {
$this->token();
$params = $this->request->post("row/a", [], 'strip_tags');
$params['shop_id'] = SHOP_ID;
$params['rules'] = explode(',', $params['rules']);
if (!in_array($params['pid'], $this->childrenGroupIds)) {
$this->error(__('The parent group can not be its own child'));
}
$parentmodel = model("ManystoreAuthGroup")->get($params['pid']);
if (!$parentmodel) {
$this->error(__('The parent group can not found'));
}
// 父级别的规则节点
$parentrules = explode(',', $parentmodel->rules);
// 当前组别的规则节点
$currentrules = $this->auth->getRuleIds();
$rules = $params['rules'];
// 如果父组不是超级管理员则需要过滤规则节点,不能超过父组别的权限
$rules = in_array('*', $parentrules) ? $rules : array_intersect($parentrules, $rules);
// 如果当前组别不是超级管理员则需要过滤规则节点,不能超当前组别的权限
$rules = in_array('*', $currentrules) ? $rules : array_intersect($currentrules, $rules);
$params['rules'] = implode(',', $rules);
if ($params) {
$result = $this->model->create($params);
if($result){
$this->success();
}else{
$this->error();
}
}
$this->error();
}
return $this->view->fetch();
}
/**
* 编辑
*/
public function edit($ids = null)
{
$row = $this->model->get(['id' => $ids,'shop_id'=>SHOP_ID]);
if (!$row) {
$this->error(__('No Results were found'));
}
if ($this->request->isPost()) {
$this->token();
$params = $this->request->post("row/a", [], 'strip_tags');
// 父节点不能是它自身的子节点
if (!in_array($params['pid'], $this->childrenGroupIds)) {
$this->error(__('The parent group can not be its own child'));
}
$params['rules'] = explode(',', $params['rules']);
$parentmodel = model("ManystoreAuthGroup")->get($params['pid']);
if (!$parentmodel) {
$this->error(__('The parent group can not found'));
}
// 父级别的规则节点
$parentrules = explode(',', $parentmodel->rules);
// 当前组别的规则节点
$currentrules = $this->auth->getRuleIds();
$rules = $params['rules'];
// 如果父组不是超级管理员则需要过滤规则节点,不能超过父组别的权限
$rules = in_array('*', $parentrules) ? $rules : array_intersect($parentrules, $rules);
// 如果当前组别不是超级管理员则需要过滤规则节点,不能超当前组别的权限
$rules = in_array('*', $currentrules) ? $rules : array_intersect($currentrules, $rules);
$params['rules'] = implode(',', $rules);
if ($params) {
$row->save($params);
$this->success();
}
$this->error();
return;
}
$this->view->assign("row", $row);
return $this->view->fetch();
}
/**
* 删除
*/
public function del($ids = "")
{
if ($ids) {
$ids = explode(',', $ids);
$grouplist = $this->auth->getGroups();
$group_ids = array_map(function ($group) {
return $group['id'];
}, $grouplist);
// 移除掉当前管理员所在组别
$ids = array_diff($ids, $group_ids);
// 循环判断每一个组别是否可删除
$grouplist = $this->model->where('id', 'in', $ids)->where(array('shop_id'=>SHOP_ID))->select();
$groupaccessmodel = model('ManystoreAuthGroupAccess');
foreach ($grouplist as $k => $v) {
// 当前组别下有管理员
$groupone = $groupaccessmodel->get(['group_id' => $v['id']]);
if ($groupone) {
$ids = array_diff($ids, [$v['id']]);
continue;
}
// 当前组别下有子组别
$groupone = $this->model->get(['pid' => $v['id']]);
if ($groupone) {
$ids = array_diff($ids, [$v['id']]);
continue;
}
}
if (!$ids) {
$this->error(__('You can not delete group that contain child group and administrators'));
}
$count = $this->model->where('id', 'in', $ids)->delete();
if ($count) {
$this->success();
}
}
$this->error();
}
/**
* 批量更新
* @internal
*/
public function multi($ids = "")
{
// 组别禁止批量操作
$this->error();
}
/**
* 读取角色权限树
*
* @internal
*/
public function roletree()
{
$this->loadlang('auth/group');
$model = model('ManystoreAuthGroup');
$id = $this->request->post("id");
$pid = $this->request->post("pid");
$parentGroupModel = $model->get($pid);
$currentGroupModel = null;
if ($id) {
$currentGroupModel = $model->get($id);
}
if (($pid || $parentGroupModel) && (!$id || $currentGroupModel)) {
$id = $id ? $id : null;
$ruleList = collection(model('ManystoreAuthRule')->order('weigh', 'desc')->order('id', 'asc')->select())->toArray();
//读取父类角色所有节点列表
$parentRuleList = [];
if (in_array('*', explode(',', $parentGroupModel->rules))) {
$parentRuleList = $ruleList;
} else {
$parentRuleIds = explode(',', $parentGroupModel->rules);
foreach ($ruleList as $k => $v) {
if (in_array($v['id'], $parentRuleIds)) {
$parentRuleList[] = $v;
}
}
}
$ruleTree = new Tree();
$groupTree = new Tree();
//当前所有正常规则列表
$ruleTree->init($parentRuleList);
//角色组列表
$groupTree->init(collection(model('ManystoreAuthGroup')->where(array('shop_id'=>SHOP_ID))->where('id', 'in', $this->childrenGroupIds)->select())->toArray());
//读取当前角色下规则ID集合
$adminRuleIds = $this->auth->getRuleIds();
//是否是超级管理员
$superadmin = $this->auth->isSuperAdmin();
//当前拥有的规则ID集合
$currentRuleIds = $id ? explode(',', $currentGroupModel->rules) : [];
if (!$id || !in_array($pid, $this->childrenGroupIds) || !in_array($pid, $groupTree->getChildrenIds($id, true))) {
$parentRuleList = $ruleTree->getTreeList($ruleTree->getTreeArray(0), 'name');
$hasChildrens = [];
foreach ($parentRuleList as $k => $v) {
if ($v['haschild']) {
$hasChildrens[] = $v['id'];
}
}
$parentRuleIds = array_map(function ($item) {
return $item['id'];
}, $parentRuleList);
$nodeList = [];
foreach ($parentRuleList as $k => $v) {
if (!$superadmin && !in_array($v['id'], $adminRuleIds)) {
continue;
}
if ($v['pid'] && !in_array($v['pid'], $parentRuleIds)) {
continue;
}
$state = array('selected' => in_array($v['id'], $currentRuleIds) && !in_array($v['id'], $hasChildrens));
$nodeList[] = array('id' => $v['id'], 'parent' => $v['pid'] ? $v['pid'] : '#', 'text' => __($v['title']), 'type' => 'menu', 'state' => $state);
}
$this->success('', null, $nodeList);
} else {
$this->error(__('Can not change the parent to child'));
}
} else {
$this->error(__('Group not found'));
}
}
}

View File

@ -0,0 +1,264 @@
<?php
namespace app\manystore\controller\auth;
use app\manystore\model\ManystoreAuthGroup;
use app\manystore\model\ManystoreAuthGroupAccess;
use app\common\controller\ManystoreBase;
use fast\Random;
use fast\Tree;
use think\Validate;
/**
* 管理员管理
*
* @icon fa fa-users
* @remark 一个管理员可以有多个角色组,左侧的菜单根据管理员所拥有的权限进行生成
*/
class Manystore extends ManystoreBase
{
/**
* @var \app\manystore\model\Manystore
*/
protected $model = null;
protected $childrenGroupIds = [];
protected $childrenAdminIds = [];
public function _initialize()
{
parent::_initialize();
$this->model = model('Manystore');
$this->childrenAdminIds = $this->auth->getChildrenAdminIds(true);
$this->childrenGroupIds = $this->auth->getChildrenGroupIds(true);
$groupList = collection(ManystoreAuthGroup::where('id', 'in', $this->childrenGroupIds)->where(array('shop_id'=>SHOP_ID))->select())->toArray();
Tree::instance()->init($groupList);
$groupdata = [];
if ($this->auth->isSuperAdmin()) {
$result = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0));
foreach ($result as $k => $v) {
$groupdata[$v['id']] = $v['name'];
}
} else {
$result = [];
$groups = $this->auth->getGroups();
foreach ($groups as $m => $n) {
$childlist = Tree::instance()->getTreeList(Tree::instance()->getTreeArray($n['id']));
$temp = [];
foreach ($childlist as $k => $v) {
$temp[$v['id']] = $v['name'];
}
$result[__($n['name'])] = $temp;
}
$groupdata = $result;
}
$this->view->assign('groupdata', $groupdata);
$this->assignconfig("manystore", ['id' => STORE_ID]);
}
/**
* 查看
*/
public function index()
{
if ($this->request->isAjax()) {
//如果发送的来源是Selectpage则转发到Selectpage
if ($this->request->request('keyField')) {
return $this->selectpage();
}
$this->storePidAutoCondition = false;
$childrenGroupIds = $this->childrenGroupIds;
$groupName = ManystoreAuthGroup::where('id', 'in', $childrenGroupIds)->where(array('shop_id'=>SHOP_ID))
->column('id,name');
$authGroupList = ManystoreAuthGroupAccess::where('group_id', 'in', $childrenGroupIds)
->field('uid,group_id')
->select();
$adminGroupName = [];
foreach ($authGroupList as $k => $v) {
if (isset($groupName[$v['group_id']])) {
$adminGroupName[$v['uid']][$v['group_id']] = $groupName[$v['group_id']];
}
}
$groups = $this->auth->getGroups();
foreach ($groups as $m => $n) {
$adminGroupName[$this->auth->id][$n['id']] = $n['name'];
}
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
$total = $this->model
->where($where)
->where('id', 'in', $this->childrenAdminIds)
->order($sort, $order)
->count();
$list = $this->model
->where($where)
->where('id', 'in', $this->childrenAdminIds)
->field(['password', 'salt', 'token'], true)
->order($sort, $order)
->limit($offset, $limit)
->select();
foreach ($list as $k => &$v) {
$groups = isset($adminGroupName[$v['id']]) ? $adminGroupName[$v['id']] : [];
$v['groups'] = implode(',', array_keys($groups));
$v['groups_text'] = implode(',', array_values($groups));
}
unset($v);
$result = array("total" => $total, "rows" => $list);
return json($result);
}
return $this->view->fetch();
}
/**
* 添加
*/
public function add()
{
if ($this->request->isPost()) {
$this->token();
$params = $this->request->post("row/a");
if ($params) {
if (!Validate::is($params['password'], '\S{6,16}')) {
$this->error(__("Please input correct password"));
}
$params['shop_id'] = SHOP_ID;
$params['salt'] = Random::alnum();
$params['password'] = md5(md5($params['password']) . $params['salt']);
$params['avatar'] = '/assets/img/avatar.png'; //设置新管理员默认头像。
$result = $this->model->validate('Manystore.add')->save($params);
if ($result === false) {
$this->error($this->model->getError());
}
$group = $this->request->post("group/a");
//过滤不允许的组别,避免越权
$group = array_intersect($this->childrenGroupIds, $group);
$dataset = [];
foreach ($group as $value) {
$dataset[] = ['uid' => $this->model->id, 'group_id' => $value];
}
model('ManystoreAuthGroupAccess')->saveAll($dataset);
$this->success();
}
$this->error();
}
return $this->view->fetch();
}
/**
* 编辑
*/
public function edit($ids = null)
{
$row = $this->model->get(['id' => $ids,'shop_id'=>SHOP_ID]);
if (!$row) {
$this->error(__('No Results were found'));
}
if (!in_array($row->id, $this->childrenAdminIds)) {
$this->error(__('You have no permission'));
}
if ($this->request->isPost()) {
$this->token();
$params = $this->request->post("row/a");
if ($params) {
if ($params['password']) {
if (!Validate::is($params['password'], '\S{6,16}')) {
$this->error(__("Please input correct password"));
}
$params['salt'] = Random::alnum();
$params['password'] = md5(md5($params['password']) . $params['salt']);
} else {
unset($params['password'], $params['salt']);
}
//这里需要针对username和email做唯一验证
$manystoreValidate = \think\Loader::validate('Manystore');
$manystoreValidate->rule([
'username' => 'require|regex:\w{3,12}|unique:manystore,username,' . $row->id,
'email' => 'require|email|unique:manystore,email,' . $row->id,
'password' => 'regex:\S{32}',
]);
$result = $row->validate('Manystore.edit')->save($params);
if ($result === false) {
$this->error($row->getError());
}
// 先移除所有权限
model('ManystoreAuthGroupAccess')->where('uid', $row->id)->delete();
$group = $this->request->post("group/a");
// 过滤不允许的组别,避免越权
$group = array_intersect($this->childrenGroupIds, $group);
$dataset = [];
foreach ($group as $value) {
$dataset[] = ['uid' => $row->id, 'group_id' => $value];
}
model('ManystoreAuthGroupAccess')->saveAll($dataset);
$this->success();
}
$this->error();
}
$grouplist = $this->auth->getGroups($row['id']);
$groupids = [];
foreach ($grouplist as $k => $v) {
$groupids[] = $v['id'];
}
$this->view->assign("row", $row);
$this->view->assign("groupids", $groupids);
return $this->view->fetch();
}
/**
* 删除
*/
public function del($ids = "")
{
if ($ids) {
$ids = array_intersect($this->childrenAdminIds, array_filter(explode(',', $ids)));
// 避免越权删除管理员
$adminList = $this->model->where('id', 'in', $ids)->where(array('shop_id'=>SHOP_ID))->select();
if ($adminList) {
$deleteIds = [];
foreach ($adminList as $k => $v) {
$deleteIds[] = $v->id;
}
$deleteIds = array_values(array_diff($deleteIds, [$this->auth->id]));
if ($deleteIds) {
$this->model->destroy($deleteIds);
model('ManystoreAuthGroupAccess')->where('uid', 'in', $deleteIds)->delete();
$this->success();
}
}
}
$this->error(__('You have no permission'));
}
/**
* 批量更新
* @internal
*/
public function multi($ids = "")
{
// 管理员禁止批量操作
$this->error();
}
/**
* 下拉搜索
*/
public function selectpage()
{
$this->dataLimit = 'auth';
$this->dataLimitField = 'id';
return parent::selectpage();
}
}

View File

@ -0,0 +1,134 @@
<?php
namespace app\manystore\controller\auth;
use app\manystore\model\ManystoreAuthGroup;
use app\common\controller\ManystoreBase;
/**
* 管理员日志
*
* @icon fa fa-users
* @remark 管理员可以查看自己所拥有的权限的管理员日志
*/
class Manystorelog extends ManystoreBase
{
/**
* @var \app\manystore\model\ManystoreLog
*/
protected $model = null;
protected $childrenGroupIds = [];
protected $childrenAdminIds = [];
public function _initialize()
{
parent::_initialize();
$this->model = model('ManystoreLog');
$this->childrenAdminIds = $this->auth->getChildrenAdminIds(true);
$this->childrenGroupIds = $this->auth->getChildrenGroupIds($this->auth->isSuperAdmin() ? true : false);
$groupName = ManystoreAuthGroup::where('id', 'in', $this->childrenGroupIds)
->column('id,name');
$this->view->assign('groupdata', $groupName);
}
/**
* 查看
*/
public function index()
{
if ($this->request->isAjax())
{
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();
$result = array("total" => $total, "rows" => $list);
return json($result);
}
return $this->view->fetch();
}
/**
* 详情
*/
public function detail($ids)
{
$row = $this->model->field('store_id,shop_id',true)->where(['id' => $ids,'shop_id'=>SHOP_ID])->find();
if (!$row)
$this->error(__('No Results were found'));
$this->view->assign("row", $row->toArray());
return $this->view->fetch();
}
/**
* 添加
* @internal
*/
public function add()
{
$this->error();
}
/**
* 编辑
* @internal
*/
public function edit($ids = NULL)
{
$this->error();
}
/**
* 删除
*/
public function del($ids = "")
{
if ($ids)
{
$adminList = $this->model->where('id', 'in', $ids)->where(array('shop_id'=>SHOP_ID))->select();
if ($adminList)
{
$deleteIds = [];
foreach ($adminList as $k => $v)
{
$deleteIds[] = $v->id;
}
if ($deleteIds)
{
$this->model->destroy($deleteIds);
$this->success();
}
}
}
$this->error();
}
/**
* 批量更新
* @internal
*/
public function multi($ids = "")
{
// 管理员禁止批量操作
$this->error();
}
public function selectpage()
{
return parent::selectpage();
}
}

View File

@ -0,0 +1,127 @@
<?php
namespace app\manystore\controller\general;
use app\common\controller\ManystoreBase;
/**
* 附件管理
*
* @icon fa fa-circle-o
* @remark 主要用于管理上传到又拍云的数据或上传至本服务的上传数据
*/
class Attachment extends ManystoreBase
{
/**
* @var \app\common\model\ManystoreAttachment
*/
protected $model = null;
public function _initialize()
{
parent::_initialize();
$this->model = model('ManystoreAttachment');
$this->view->assign("mimetypeList", \app\common\model\ManystoreAttachment::getMimetypeList());
}
/**
* 查看
*/
public function index()
{
//设置过滤方法
$this->request->filter(['strip_tags', 'trim']);
if ($this->request->isAjax()) {
$mimetypeQuery = [];
$filter = $this->request->request('filter');
$filterArr = (array)json_decode($filter, true);
if (isset($filterArr['mimetype']) && preg_match("/[]\,|\*]/", $filterArr['mimetype'])) {
$this->request->get(['filter' => json_encode(array_diff_key($filterArr, ['mimetype' => '']))]);
$mimetypeQuery = function ($query) use ($filterArr) {
$mimetypeArr = explode(',', $filterArr['mimetype']);
foreach ($mimetypeArr as $index => $item) {
if (stripos($item, "/*") !== false) {
$query->whereOr('mimetype', 'like', str_replace("/*", "/", $item) . '%');
} else {
$query->whereOr('mimetype', 'like', '%' . $item . '%');
}
}
};
}
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
$list = $this->model
->where($mimetypeQuery)
->where($where)
->order($sort, $order)
->paginate($limit);
$cdnurl = preg_replace("/\/(\w+)\.php$/i", '', $this->request->root());
foreach ($list as $k => &$v) {
$v['fullurl'] = ($v['storage'] == 'local' ? $cdnurl : $this->view->config['upload']['cdnurl']) . $v['url'];
}
unset($v);
$result = array("total" => $list->total(), "rows" => $list->items());
return json($result);
}
return $this->view->fetch();
}
/**
* 选择附件
*/
public function select()
{
if ($this->request->isAjax()) {
return $this->index();
}
$mimetype = $this->request->get('mimetype', '');
$mimetype = substr($mimetype, -1) === '/' ? $mimetype . '*' : $mimetype;
$this->view->assign('mimetype', $mimetype);
return $this->view->fetch();
}
/**
* 添加
*/
public function add()
{
if ($this->request->isAjax()) {
$this->error();
}
return $this->view->fetch();
}
/**
* 删除附件
* @param array $ids
*/
public function del($ids = "")
{
if (!$this->request->isPost()) {
$this->error(__("Invalid parameters"));
}
$ids = $ids ? $ids : $this->request->post("ids");
if ($ids) {
\think\Hook::add('upload_delete', function ($params) {
if ($params['storage'] == 'local') {
$attachmentFile = ROOT_PATH . '/public' . $params['url'];
if (is_file($attachmentFile)) {
@unlink($attachmentFile);
}
}
});
$attachmentlist = $this->model->where('id', 'in', $ids)->select();
foreach ($attachmentlist as $attachment) {
\think\Hook::listen("upload_delete", $attachment);
$attachment->delete();
}
$this->success();
}
$this->error(__('Parameter %s can not be empty', 'ids'));
}
}

View File

@ -0,0 +1,162 @@
<?php
namespace app\manystore\controller\general;
use app\common\controller\ManystoreBase;
use app\common\library\Email;
use app\common\model\ManystoreConfigGroup;
use app\common\model\ManystoreConfig as ManystoreConfigModel;
use think\Cache;
use think\Exception;
use think\Validate;
/**
* 系统配置
*
* @icon fa fa-cogs
* @remark 可以在此增改系统的变量和分组,也可以自定义分组和变量,如果需要删除请从数据库中删除
*/
class Config extends ManystoreBase
{
/**
* @var \app\common\model\Config
*/
protected $model = null;
protected $config_value_model = null;
public function _initialize()
{
parent::_initialize();
$this->model = model('ManystoreConfig');
$this->config_value_model = model('ManystoreValue');
}
/**
* 查看
*/
public function index()
{
$siteList = [];
$manystoreConfigGroup = new ManystoreConfigGroup();
$groupList = $manystoreConfigGroup->getGroupData();
foreach ($groupList as $k => $v) {
$siteList[$k]['name'] = $k;
$siteList[$k]['title'] = $v;
$siteList[$k]['list'] = [];
}
$config_value_data_array = [];
$config_value_data = collection($this->config_value_model->where(array('shop_id' => SHOP_ID))->select())->toArray();
foreach ($config_value_data as $value) {
$config_value_data_array[$value['config_id']] = $value;
}
foreach ($this->model->all() as $k => $v) {
if (!isset($siteList[$v['group']])) {
continue;
}
$value = $v->toArray();
$value['title'] = __($value['title']);
if (in_array($value['type'], ['select', 'selects', 'checkbox', 'radio'])) {
$value['value'] = explode(',', isset($config_value_data_array[$value['id']]['value']) ? $config_value_data_array[$value['id']]['value'] : $value['default']);
} else {
$value['value'] = isset($config_value_data_array[$value['id']]['value']) ? $config_value_data_array[$value['id']]['value'] : $value['default'];
}
$value['content'] = json_decode($value['content'], TRUE);
$siteList[$v['group']]['list'][] = $value;
}
$index = 0;
foreach ($siteList as $k => &$v) {
$v['active'] = !$index ? true : false;
$index++;
}
$this->view->assign('siteList', $siteList);
return $this->view->fetch();
}
/**
* 编辑
* @param null $ids
*/
public function edit($ids = NULL)
{
if ($this->request->isPost()) {
$row = $this->request->post("row/a");
if ($row) {
$configValueAll = [];
$config_value_data_array = [];
$config_value_data = collection($this->config_value_model->where(array('shop_id' => SHOP_ID))->select())->toArray();
foreach ($config_value_data as $value) {
$config_value_data_array[$value['config_id']] = $value;
}
foreach ($this->model->all() as $v) {
if (isset($row[$v['name']])) {
$value = $row[$v['name']];
if (is_array($value) && isset($value['field'])) {
$value = json_encode(ManystoreConfigModel::getArrayData($value), JSON_UNESCAPED_UNICODE);
} else {
$value = is_array($value) ? implode(',', $value) : $value;
}
$v['value'] = $value;
$config = $v->toArray();
$config_value = array();
if (!empty($config_value_data_array[$v['id']])) {
$config_value['id'] = $config_value_data_array[$v['id']]['id'];
}
$config_value['shop_id'] = SHOP_ID;
$config_value['store_id'] = STORE_ID;
$config_value['config_id'] = $config['id'];
$config_value['value'] = $value;
$configValueAll[] = $config_value;
}
}
$this->config_value_model->allowField(true)->saveAll($configValueAll);
try {
$this->refreshFile();
$this->success();
} catch (Exception $e) {
$this->error($e->getMessage());
}
}
$this->error(__('Parameter %s can not be empty', ''));
}
}
/**
* 刷新配置文件
*/
protected function refreshFile()
{
Cache::rm('ManystoreConfig:' . SHOP_ID);
}
public function selectpage()
{
$id = $this->request->get("id/d");
$config = \app\common\model\ManystoreConfig::get($id);
if (!$config) {
$this->error(__('Invalid parameters'));
}
$setting = $config['setting'];
//自定义条件
$custom = isset($setting['conditions']) ? (array)json_decode($setting['conditions'], true) : [];
$custom = array_filter($custom);
$this->request->request(['showField' => $setting['field'], 'keyField' => $setting['primarykey'], 'custom' => $custom, 'searchField' => [$setting['field'], $setting['primarykey']]]);
$this->model = \think\Db::connect()->setTable($setting['table']);
return parent::selectpage();
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace app\manystore\controller\general;
use app\common\controller\ManystoreBase;
/**
* 商家日志
*
* @icon fa fa-user
*/
class Log extends ManystoreBase
{
/**
* 查看
*/
public function index()
{
//设置过滤方法
$this->request->filter(['strip_tags']);
if ($this->request->isAjax()) {
$model = model('ManystoreLog');
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
$total = $model
->where($where)
->where(array('shop_id'=> SHOP_ID))
->order($sort, $order)
->count();
$list = $model
->where($where)
->where(array('shop_id'=> SHOP_ID))
->order($sort, $order)
->limit($offset, $limit)
->select();
$result = array("total" => $total, "rows" => $list);
return json($result);
}
return $this->view->fetch();
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace app\manystore\controller\general;
use app\manystore\model\Manystore;
use app\manystore\model\ManystoreShop;
use app\common\controller\ManystoreBase;
use fast\Random;
use think\Exception;
use think\Session;
use think\Validate;
/**
* 商家信息配置
*
* @icon fa fa-user
*/
class Profile extends ManystoreBase
{
/**
* 查看
*/
public function index()
{
$shopModel = new ManystoreShop();
$shop_info = $shopModel->where(array('id'=>SHOP_ID))->find();
$this->view->assign('statusList',[0=>'待审核',1=>'审核通过',2=>'审核拒绝']);
$this->view->assign('shop_info',$shop_info);
return $this->view->fetch();
}
/**
* 更新个人信息
*/
public function update()
{
if ($this->request->isPost()) {
$this->token();
$params = $this->request->post("row/a");
$params = array_filter(array_intersect_key(
$params,
array_flip(array('email', 'nickname', 'password', 'avatar'))
));
unset($v);
if (!Validate::is($params['email'], "email")) {
$this->error(__("Please input correct email"));
}
if (!Validate::is($params['nickname'], "/^[\x{4e00}-\x{9fa5}a-zA-Z0-9_-]+$/u")) {
$this->error(__("Please input correct nickname"));
}
if (isset($params['password'])) {
if (!Validate::is($params['password'], "/^[\S]{6,16}$/")) {
$this->error(__("Please input correct password"));
}
$params['salt'] = Random::alnum();
$params['password'] = md5(md5($params['password']) . $params['salt']);
}
$exist = Manystore::where('email', $params['email'])->where('id', '<>', $this->auth->id)->find();
if ($exist) {
$this->error(__("Email already exists"));
}
if ($params) {
$manystore = Manystore::get($this->auth->id);
$manystore->save($params);
Session::set("manystore", $manystore->toArray());
$this->success();
}
$this->error();
}
return;
}
public function shop_update(){
if ($this->request->isPost()) {
$this->token();
$shop = $this->request->post("shop/a");
$shopModel = new ManystoreShop();
$shopModel->save($shop,array('id'=>SHOP_ID));
$this->success();
}
$this->error();
}
}

View File

@ -0,0 +1,183 @@
<?php
return [
'User id' => '会员ID',
'Username' => '用户名',
'Nickname' => '昵称',
'Password' => '密码',
'Sign up' => '注 册',
'Sign in' => '登 录',
'Sign out' => '注 销',
'Keep login' => '保持会话',
'Guest' => '游客',
'Welcome' => '%s你好',
'View' => '查看',
'Add' => '添加',
'Edit' => '编辑',
'Del' => '删除',
'Delete' => '删除',
'Import' => '导入',
'Export' => '导出',
'All' => '全部',
'Detail' => '详情',
'Multi' => '批量更新',
'Setting' => '配置',
'Move' => '移动',
'Name' => '名称',
'Status' => '状态',
'Weigh' => '权重',
'Operate' => '操作',
'Warning' => '温馨提示',
'Default' => '默认',
'Article' => '文章',
'Page' => '单页',
'OK' => '确定',
'Apply' => '应用',
'Cancel' => '取消',
'Clear' => '清空',
'Custom Range' => '自定义',
'Today' => '今天',
'Yesterday' => '昨天',
'Last 7 days' => '最近7天',
'Last 30 days' => '最近30天',
'Last month' => '上月',
'This month' => '本月',
'Loading' => '加载中',
'Money' => '余额',
'Score' => '积分',
'More' => '更多',
'Yes' => '是',
'No' => '否',
'Normal' => '正常',
'Hidden' => '隐藏',
'Locked' => '锁定',
'Submit' => '提交',
'Reset' => '重置',
'Execute' => '执行',
'Close' => '关闭',
'Choose' => '选择',
'Search' => '搜索',
'Refresh' => '刷新',
'Install' => '安装',
'Uninstall' => '卸载',
'First' => '首页',
'Previous' => '上一页',
'Next' => '下一页',
'Last' => '末页',
'None' => '无',
'Home' => '主页',
'Online' => '在线',
'Login' => '登录',
'Logout' => '注销',
'Profile' => '个人资料',
'Index' => '首页',
'Hot' => '热门',
'Recommend' => '推荐',
'Upload' => '上传',
'Uploading' => '上传中',
'Code' => '编号',
'Message' => '内容',
'Line' => '行号',
'File' => '文件',
'Menu' => '菜单',
'Type' => '类型',
'Title' => '标题',
'Content' => '内容',
'Append' => '追加',
'Select' => '选择',
'Memo' => '备注',
'Parent' => '父级',
'Params' => '参数',
'Permission' => '权限',
'Check all' => '选中全部',
'Expand all' => '展开全部',
'Begin time' => '开始时间',
'End time' => '结束时间',
'Create time' => '创建时间',
'Update time' => '更新时间',
'Flag' => '标志',
'Drag to sort' => '拖动进行排序',
'Redirect now' => '立即跳转',
'Key' => '键',
'Value' => '值',
'Common search' => '普通搜索',
'Search %s' => '搜索 %s',
'View %s' => '查看 %s',
'%d second%s ago' => '%d秒前',
'%d minute%s ago' => '%d分钟前',
'%d hour%s ago' => '%d小时前',
'%d day%s ago' => '%d天前',
'%d week%s ago' => '%d周前',
'%d month%s ago' => '%d月前',
'%d year%s ago' => '%d年前',
'Set to normal' => '设为正常',
'Set to hidden' => '设为隐藏',
'Recycle bin' => '回收站',
'Restore' => '还原',
'Restore all' => '还原全部',
'Destroy' => '销毁',
'Destroy all' => '清空回收站',
'Nothing need restore' => '没有需要还原的数据',
//提示
'Go back' => '返回首页',
'Jump now' => '立即跳转',
'Click to search %s' => '点击搜索 %s',
'Click to toggle' => '点击切换',
'Operation completed' => '操作成功!',
'Operation failed' => '操作失败!',
'Unknown data format' => '未知的数据格式!',
'Network error' => '网络错误!',
'Invalid parameters' => '未知参数',
'No results were found' => '记录未找到',
'No rows were inserted' => '未插入任何行',
'No rows were deleted' => '未删除任何行',
'No rows were updated' => '未更新任何行',
'Parameter %s can not be empty' => '参数%s不能为空',
'Are you sure you want to delete the %s selected item?' => '确定删除选中的 %s 项?',
'Are you sure you want to delete this item?' => '确定删除此项?',
'Are you sure you want to delete or turncate?' => '确定删除或清空?',
'Are you sure you want to truncate?' => '确定清空?',
'Token verification error' => 'Token验证错误',
'You have no permission' => '你没有权限访问',
'Please enter your username' => '请输入你的用户名',
'Please enter your password' => '请输入你的密码',
'Please login first' => '请登录后操作',
'You can upload up to %d file%s' => '你最多还可以上传%d个文件',
'You can choose up to %d file%s' => '你最多还可以选择%d个文件',
'An unexpected error occurred' => '发生了一个意外错误,程序猿正在紧急处理中',
'This page will be re-directed in %s seconds' => '页面将在 %s 秒后自动跳转',
//菜单
'Dashboard' => '控制台',
'General' => '常规管理',
'Category' => '分类管理',
'Addon' => '插件管理',
'Auth' => '权限管理',
'Config' => '系统配置',
'Attachment' => '附件管理',
'Admin' => '管理员管理',
'Admin log' => '管理员日志',
'Group' => '角色组',
'Rule' => '菜单规则',
'User' => '会员管理',
'User group' => '会员分组',
'User rule' => '会员规则',
'Select attachment' => '选择附件',
'Update profile' => '更新个人信息',
'Update shop' => '更新商家信息',
'Local install' => '本地安装',
'Update state' => '禁用启用',
'Admin group' => '超级管理组',
'Second group' => '二级管理组',
'Third group' => '三级管理组',
'Second group 2' => '二级管理组2',
'Third group 2' => '三级管理组2',
'Dashboard tips' => '用于展示当前系统中的统计数据、统计报表及重要实时数据',
'Config tips' => '可以在此增改系统的变量和分组,也可以自定义分组和变量,如果需要删除请从数据库中删除,获取方式config("manystore_config.$name")',
'Category tips' => '用于统一管理网站的所有分类,分类可进行无限级分类,分类类型请在常规管理->系统配置->字典配置中添加',
'Attachment tips' => '主要用于管理上传到服务器或第三方存储的数据',
'Addon tips' => '可在线安装、卸载、禁用、启用插件同时支持添加本地插件。FastAdmin已上线插件商店 ,你可以发布你的免费或付费插件:<a href="https://www.fastadmin.net/store.html" target="_blank">https://www.fastadmin.net/store.html</a>',
'Admin tips' => '一个管理员可以有多个角色组,左侧的菜单根据管理员所拥有的权限进行生成',
'Admin log tips' => '管理员可以查看自己所拥有的权限的管理员日志',
'Group tips' => '角色组可以有多个,角色有上下级层级关系,如果子角色有角色组和管理员的权限则可以派生属于自己组别的下级角色组或管理员',
'Rule tips' => '规则通常对应一个控制器的方法,同时左侧的菜单栏数据也从规则中体现,通常建议通过命令行进行生成规则节点',
];

View File

@ -0,0 +1,93 @@
<?php
return [
'Id' => 'ID',
'Title' => '插件名称',
'Value' => '配置值',
'Array key' => '键',
'Array value' => '值',
'File' => '文件',
'Donate' => '打赏作者',
'Warmtips' => '温馨提示',
'Pay now' => '立即支付',
'Offline install' => '离线安装',
'Refresh addon cache' => '刷新插件缓存',
'Userinfo' => '会员信息',
'Online store' => '在线商店',
'Local addon' => '本地插件',
'Conflict tips' => '此插件中发现和现有系统中部分文件发现冲突!以下文件将会被影响,请备份好相关文件后再继续操作',
'Login tips' => '此处登录账号为<a href="https://www.fastadmin.net" target="_blank">FastAdmin官网账号</a>',
'Logined tips' => '你好!%s<br />当前你已经登录,将同步保存你的购买记录',
'Pay tips' => '扫码支付后如果仍然无法立即下载,请不要重复支付,请加<a href="https://jq.qq.com/?_wv=1027&k=487PNBb" target="_blank">QQ群636393962</a>向管理员反馈',
'Pay click tips' => '请点击这里在新窗口中进行支付!',
'Pay new window tips' => '请在新弹出的窗口中进行支付,支付完成后再重新点击安装按钮进行安装!',
'Uninstall tips' => '确认卸载<b>[%s]</b><p class="text-danger">卸载将会删除所有插件文件且不可找回!!! 插件如果有创建数据库表请手动删除!!!</p>如有重要数据请备份后再操作!',
'Upgrade tips' => '确认升级<b>[%s]</b><p class="text-danger">如果之前购买插件时未登录,此次升级可能出现购买后才可以下载的提示!!!<br>升级后可能出现部分冗余数据记录,请根据需要移除即可!!!</p>如有重要数据请备份后再操作!',
'Offline installed tips' => '插件安装成功!清除浏览器缓存和框架缓存后生效!',
'Online installed tips' => '插件安装成功!清除浏览器缓存和框架缓存后生效!',
'Not login tips' => '你当前未登录FastAdmin登录后将同步已购买的记录下载时无需二次付费',
'Not installed tips' => '请安装后再访问插件前台页面!',
'Not enabled tips' => '插件已经禁用,请启用后再访问插件前台页面!',
'New version tips' => '发现新版本:%s 点击查看更新日志',
'Store now available tips' => 'FastAdmin插件市场暂不可用是否切换到本地插件',
'Switch to the local' => '切换到本地插件',
'try to reload' => '重新尝试加载',
'Please disable addon first' => '请先禁用插件再进行升级',
'Login now' => '立即登录',
'Continue install' => '不登录,继续安装',
'View addon home page' => '查看插件介绍和帮助',
'View addon index page' => '查看插件前台首页',
'View addon screenshots' => '点击查看插件截图',
'Click to toggle status' => '点击切换插件状态',
'Click to contact developer' => '点击与插件开发者取得联系',
'My addons' => '我购买的插件',
'My posts' => '我发布的插件',
'Index' => '前台',
'All' => '全部',
'Uncategoried' => '未归类',
'Recommend' => '推荐',
'Hot' => '热门',
'New' => '新',
'Paying' => '付费',
'Free' => '免费',
'Sale' => '折扣',
'No image' => '暂无缩略图',
'Price' => '价格',
'Downloads' => '下载',
'Author' => '作者',
'Identify' => '标识',
'Homepage' => '主页',
'Intro' => '介绍',
'Version' => '版本',
'New version' => '新版本',
'Createtime' => '添加时间',
'Releasetime' => '更新时间',
'Detail' => '插件详情',
'Document' => '文档',
'Demo' => '演示',
'Feedback' => '反馈BUG',
'Install' => '安装',
'Uninstall' => '卸载',
'Upgrade' => '升级',
'Setting' => '配置',
'Disable' => '禁用',
'Enable' => '启用',
'Your username or email' => '你的手机号、用户名或邮箱',
'Your password' => '你的密码',
'Login FastAdmin' => '登录FastAdmin',
'Login' => '登录',
'Logout' => '退出登录',
'Register' => '注册账号',
'You\'re not login' => '当前未登录',
'Continue uninstall' => '继续卸载',
'Continue operate' => '继续操作',
'Install successful' => '安装成功',
'Uninstall successful' => '卸载成功',
'Operate successful' => '操作成功',
'Addon name incorrect' => '插件名称不正确',
'Addon info file was not found' => '插件配置文件未找到',
'Addon info file data incorrect' => '插件配置信息不正确',
'Addon already exists' => '上传的插件已经存在',
'Unable to open the zip file' => '无法打开ZIP文件',
'Unable to extract the file' => '无法解压ZIP文件',
];

View File

@ -0,0 +1,8 @@
<?php
return [
'No file upload or server upload limit exceeded' => '未上传文件或超出服务器上传限制',
'Uploaded file format is limited' => '上传文件格式受限制',
'Uploaded file is not a valid image' => '上传文件不是有效的图片文件',
'Upload successful' => '上传成功',
];

View File

@ -0,0 +1,12 @@
<?php
return [
'The parent group can not be its own child' => '父组别不能是自身的子组别',
'The parent group can not found' => '父组别未找到',
'Group not found' => '组别未找到',
'Can not change the parent to child' => '父组别不能是它的子组别',
'Can not change the parent to self' => '父组别不能是它自己',
'You can not delete group that contain child group and administrators' => '你不能删除含有子组和管理员的组',
'The parent group exceeds permission limit' => '父组别超出权限范围',
'The parent group can not be its own child or itself' => '父组别不能是它的子组别及本身',
];

View File

@ -0,0 +1,9 @@
<?php
return [
'Group' => '所属组别',
'Loginfailure' => '登录失败次数',
'Login time' => '最后登录',
'Please input correct username' => '用户名只能由3-12位数字、字母、下划线组合',
'Please input correct password' => '密码长度必须在6-16位之间不能包含空格',
];

View File

@ -0,0 +1,14 @@
<?php
return [
'Id' => '编号',
'Shop_id' => '商家编号',
'Store_id' => '管理员编号',
'Username' => '管理员名字',
'Url' => '操作页面',
'Title' => '日志标题',
'Content' => '内容',
'Ip' => 'IP地址',
'Useragent' => '浏览器',
'Createtime' => '操作时间',
];

View File

@ -0,0 +1,20 @@
<?php
return [
'Toggle all' => '显示全部',
'Condition' => '规则条件',
'Remark' => '备注',
'Icon' => '图标',
'Alert' => '警告',
'Name' => '规则',
'Controller/Action' => '控制器名/方法名',
'Ismenu' => '菜单',
'Search icon' => '搜索图标',
'Toggle menu visible' => '点击切换菜单显示',
'Toggle sub menu' => '点击切换子菜单',
'Menu tips' => '父级菜单无需匹配控制器和方法,子级菜单请使用控制器名',
'Node tips' => '控制器/方法名,如果有目录请使用 目录名/控制器名/方法名',
'The non-menu rule must have parent' => '非菜单规则节点必须有父级',
'Can not change the parent to child' => '父组别不能是它的子组别',
'Name only supports letters, numbers, underscore and slash' => 'URL规则只能是小写字母、数字、下划线和/组成',
];

View File

@ -0,0 +1,18 @@
<?php
return [
'Id' => 'ID',
'Pid' => '父ID',
'Type' => '类型',
'All' => '全部',
'Image' => '图片',
'Keywords' => '关键字',
'Description' => '描述',
'Diyname' => '自定义名称',
'Createtime' => '创建时间',
'Updatetime' => '更新时间',
'Weigh' => '权重',
'Category warmtips' => '温馨提示:栏目类型请前往<b>常规管理</b>-><b>系统配置</b>-><b>字典配置</b>中进行管理',
'Can not change the parent to child' => '父组别不能是它的子组别',
'Status' => '状态'
];

Some files were not shown because too many files have changed in this diff Show More