1
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
namespace plugin\admin\api;
|
||||
|
||||
use plugin\admin\app\model\AdminRole;
|
||||
use plugin\admin\app\model\AdminRule;
|
||||
use support\exception\BusinessException;
|
||||
use function admin;
|
||||
|
||||
/**
|
||||
* 对外提供的鉴权接口
|
||||
*/
|
||||
class Auth
|
||||
{
|
||||
/**
|
||||
* 判断权限
|
||||
* 如果没有权限则抛出异常
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @return void
|
||||
* @throws \ReflectionException|BusinessException
|
||||
*/
|
||||
public static function access(string $controller, string $action)
|
||||
{
|
||||
$code = 0;
|
||||
$msg = '';
|
||||
if (!static::canAccess($controller, $action, $code, $msg)) {
|
||||
throw new BusinessException($msg, $code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否有权限
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @param int $code
|
||||
* @param string $msg
|
||||
* @return bool
|
||||
* @throws \ReflectionException|BusinessException
|
||||
*/
|
||||
public static function canAccess(string $controller, string $action, int &$code = 0, string &$msg = ''): bool
|
||||
{
|
||||
// 无控制器信息说明是函数调用,函数不属于任何控制器,鉴权操作应该在函数内部完成。
|
||||
if (!$controller) {
|
||||
return true;
|
||||
}
|
||||
// 获取控制器鉴权信息
|
||||
$class = new \ReflectionClass($controller);
|
||||
$properties = $class->getDefaultProperties();
|
||||
$noNeedLogin = $properties['noNeedLogin'] ?? [];
|
||||
$noNeedAuth = $properties['noNeedAuth'] ?? [];
|
||||
|
||||
// 不需要登录
|
||||
if (in_array($action, $noNeedLogin)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取登录信息
|
||||
$admin = admin();
|
||||
if (!$admin) {
|
||||
$msg = '请登录';
|
||||
// 401是未登录固定的返回码
|
||||
$code = 401;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 不需要鉴权
|
||||
if (in_array($action, $noNeedAuth)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 当前管理员无角色
|
||||
$roles = $admin['roles'];
|
||||
if (!$roles) {
|
||||
$msg = '无权限';
|
||||
$code = 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 角色没有规则
|
||||
$rules = AdminRole::whereIn('id', $roles)->column('rules');
|
||||
$rule_ids = [];
|
||||
foreach ($rules as $rule_string) {
|
||||
if (!$rule_string) {
|
||||
continue;
|
||||
}
|
||||
$rule_ids = array_merge($rule_ids, explode(',', $rule_string));
|
||||
}
|
||||
if (!$rule_ids) {
|
||||
$msg = '无权限';
|
||||
$code = 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 超级管理员
|
||||
if (in_array('*', $rule_ids)){
|
||||
return true;
|
||||
}
|
||||
|
||||
//cp($rule_ids);
|
||||
// 如果action为index,规则里有任意一个以$controller开头的权限即可
|
||||
if (strtolower($action) === 'index') {
|
||||
$rule = AdminRule::where(function ($query) use ($controller, $action) {
|
||||
$controller_like = str_replace('plugin\\admin\\', '', $controller);
|
||||
$controller_like = str_replace('\\', '\\\\', $controller_like);
|
||||
$query->where('key', 'like', "$controller_like@%")->whereOr('key', $controller);
|
||||
})->whereIn('id', $rule_ids)->find();
|
||||
if ($rule) {
|
||||
return true;
|
||||
}
|
||||
$msg = '无权限';
|
||||
$code = 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 查询是否有当前控制器的规则
|
||||
$rule = AdminRule::where(function ($query) use ($controller, $action) {
|
||||
$controller_like = str_replace('plugin\\admin\\', '', $controller);
|
||||
$query->where('key', "$controller_like@$action");
|
||||
})->whereIn('id', $rule_ids)->find();
|
||||
if (!$rule) {
|
||||
$msg = '无权限';
|
||||
$code = 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace plugin\admin\api;
|
||||
|
||||
use Illuminate\Database\Events\QueryExecuted;
|
||||
use Workerman\Protocols\Http;
|
||||
use Workerman\Protocols\Http\Session as SessionBase;
|
||||
use Workerman\Worker;
|
||||
use function config;
|
||||
use function property_exists;
|
||||
use support\think\Db;
|
||||
|
||||
/**
|
||||
* Class Session
|
||||
* @package support
|
||||
*/
|
||||
class Bootstrap implements \Webman\Bootstrap
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Worker|null $worker
|
||||
* @return void
|
||||
*/
|
||||
public static function start(?Worker $worker)
|
||||
{
|
||||
|
||||
Db::connection()->listen(function (QueryExecuted $queryExecuted) {
|
||||
var_dump("[{$queryExecuted->time} ms] {$queryExecuted->sql}");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
namespace plugin\admin\api;
|
||||
|
||||
use plugin\admin\app\model\AdminRole;
|
||||
use plugin\admin\app\model\AdminRule;
|
||||
use support\exception\BusinessException;
|
||||
use function admin;
|
||||
|
||||
/**
|
||||
* 对外提供的菜单接口
|
||||
*/
|
||||
class Menu
|
||||
{
|
||||
|
||||
/**
|
||||
* 根据key获取菜单
|
||||
* @param $key
|
||||
* @return array
|
||||
*/
|
||||
public static function get($key)
|
||||
{
|
||||
$menu = AdminRule::where('key', $key)->find();
|
||||
return $menu ? $menu->toArray() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id获得菜单
|
||||
* @param $id
|
||||
* @return array
|
||||
*/
|
||||
public static function find($id): array
|
||||
{
|
||||
return AdminRule::find($id)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加菜单
|
||||
* @param array $menu
|
||||
* @return int
|
||||
*/
|
||||
public static function add(array $menu)
|
||||
{
|
||||
$item = new AdminRule;
|
||||
foreach ($menu as $key => $value) {
|
||||
$item->$key = $value;
|
||||
}
|
||||
$item->save();
|
||||
return $item->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入菜单
|
||||
* @param array $menu_tree
|
||||
* @return void
|
||||
*/
|
||||
public static function import(array $menu_tree)
|
||||
{
|
||||
if (is_numeric(key($menu_tree)) && !isset($menu_tree['key'])) {
|
||||
foreach ($menu_tree as $item) {
|
||||
static::import($item);
|
||||
}
|
||||
return;
|
||||
}
|
||||
$children = $menu_tree['children'] ?? [];
|
||||
unset($menu_tree['children']);
|
||||
if ($old_menu = Menu::get($menu_tree['key'])) {
|
||||
$pid = $old_menu['id'];
|
||||
AdminRule::where('key', $menu_tree['key'])->update($menu_tree);
|
||||
} else {
|
||||
$pid = static::add($menu_tree);
|
||||
}
|
||||
foreach ($children as $menu) {
|
||||
$menu['pid'] = $pid;
|
||||
static::import($menu);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除菜单
|
||||
* @param $key
|
||||
* @return void
|
||||
*/
|
||||
public static function delete($key)
|
||||
{
|
||||
$item = AdminRule::where('key', $key)->find();
|
||||
if (!$item) {
|
||||
return;
|
||||
}
|
||||
// 子规则一起删除
|
||||
$delete_ids = $children_ids = [$item['id']];
|
||||
while($children_ids) {
|
||||
$children_ids = AdminRule::whereIn('pid', $children_ids)->column('id');
|
||||
$delete_ids = array_merge($delete_ids, $children_ids);
|
||||
}
|
||||
AdminRule::whereIn('id', $delete_ids)->delete();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取菜单中某个(些)字段的值
|
||||
* @param $menu
|
||||
* @param null $column
|
||||
* @param null $index
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function column($menu, $column = null, $index = null)
|
||||
{
|
||||
$values = [];
|
||||
if (is_numeric(key($menu)) && !isset($menu['key'])) {
|
||||
foreach ($menu as $item) {
|
||||
$values = array_merge($values, static::column($item, $column, $index));
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
$children = $menu['children'] ?? [];
|
||||
unset($menu['children']);
|
||||
if ($column === null) {
|
||||
if ($index) {
|
||||
$values[$menu[$index]] = $menu;
|
||||
} else {
|
||||
$values[] = $menu;
|
||||
}
|
||||
} else {
|
||||
if (is_array($column)) {
|
||||
$item = [];
|
||||
foreach ($column as $f) {
|
||||
$item[$f] = $menu[$f] ?? null;
|
||||
}
|
||||
if ($index) {
|
||||
$values[$menu[$index]] = $item;
|
||||
} else {
|
||||
$values[] = $item;
|
||||
}
|
||||
} else {
|
||||
$value = $menu[$column] ?? null;
|
||||
if ($index) {
|
||||
$values[$menu[$index]] = $value;
|
||||
} else {
|
||||
$values[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($children as $child) {
|
||||
$values = array_merge($values, static::column($child, $column, $index));
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace plugin\admin\api;
|
||||
|
||||
use ReflectionException;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use Webman\MiddlewareInterface;
|
||||
use support\exception\BusinessException;
|
||||
|
||||
/**
|
||||
* 对外提供的鉴权中间件
|
||||
*/
|
||||
class Middleware implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* 鉴权
|
||||
* @param Request $request
|
||||
* @param callable $handler
|
||||
* @return Response
|
||||
* @throws ReflectionException
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function process(Request $request, callable $handler): Response
|
||||
{
|
||||
$controller = $request->controller;
|
||||
$action = $request->action;
|
||||
|
||||
$code = 0;
|
||||
$msg = '';
|
||||
if (!Auth::canAccess($controller, $action, $code, $msg)) {
|
||||
if ($request->expectsJson()) {
|
||||
$response = json(['code' => $code, 'msg' => $msg, 'type' => 'error']);
|
||||
} else {
|
||||
if ($code === 401) {
|
||||
$response = response(<<<EOF
|
||||
<script>
|
||||
if (self !== top) {
|
||||
parent.location.reload();
|
||||
}
|
||||
</script>
|
||||
EOF
|
||||
);
|
||||
} else {
|
||||
$request->app = '';
|
||||
$request->plugin = 'admin';
|
||||
$response = view('common/403')->withStatus(403);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$response = $request->method() == 'OPTIONS' ? response('') : $handler($request);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
namespace plugin\admin\app\common;
|
||||
|
||||
|
||||
use plugin\admin\app\model\Admin;
|
||||
use plugin\admin\app\model\AdminAccess;
|
||||
use plugin\admin\app\model\AdminRole;
|
||||
use plugin\admin\app\model\AdminRule;
|
||||
|
||||
class Auth
|
||||
{
|
||||
/**
|
||||
* 获取权限范围内的所有角色id
|
||||
* @param bool $with_self
|
||||
* @return array
|
||||
*/
|
||||
public static function getScopeRoleIds(bool $with_self = false): array
|
||||
{
|
||||
if (!$admin = admin()) {
|
||||
return [];
|
||||
}
|
||||
$role_ids = $admin['roles'];
|
||||
$rules = AdminRole::whereIn('id', $role_ids)->column('rules');//->toArray();
|
||||
if ($rules && in_array('*', $rules)) {
|
||||
return AdminRole::column('id');//->toArray();
|
||||
}
|
||||
|
||||
$roles = AdminRole::select();
|
||||
$tree = new Tree($roles);
|
||||
$descendants = $tree->getDescendant($role_ids, $with_self);
|
||||
return array_column($descendants, 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限范围内的所有管理员id
|
||||
* @param bool $with_self
|
||||
* @return array
|
||||
*/
|
||||
public static function getScopeAdminIds(bool $with_self = false): array
|
||||
{
|
||||
$role_ids = static::getScopeRoleIds();
|
||||
$admin_ids = AdminAccess::whereIn('role_id', $role_ids)->column('admin_id');//->toArray();
|
||||
if ($with_self) {
|
||||
$admin_ids[] = admin_id();
|
||||
}
|
||||
return array_unique($admin_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容旧版本
|
||||
* @param int $admin_id
|
||||
* @deprecated
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSupperAdmin(int $admin_id = 0): bool
|
||||
{
|
||||
return static::isSuperAdmin($admin_id);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是超级管理员
|
||||
* @param int $admin_id
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSuperAdmin(int $admin_id = null): bool
|
||||
{
|
||||
if(!$admin_id){
|
||||
$admin_id = admin()['id'];
|
||||
}
|
||||
if($admin_id <=1){
|
||||
return true;
|
||||
}
|
||||
if (!$admin_id) {
|
||||
if (!$roles = admin('roles')) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$roles = AdminAccess::where('admin_id', $admin_id)->column('role_id');
|
||||
}
|
||||
$rules = AdminRole::whereIn('id', $roles)->column('rules');
|
||||
return $rules && in_array('*', $rules);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
namespace plugin\admin\app\common;
|
||||
|
||||
|
||||
class Tree
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取完整的树结构,包含祖先节点
|
||||
*/
|
||||
const INCLUDE_ANCESTORS = 1;
|
||||
|
||||
/**
|
||||
* 获取部分树,不包含祖先节点
|
||||
*/
|
||||
const EXCLUDE_ANCESTORS = 0;
|
||||
|
||||
/**
|
||||
* 数据
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* 哈希树
|
||||
* @var array
|
||||
*/
|
||||
protected $hashTree = [];
|
||||
|
||||
/**
|
||||
* 父级字段名
|
||||
* @var string
|
||||
*/
|
||||
protected $pidName = 'pid';
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param string $pid_name
|
||||
*/
|
||||
public function __construct($data, string $pid_name = 'pid')
|
||||
{
|
||||
$this->pidName = $pid_name;
|
||||
if (is_object($data) && method_exists($data, 'toArray')) {
|
||||
$this->data = $data->toArray();
|
||||
} else {
|
||||
$this->data = (array)$data;
|
||||
$this->data = array_map(function ($item) {
|
||||
if (is_object($item) && method_exists($item, 'toArray')) {
|
||||
return $item->toArray();
|
||||
}
|
||||
return $item;
|
||||
}, $this->data);
|
||||
}
|
||||
$this->hashTree = $this->getHashTree();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子孙节点
|
||||
* @param array $include
|
||||
* @param bool $with_self
|
||||
* @return array
|
||||
*/
|
||||
public function getDescendant(array $include, bool $with_self = false): array
|
||||
{
|
||||
$items = [];
|
||||
foreach ($include as $id) {
|
||||
if (!isset($this->hashTree[$id])) {
|
||||
return [];
|
||||
}
|
||||
if ($with_self) {
|
||||
$item = $this->hashTree[$id];
|
||||
unset($item['children']);
|
||||
$items[$item['id']] = $item;
|
||||
}
|
||||
foreach ($this->hashTree[$id]['children'] ?? [] as $item) {
|
||||
unset($item['children']);
|
||||
$items[$item['id']] = $item;
|
||||
foreach ($this->getDescendant([$item['id']]) as $it) {
|
||||
$items[$it['id']] = $it;
|
||||
}
|
||||
}
|
||||
}
|
||||
return array_values($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取哈希树
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
protected function getHashTree(array $data = []): array
|
||||
{
|
||||
$data = $data ?: $this->data;
|
||||
$hash_tree = [];
|
||||
foreach ($data as $item) {
|
||||
$hash_tree[$item['id']] = $item;
|
||||
}
|
||||
foreach ($hash_tree as $index => $item) {
|
||||
if ($item[$this->pidName] && isset($hash_tree[$item[$this->pidName]])) {
|
||||
$hash_tree[$item[$this->pidName]]['children'][$hash_tree[$index]['id']] = &$hash_tree[$index];
|
||||
}
|
||||
}
|
||||
return $hash_tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取树
|
||||
* @param array $include
|
||||
* @param int $type
|
||||
* @return array|null
|
||||
*/
|
||||
public function getTree(array $include = [], int $type = 1): ?array
|
||||
{
|
||||
// $type === static::EXCLUDE_ANCESTORS
|
||||
if ($type === static::EXCLUDE_ANCESTORS) {
|
||||
$items = [];
|
||||
$include = array_unique($include);
|
||||
foreach ($include as $id) {
|
||||
if (!isset($this->hashTree[$id])) {
|
||||
return [];
|
||||
}
|
||||
$items[] = $this->hashTree[$id];
|
||||
}
|
||||
return static::arrayValues($items);
|
||||
}
|
||||
|
||||
// $type === static::INCLUDE_ANCESTORS
|
||||
$hash_tree = $this->hashTree;
|
||||
$items = [];
|
||||
if ($include) {
|
||||
$map = [];
|
||||
foreach ($include as $id) {
|
||||
if (!isset($hash_tree[$id])) {
|
||||
continue;
|
||||
}
|
||||
$item = $hash_tree[$id];
|
||||
$max_depth = 100;
|
||||
while ($max_depth-- > 0 && $item[$this->pidName] && isset($hash_tree[$item[$this->pidName]])) {
|
||||
$last_item = $item;
|
||||
$pid = $item[$this->pidName];
|
||||
$item = $hash_tree[$pid];
|
||||
$item_id = $item['id'];
|
||||
if (empty($map[$item_id])) {
|
||||
$map[$item_id] = 1;
|
||||
$hash_tree[$pid]['children'] = [];
|
||||
}
|
||||
$hash_tree[$pid]['children'][$last_item['id']] = $last_item;
|
||||
$item = $hash_tree[$pid];
|
||||
}
|
||||
$items[$item['id']] = $item;
|
||||
}
|
||||
} else {
|
||||
$items = $hash_tree;
|
||||
}
|
||||
$formatted_items = [];
|
||||
foreach ($items as $item) {
|
||||
if (!$item[$this->pidName] || !isset($hash_tree[$item[$this->pidName]])) {
|
||||
$formatted_items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return static::arrayValues($formatted_items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归重建数组下标
|
||||
* @param $array
|
||||
* @return array
|
||||
*/
|
||||
public static function arrayValues($array): array
|
||||
{
|
||||
if (!$array) {
|
||||
return [];
|
||||
}
|
||||
if (!isset($array['children'])) {
|
||||
$current = current($array);
|
||||
if (!is_array($current)) {
|
||||
return $array;
|
||||
}
|
||||
$tree = array_values($array);
|
||||
foreach ($tree as $index => $item) {
|
||||
$tree[$index] = static::arrayValues($item);
|
||||
}
|
||||
return $tree;
|
||||
}
|
||||
$array['children'] = array_values($array['children']);
|
||||
foreach ($array['children'] as $index => $child) {
|
||||
$array['children'][$index] = static::arrayValues($child);
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,567 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\common;
|
||||
|
||||
use process\Monitor;
|
||||
use Throwable;
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
use plugin\admin\app\model\Option;
|
||||
use support\exception\BusinessException;
|
||||
use support\Db;
|
||||
use Workerman\Timer;
|
||||
use Workerman\Worker;
|
||||
|
||||
class Util
|
||||
{
|
||||
/**
|
||||
* 密码哈希
|
||||
* @param $password
|
||||
* @param string $algo
|
||||
* @return false|string|null
|
||||
*/
|
||||
public static function passwordHash($password, string $algo = PASSWORD_DEFAULT)
|
||||
{
|
||||
return password_hash($password, $algo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证密码哈希
|
||||
* @param $password
|
||||
* @param $hash
|
||||
* @return bool
|
||||
*/
|
||||
public static function passwordVerify(string $password, string $hash): bool
|
||||
{
|
||||
return password_verify($password, $hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取webman-admin数据库连接
|
||||
* @return Connection
|
||||
*/
|
||||
public static function db(): Connection
|
||||
{
|
||||
return Db::connection('mysql');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SchemaBuilder
|
||||
* @return Builder
|
||||
*/
|
||||
public static function schema(): Builder
|
||||
{
|
||||
return Db::schema('mysql');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语义化时间
|
||||
* @param $time
|
||||
* @return false|string
|
||||
*/
|
||||
public static function humanDate($time)
|
||||
{
|
||||
$timestamp = is_numeric($time) ? $time : strtotime($time);
|
||||
$dur = time() - $timestamp;
|
||||
if ($dur < 0) {
|
||||
return date('Y-m-d', $timestamp);
|
||||
} else {
|
||||
if ($dur < 60) {
|
||||
return $dur . '秒前';
|
||||
} else {
|
||||
if ($dur < 3600) {
|
||||
return floor($dur / 60) . '分钟前';
|
||||
} else {
|
||||
if ($dur < 86400) {
|
||||
return floor($dur / 3600) . '小时前';
|
||||
} else {
|
||||
if ($dur < 2592000) { // 30天内
|
||||
return floor($dur / 86400) . '天前';
|
||||
} else {
|
||||
return date('Y-m-d', $timestamp);;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return date('Y-m-d', $timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小
|
||||
* @param $file_size
|
||||
* @return string
|
||||
*/
|
||||
public static function formatBytes($file_size): string
|
||||
{
|
||||
$size = sprintf("%u", $file_size);
|
||||
if($size == 0) {
|
||||
return("0 Bytes");
|
||||
}
|
||||
$size_name = array(" Bytes", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB");
|
||||
return round($size/pow(1024, ($i = floor(log($size, 1024)))), 2) . $size_name[$i];
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库字符串转义
|
||||
* @param $var
|
||||
* @return false|string
|
||||
*/
|
||||
public static function pdoQuote($var)
|
||||
{
|
||||
return Util::db()->getPdo()->quote($var, \PDO::PARAM_STR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查表名是否合法
|
||||
* @param string $table
|
||||
* @return string
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public static function checkTableName(string $table): string
|
||||
{
|
||||
if (!preg_match('/^[a-zA-Z_0-9]+$/', $table)) {
|
||||
throw new BusinessException('表名不合法');
|
||||
}
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* 变量或数组中的元素只能是字母数字下划线组合
|
||||
* @param $var
|
||||
* @return mixed
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public static function filterAlphaNum($var)
|
||||
{
|
||||
$vars = (array)$var;
|
||||
array_walk_recursive($vars, function ($item) {
|
||||
if (is_string($item) && !preg_match('/^[a-zA-Z_0-9]+$/', $item)) {
|
||||
throw new BusinessException('参数不合法');
|
||||
}
|
||||
});
|
||||
return $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* 变量或数组中的元素只能是字母数字
|
||||
* @param $var
|
||||
* @return mixed
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public static function filterNum($var)
|
||||
{
|
||||
$vars = (array)$var;
|
||||
array_walk_recursive($vars, function ($item) {
|
||||
if (is_string($item) && !preg_match('/^[0-9]+$/', $item)) {
|
||||
throw new BusinessException('参数不合法');
|
||||
}
|
||||
});
|
||||
return $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否是合法URL Path
|
||||
* @param $var
|
||||
* @return string
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public static function filterUrlPath($var): string
|
||||
{
|
||||
if (!is_string($var) || !preg_match('/^[a-zA-Z0-9_\-\/&?.]+$/', $var)) {
|
||||
throw new BusinessException('参数不合法');
|
||||
}
|
||||
return $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否是合法Path
|
||||
* @param $var
|
||||
* @return string
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public static function filterPath($var): string
|
||||
{
|
||||
if (!is_string($var) || !preg_match('/^[a-zA-Z0-9_\-\/]+$/', $var)) {
|
||||
throw new BusinessException('参数不合法');
|
||||
}
|
||||
return $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* 类转换为url path
|
||||
* @param $controller_class
|
||||
* @return false|string
|
||||
*/
|
||||
static function controllerToUrlPath($controller_class)
|
||||
{
|
||||
$key = strtolower($controller_class);
|
||||
$action = '';
|
||||
if (strpos($key, '@')) {
|
||||
[$key, $action] = explode( '@', $key, 2);
|
||||
}
|
||||
$prefix = 'plugin';
|
||||
$paths = explode('\\', $key);
|
||||
if (count($paths) < 2) {
|
||||
return false;
|
||||
}
|
||||
$base = '';
|
||||
if (strpos($key, "$prefix\\") === 0) {
|
||||
if (count($paths) < 4) {
|
||||
return false;
|
||||
}
|
||||
array_shift($paths);
|
||||
$plugin = array_shift($paths);
|
||||
$base = "/app/$plugin/";
|
||||
}
|
||||
array_shift($paths);
|
||||
foreach ($paths as $index => $path) {
|
||||
if ($path === 'controller') {
|
||||
unset($paths[$index]);
|
||||
}
|
||||
}
|
||||
$suffix = 'controller';
|
||||
$code = $base . implode('/', $paths);
|
||||
if (substr($code, -strlen($suffix)) === $suffix) {
|
||||
$code = substr($code, 0, -strlen($suffix));
|
||||
}
|
||||
return $action ? "$code/$action" : $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为驼峰
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function camel(string $value): string
|
||||
{
|
||||
static $cache = [];
|
||||
$key = $value;
|
||||
|
||||
if (isset($cache[$key])) {
|
||||
return $cache[$key];
|
||||
}
|
||||
|
||||
$value = ucwords(str_replace(['-', '_'], ' ', $value));
|
||||
|
||||
return $cache[$key] = str_replace(' ', '', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为小驼峰
|
||||
* @param $value
|
||||
* @return string
|
||||
*/
|
||||
public static function smCamel($value): string
|
||||
{
|
||||
return lcfirst(static::camel($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注释中第一行
|
||||
* @param $comment
|
||||
* @return false|mixed|string
|
||||
*/
|
||||
public static function getCommentFirstLine($comment)
|
||||
{
|
||||
if ($comment === false) {
|
||||
return false;
|
||||
}
|
||||
foreach (explode("\n", $comment) as $str) {
|
||||
if ($s = trim($str, "*/\ \t\n\r\0\x0B")) {
|
||||
return $s;
|
||||
}
|
||||
}
|
||||
return $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单类型到插件的映射
|
||||
* @return \string[][]
|
||||
*/
|
||||
public static function methodControlMap(): array
|
||||
{
|
||||
return [
|
||||
//method=>[控件]
|
||||
'integer' => ['InputNumber'],
|
||||
'string' => ['Input'],
|
||||
'text' => ['TextArea'],
|
||||
'date' => ['DatePicker'],
|
||||
'enum' => ['Select'],
|
||||
'float' => ['Input'],
|
||||
|
||||
'tinyInteger' => ['InputNumber'],
|
||||
'smallInteger' => ['InputNumber'],
|
||||
'mediumInteger' => ['InputNumber'],
|
||||
'bigInteger' => ['InputNumber'],
|
||||
|
||||
'unsignedInteger' => ['InputNumber'],
|
||||
'unsignedTinyInteger' => ['InputNumber'],
|
||||
'unsignedSmallInteger' => ['InputNumber'],
|
||||
'unsignedMediumInteger' => ['InputNumber'],
|
||||
'unsignedBigInteger' => ['InputNumber'],
|
||||
|
||||
'decimal' => ['Input'],
|
||||
'double' => ['Input'],
|
||||
|
||||
'mediumText' => ['TextArea'],
|
||||
'longText' => ['TextArea'],
|
||||
|
||||
'dateTime' => ['DateTimePicker'],
|
||||
|
||||
'time' => ['DateTimePicker'],
|
||||
'timestamp' => ['DateTimePicker'],
|
||||
|
||||
'char' => ['Input'],
|
||||
|
||||
'binary' => ['Input'],
|
||||
|
||||
'json' => ['input']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库类型到插件的转换
|
||||
* @param $type
|
||||
* @return string
|
||||
*/
|
||||
public static function typeToControl($type): string
|
||||
{
|
||||
if (stripos($type, 'int') !== false) {
|
||||
return 'inputNumber';
|
||||
}
|
||||
if (stripos($type, 'time') !== false || stripos($type, 'date') !== false) {
|
||||
return 'dateTimePicker';
|
||||
}
|
||||
if (stripos($type, 'text') !== false) {
|
||||
return 'textArea';
|
||||
}
|
||||
if ($type === 'enum') {
|
||||
return 'select';
|
||||
}
|
||||
return 'input';
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库类型到表单类型的转换
|
||||
* @param $type
|
||||
* @param $unsigned
|
||||
* @return string
|
||||
*/
|
||||
public static function typeToMethod($type, $unsigned = false)
|
||||
{
|
||||
if (stripos($type, 'int') !== false) {
|
||||
$type = str_replace('int', 'Integer', $type);
|
||||
return $unsigned ? "unsigned" . ucfirst($type) : lcfirst($type);
|
||||
}
|
||||
$map = [
|
||||
'int' => 'integer',
|
||||
'varchar' => 'string',
|
||||
'mediumtext' => 'mediumText',
|
||||
'longtext' => 'longText',
|
||||
'datetime' => 'dateTime',
|
||||
];
|
||||
return $map[$type] ?? $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按表获取摘要
|
||||
* @param $table
|
||||
* @param null $section
|
||||
* @return array|mixed
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public static function getSchema($table, $section = null)
|
||||
{
|
||||
Util::checkTableName($table);
|
||||
$database = config('database.connections')['mysql']['database'];
|
||||
$schema_raw = $section !== 'table' ? Util::db()->select("select * from information_schema.COLUMNS where TABLE_SCHEMA = '$database' and table_name = '$table' order by ORDINAL_POSITION") : [];
|
||||
$forms = [];
|
||||
$columns = [];
|
||||
foreach ($schema_raw as $item) {
|
||||
$field = $item->COLUMN_NAME;
|
||||
$columns[$field] = [
|
||||
'field' => $field,
|
||||
'type' => Util::typeToMethod($item->DATA_TYPE, (bool)strpos($item->COLUMN_TYPE, 'unsigned')),
|
||||
'comment' => $item->COLUMN_COMMENT,
|
||||
'default' => $item->COLUMN_DEFAULT,
|
||||
'length' => static::getLengthValue($item),
|
||||
'nullable' => $item->IS_NULLABLE !== 'NO',
|
||||
'primary_key' => $item->COLUMN_KEY === 'PRI',
|
||||
'auto_increment' => strpos($item->EXTRA, 'auto_increment') !== false
|
||||
];
|
||||
|
||||
$forms[$field] = [
|
||||
'field' => $field,
|
||||
'comment' => $item->COLUMN_COMMENT,
|
||||
'control' => static::typeToControl($item->DATA_TYPE),
|
||||
'form_show' => $item->COLUMN_KEY !== 'PRI',
|
||||
'list_show' => true,
|
||||
'enable_sort' => false,
|
||||
'searchable' => false,
|
||||
'search_type' => 'normal',
|
||||
'control_args' => '',
|
||||
];
|
||||
}
|
||||
$table_schema = $section == 'table' || !$section ? Util::db()->select("SELECT TABLE_COMMENT FROM information_schema.`TABLES` WHERE TABLE_SCHEMA='$database' and TABLE_NAME='$table'") : [];
|
||||
$indexes = !$section || in_array($section, ['keys', 'table']) ? Util::db()->select("SHOW INDEX FROM `$table`") : [];
|
||||
$keys = [];
|
||||
$primary_key = [];
|
||||
foreach ($indexes as $index) {
|
||||
$key_name = $index->Key_name;
|
||||
if ($key_name == 'PRIMARY') {
|
||||
$primary_key[] = $index->Column_name;
|
||||
continue;
|
||||
}
|
||||
if (!isset($keys[$key_name])) {
|
||||
$keys[$key_name] = [
|
||||
'name' => $key_name,
|
||||
'columns' => [],
|
||||
'type' => $index->Non_unique == 0 ? 'unique' : 'normal'
|
||||
];
|
||||
}
|
||||
$keys[$key_name]['columns'][] = $index->Column_name;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'table' => ['name' => $table, 'comment' => $table_schema[0]->TABLE_COMMENT ?? '', 'primary_key' => $primary_key],
|
||||
'columns' => $columns,
|
||||
'forms' => $forms,
|
||||
'keys' => array_reverse($keys, true)
|
||||
];
|
||||
|
||||
$schema = Option::where('name', "table_form_schema_$table")->value('value');
|
||||
$form_schema_map = $schema ? json_decode($schema, true) : [];
|
||||
|
||||
foreach ($data['forms'] as $field => $item) {
|
||||
if (isset($form_schema_map[$field])) {
|
||||
$data['forms'][$field] = $form_schema_map[$field];
|
||||
}
|
||||
}
|
||||
|
||||
return $section ? $data[$section] : $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段长度或默认值
|
||||
* @param $schema
|
||||
* @return mixed|string
|
||||
*/
|
||||
public static function getLengthValue($schema)
|
||||
{
|
||||
$type = $schema->DATA_TYPE;
|
||||
if (in_array($type, ['float', 'decimal', 'double'])) {
|
||||
return "{$schema->NUMERIC_PRECISION},{$schema->NUMERIC_SCALE}";
|
||||
}
|
||||
if ($type === 'enum') {
|
||||
return implode(',', array_map(function($item){
|
||||
return trim($item, "'");
|
||||
}, explode(',', substr($schema->COLUMN_TYPE, 5, -1))));
|
||||
}
|
||||
if (in_array($type, ['varchar', 'text', 'char'])) {
|
||||
return $schema->CHARACTER_MAXIMUM_LENGTH;
|
||||
}
|
||||
if (in_array($type, ['time', 'datetime', 'timestamp'])) {
|
||||
return $schema->CHARACTER_MAXIMUM_LENGTH;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取控件参数
|
||||
* @param $control
|
||||
* @param $control_args
|
||||
* @return array
|
||||
*/
|
||||
public static function getControlProps($control, $control_args): array
|
||||
{
|
||||
if (!$control_args) {
|
||||
return [];
|
||||
}
|
||||
$control = strtolower($control);
|
||||
$props = [];
|
||||
$split = explode(';', $control_args);
|
||||
foreach ($split as $item) {
|
||||
$pos = strpos($item, ':');
|
||||
if ($pos === false) {
|
||||
continue;
|
||||
}
|
||||
$name = trim(substr($item, 0, $pos));
|
||||
$values = trim(substr($item, $pos + 1));
|
||||
// values = a:v,c:d
|
||||
$pos = strpos($values, ':');
|
||||
if ($pos !== false && strpos($values, "#") !== 0) {
|
||||
$options = explode(',', $values);
|
||||
$values = [];
|
||||
foreach ($options as $option) {
|
||||
[$v, $n] = explode(':', $option);
|
||||
if (in_array($control, ['select', 'selectmulti', 'treeselect', 'treemultiselect']) && $name == 'data') {
|
||||
$values[] = ['value' => $v, 'name' => $n];
|
||||
} else {
|
||||
$values[$v] = $n;
|
||||
}
|
||||
}
|
||||
}
|
||||
$props[$name] = $values;
|
||||
}
|
||||
return $props;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个composer包的版本
|
||||
* @param string $package
|
||||
* @return mixed|string
|
||||
*/
|
||||
public static function getPackageVersion(string $package)
|
||||
{
|
||||
$installed_php = base_path('vendor/composer/installed.php');
|
||||
if (is_file($installed_php)) {
|
||||
$packages = include $installed_php;
|
||||
}
|
||||
return substr($packages['versions'][$package]['version'] ?? 'unknown ', 0, -2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reload webman
|
||||
* @return bool
|
||||
*/
|
||||
public static function reloadWebman()
|
||||
{
|
||||
if (function_exists('posix_kill')) {
|
||||
try {
|
||||
posix_kill(posix_getppid(), SIGUSR1);
|
||||
return true;
|
||||
} catch (Throwable $e) {}
|
||||
} else {
|
||||
Timer::add(1, function () {
|
||||
Worker::stopAll();
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause file monitor
|
||||
* @return void
|
||||
*/
|
||||
public static function pauseFileMonitor()
|
||||
{
|
||||
if (method_exists(Monitor::class, 'pause')) {
|
||||
Monitor::pause();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume file monitor
|
||||
* @return void
|
||||
*/
|
||||
public static function resumeFileMonitor()
|
||||
{
|
||||
if (method_exists(Monitor::class, 'resume')) {
|
||||
Monitor::resume();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\common\Auth;
|
||||
use plugin\admin\app\common\Util;
|
||||
use plugin\admin\app\model\Admin;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Exception;
|
||||
use Webman\Captcha\CaptchaBuilder;
|
||||
use Webman\Captcha\PhraseBuilder;
|
||||
|
||||
/**
|
||||
* 管理员账户
|
||||
*/
|
||||
class AccountController extends Crud
|
||||
{
|
||||
/**
|
||||
* 不需要登录的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedLogin = ['login', 'logout', 'captcha'];
|
||||
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['info'];
|
||||
|
||||
/**
|
||||
* @var Admin
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new Admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 账户设置
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index(Request $request):Response
|
||||
{
|
||||
$admin = admin();
|
||||
if (!$admin) {
|
||||
return $this->json(1);
|
||||
}
|
||||
$info = [
|
||||
'id' => $admin['id'],
|
||||
'username' => $admin['username'],
|
||||
'nickname' => $admin['nickname'],
|
||||
'avatar' => $admin['avatar'],
|
||||
'email' => $admin['email'],
|
||||
'mobile' => $admin['mobile'],
|
||||
'isSuperAdmin' => Auth::isSuperAdmin()
|
||||
];
|
||||
return view('account/index',[
|
||||
'row' => $info
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function login(Request $request): Response
|
||||
{
|
||||
$this->checkDatabaseAvailable();
|
||||
if(Config('site.admin_login_captcha')){
|
||||
$captcha = $request->post('captcha', '');
|
||||
if (strtolower($captcha) !== session('captcha-login')) {
|
||||
return $this->fail('验证码错误');
|
||||
}
|
||||
$request->session()->forget('captcha-login');
|
||||
}
|
||||
$username = $request->post('username', '');
|
||||
$this->removeLoginLimit($username);
|
||||
$password = $request->post('password', '');
|
||||
if (!$username) {
|
||||
return $this->fail('用户名不能为空');
|
||||
}
|
||||
$this->checkLoginLimit($username);
|
||||
/**
|
||||
* @var Admin $admin
|
||||
*/
|
||||
$admin = Admin::where('username', $username)->find();
|
||||
// if (!$admin || !Util::passwordVerify($password, $admin->password)) {
|
||||
// return $this->fail('账户不存在或密码错误');
|
||||
// }
|
||||
//$secret = $admin['totp_secret'] ?:'EJGYB7OZR2W46XRX7VB3PXHSOY4LUAWCA5GTDAVTWKHXNDAAAIIP7AQ3JSO3XZJNX5J5OTIDEQVKLYFYIYNAXSCYF4GNZ2EMA4ORA3Y';
|
||||
\support\Log::alert($admin['totp_secret']);
|
||||
$totp = \OTPHP\TOTP::create($admin->totp_secret);
|
||||
//$secret = $totp->getSecret();
|
||||
//$totp->setLabel('cansnow');
|
||||
//$totp->setIssuer('DVPN');
|
||||
//$qrCodeUri =$totp->getProvisioningUri();
|
||||
//cp($secret);
|
||||
//cp($qrCodeUri);
|
||||
//cp('https://api.qrtool.cn/?text='.urlencode($qrCodeUri));
|
||||
//cp($totp->at(time()));
|
||||
if (!$totp->verify($request->post('code', ''))) {
|
||||
return $this->fail('当前账户暂时无法登录1');
|
||||
}
|
||||
if ($admin->status != 1) {
|
||||
return $this->fail('当前账户暂时无法登录');
|
||||
}
|
||||
$admin->login_at = time();
|
||||
$admin->save();
|
||||
$this->removeLoginLimit($username);
|
||||
$admin = $admin->toArray();
|
||||
$session = $request->session();
|
||||
$admin['password'] = md5($admin['password']);
|
||||
$session->set('admin', $admin);
|
||||
return $this->success('登录成功', [
|
||||
'nickname' => $admin['nickname'],
|
||||
'token' => $request->sessionId(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function logout(Request $request): Response
|
||||
{
|
||||
$request->session()->delete('admin');
|
||||
return $this->success('操作成功',['url'=>url('index/index')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录信息
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function info(Request $request): Response
|
||||
{
|
||||
$admin = admin();
|
||||
if (!$admin) {
|
||||
return $this->json(1);
|
||||
}
|
||||
$info = [
|
||||
'id' => $admin['id'],
|
||||
'username' => $admin['username'],
|
||||
'nickname' => $admin['nickname'],
|
||||
'avatar' => $admin['avatar'],
|
||||
'email' => $admin['email'],
|
||||
'mobile' => $admin['mobile'],
|
||||
'isSuperAdmin' => Auth::isSuperAdmin(),
|
||||
'token' => $request->sessionId(),
|
||||
];
|
||||
return $this->success("操作成功", $info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$allow_column = [
|
||||
'nickname' => 'nickname',
|
||||
'avatar' => 'avatar',
|
||||
'email' => 'email',
|
||||
'mobile' => 'mobile',
|
||||
];
|
||||
|
||||
$data = $request->post();
|
||||
$update_data = [];
|
||||
foreach ($allow_column as $key => $column) {
|
||||
if (isset($data[$key])) {
|
||||
$update_data[$column] = $data[$key];
|
||||
}
|
||||
}
|
||||
if (isset($update_data['password'])) {
|
||||
$update_data['password'] = Util::passwordHash($update_data['password']);
|
||||
}
|
||||
Admin::where('id', admin_id())->update($update_data);
|
||||
$admin = admin();
|
||||
unset($update_data['password']);
|
||||
foreach ($update_data as $key => $value) {
|
||||
$admin[$key] = $value;
|
||||
}
|
||||
$request->session()->set('admin', $admin);
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function password(Request $request): Response
|
||||
{
|
||||
if(Request()->method() == 'POST'){
|
||||
/**
|
||||
* @var string $hash
|
||||
*/
|
||||
$hash = Admin::find(admin_id())['password'];
|
||||
$password = $request->post('password');
|
||||
if (!$password) {
|
||||
return $this->json(2, '密码不能为空');
|
||||
}
|
||||
if ($request->post('password_confirm') !== $password) {
|
||||
return $this->json(3, '两次密码输入不一致');
|
||||
}
|
||||
if (!Util::passwordVerify($request->post('old_password'), $hash)) {
|
||||
return $this->fail('原始密码不正确');
|
||||
}
|
||||
$update_data = [
|
||||
'password' => Util::passwordHash($password)
|
||||
];
|
||||
Admin::where('id', admin_id())->update($update_data);
|
||||
return $this->success("操作成功");
|
||||
}else{
|
||||
return view('account/password');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
* @param Request $request
|
||||
* @param string $type
|
||||
* @return Response
|
||||
*/
|
||||
public function captcha(Request $request, string $type = 'login'): Response
|
||||
{
|
||||
$builder = new PhraseBuilder(4, 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ');
|
||||
$captcha = new CaptchaBuilder(null, $builder);
|
||||
$captcha->build(120);
|
||||
$request->session()->set("captcha-$type", strtolower($captcha->getPhrase()));
|
||||
$img_content = $captcha->get();
|
||||
return response($img_content, 200, ['Content-Type' => 'image/jpeg']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查登录频率限制
|
||||
* @param $username
|
||||
* @return void
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function checkLoginLimit($username)
|
||||
{
|
||||
$limit_log_path = runtime_path() . '/login';
|
||||
if (!is_dir($limit_log_path)) {
|
||||
mkdir($limit_log_path, 0777, true);
|
||||
}
|
||||
$limit_file = $limit_log_path . '/' . md5($username) . '.limit';
|
||||
$time = date('YmdH') . ceil(date('i')/5);
|
||||
$limit_info = [];
|
||||
if (is_file($limit_file)) {
|
||||
$json_str = file_get_contents($limit_file);
|
||||
$limit_info = json_decode($json_str, true);
|
||||
}
|
||||
|
||||
if (!$limit_info || $limit_info['time'] != $time) {
|
||||
$limit_info = [
|
||||
'username' => $username,
|
||||
'count' => 0,
|
||||
'time' => $time
|
||||
];
|
||||
}
|
||||
$limit_info['count']++;
|
||||
file_put_contents($limit_file, json_encode($limit_info));
|
||||
if ($limit_info['count'] >= 5) {
|
||||
throw new BusinessException('登录失败次数过多,请5分钟后再试');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除登录频率限制
|
||||
* @param $username
|
||||
* @return void
|
||||
*/
|
||||
protected function removeLoginLimit($username)
|
||||
{
|
||||
$limit_log_path = runtime_path() . '/login';
|
||||
$limit_file = $limit_log_path . '/' . md5($username) . '.limit';
|
||||
if (is_file($limit_file)) {
|
||||
unlink($limit_file);
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkDatabaseAvailable()
|
||||
{
|
||||
if (!config('thinkorm')) {
|
||||
throw new BusinessException('请重启webman');
|
||||
}
|
||||
}
|
||||
public function profile(Request $request):Response
|
||||
{
|
||||
if(Request()->method() == 'POST'){
|
||||
return $this->update($request);
|
||||
}
|
||||
$admin = admin();
|
||||
if (!$admin) {
|
||||
return $this->json(1);
|
||||
}
|
||||
$info = [
|
||||
'id' => $admin['id'],
|
||||
'username' => $admin['username'],
|
||||
'nickname' => $admin['nickname'],
|
||||
'avatar' => $admin['avatar'],
|
||||
'email' => $admin['email'],
|
||||
'mobile' => $admin['mobile'],
|
||||
'isSuperAdmin' => Auth::isSuperAdmin(),
|
||||
'token' => $request->sessionId(),
|
||||
];
|
||||
return view('account/index',[
|
||||
'row' => $info
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use app\model\Address as AddressModel;
|
||||
|
||||
/**
|
||||
* 用户地址
|
||||
*/
|
||||
class AddressController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \app\model\Address
|
||||
*/
|
||||
protected $model = null;
|
||||
protected $relationSearch = ['user'];
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new AddressModel();
|
||||
$statusList = $this->model->getStatusList();
|
||||
$this->assign("statusList", $statusList);
|
||||
$this->assignconfig("statusList", $statusList);
|
||||
$this->assign("networkList", $this->model->getNetworkList());
|
||||
$this->assignconfig("networkList", $this->model->getNetworkList());
|
||||
}
|
||||
function __before_index__(){
|
||||
$this->assign('refreshBalance',cache('refreshBalance'));
|
||||
}
|
||||
function refreshbalance(){
|
||||
$ids = Input('ids');
|
||||
if($ids){
|
||||
$data = AddressModel::field('id,network,address')->whereIn('id',$ids)->order('id asc')->find();
|
||||
try {
|
||||
$res = get(Config('pay.server').'/Util/balance?address='.$data['address'].'&network='.$data['network']);
|
||||
$res = json_decode($res,true);
|
||||
AddressModel::where('id',$data['id'])->update($res['data']);
|
||||
\support\Log::info($data['address'].'已经更新');
|
||||
} catch (\Exception $e) {
|
||||
\support\Log::info($data['address'].'更新失败:'.$e->getMessage());
|
||||
}
|
||||
}
|
||||
return $this->success('ok');
|
||||
}
|
||||
function refresh_balance(){
|
||||
return $this->success('ok');
|
||||
// $ids = Input('ids');
|
||||
// if($ids){
|
||||
// $address = AddressModel::field('id,network,address,approve_address')->whereIn('id',$ids)->order('id asc')->select();
|
||||
// }else{
|
||||
// $address = AddressModel::field('id,network,address,approve_address')->order('id asc')->select();
|
||||
// }
|
||||
// $data = $address->toArray();
|
||||
// addJob($data,'refreshBalance');
|
||||
// return $this->success('ok');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\common\Auth;
|
||||
use plugin\admin\app\model\Admin as AdminModel;
|
||||
use plugin\admin\app\model\AdminAccess;
|
||||
use plugin\admin\app\model\AdminRole;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 管理员列表
|
||||
*/
|
||||
class AdminController extends Crud
|
||||
{
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedAuth = ['select'];
|
||||
|
||||
/**
|
||||
* @var AdminModel
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 开启auth数据限制
|
||||
* @var string
|
||||
*/
|
||||
protected $dataLimit = 'auth';
|
||||
|
||||
/**
|
||||
* 以id为数据限制字段
|
||||
* @var string
|
||||
*/
|
||||
protected $dataLimitField = 'id';
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new AdminModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
if ($format === 'select') {
|
||||
return $this->formatSelect($query->select());
|
||||
}
|
||||
$paginator = $query->paginate($limit);
|
||||
$items = $paginator->items();
|
||||
$admin_ids = array_column($items, 'id');
|
||||
$roles = AdminAccess::whereIn('admin_id', $admin_ids)->select();
|
||||
$roles_map = [];
|
||||
foreach ($roles as $role) {
|
||||
$roles_map[$role['admin_id']][] = $role['role_id'];
|
||||
}
|
||||
$login_admin_id = admin_id();
|
||||
/** @var AdminModel $item */
|
||||
foreach ($items as $index => $item) {
|
||||
$admin_id = $item->id;
|
||||
$items[$index]['roles'] = isset($roles_map[$admin_id]) ? implode(',', $roles_map[$admin_id]) : '';
|
||||
$items[$index]['role_name'] = $items[$index]['roles'] ? AdminRole::where('id',$items[$index]['roles'])->value('name') : '';
|
||||
$items[$index]['show_toolbar'] = $admin_id != $login_admin_id;
|
||||
}
|
||||
return json(['code' => 0, 'msg' => 'ok', 'count' => $paginator->total(), 'data' => $items]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
$data = $this->insertInput($request);
|
||||
$data['status'] = 1;
|
||||
unset($data['id']);
|
||||
$admin_id = $this->doInsert($data);
|
||||
$role_ids = $request->post('roles');
|
||||
$role_ids = $role_ids ? explode(',', $role_ids) : [];
|
||||
if (!$role_ids) {
|
||||
return $this->fail('至少选择一个角色组');
|
||||
}
|
||||
if (!Auth::isSuperAdmin() && array_diff($role_ids, Auth::getScopeRoleIds())) {
|
||||
return $this->fail('角色超出权限范围');
|
||||
}
|
||||
|
||||
AdminAccess::where('admin_id', $admin_id)->delete();
|
||||
foreach ($role_ids as $id) {
|
||||
$admin_role = new AdminAccess;
|
||||
$admin_role->admin_id = $admin_id;
|
||||
$admin_role->role_id = $id;
|
||||
$admin_role->save();
|
||||
}
|
||||
return $this->success( '保存成功', ['id' => $admin_id]);
|
||||
}
|
||||
return view('admin/update',[
|
||||
'row' => []
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
$admin_id = $request->post('id');
|
||||
if (!$admin_id) {
|
||||
return $this->fail('缺少参数');
|
||||
}
|
||||
|
||||
// 不能禁用自己
|
||||
if (isset($data['status']) && $data['status'] != 1 && $id == admin_id()) {
|
||||
return $this->fail('不能禁用自己');
|
||||
}
|
||||
|
||||
// 需要更新角色
|
||||
$role_ids = $request->post('roles');
|
||||
if ($role_ids !== null) {
|
||||
if (!$role_ids) {
|
||||
return $this->fail('至少选择一个角色组');
|
||||
}
|
||||
$role_ids = explode(',', $role_ids);
|
||||
|
||||
$is_supper_admin = Auth::isSuperAdmin();
|
||||
$exist_role_ids = AdminAccess::where('admin_id', $admin_id)->column('role_id');
|
||||
$scope_role_ids = Auth::getScopeRoleIds();
|
||||
if (!$is_supper_admin && !array_intersect($exist_role_ids, $scope_role_ids)) {
|
||||
return $this->fail('无权限更改该记录');
|
||||
}
|
||||
if (!$is_supper_admin && array_diff($role_ids, $scope_role_ids)) {
|
||||
return $this->fail('角色超出权限范围');
|
||||
}
|
||||
|
||||
// 删除账户角色
|
||||
$delete_ids = array_diff($exist_role_ids, $role_ids);
|
||||
AdminAccess::whereIn('role_id', $delete_ids)->where('admin_id', $admin_id)->delete();
|
||||
// 添加账户角色
|
||||
$add_ids = array_diff($role_ids, $exist_role_ids);
|
||||
foreach ($add_ids as $role_id) {
|
||||
$admin_role = new AdminAccess;
|
||||
$admin_role->admin_id = $admin_id;
|
||||
$admin_role->role_id = $role_id;
|
||||
$admin_role->save();
|
||||
}
|
||||
}
|
||||
unset($data['roles']);
|
||||
|
||||
$this->doUpdate($id, $data);
|
||||
return $this->success('保存成功');
|
||||
}
|
||||
$ids = Request()->get('ids');
|
||||
if($ids){
|
||||
$row = $this->model->where('id', $ids)->find();
|
||||
}
|
||||
|
||||
return view('admin/update',[
|
||||
'row'=> $row
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$primary_key = $this->model->getPk();
|
||||
$ids = $request->post('ids');
|
||||
if (!$ids) {
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
$ids = (array)$ids;
|
||||
if (in_array(admin_id(), $ids)) {
|
||||
return $this->fail('不能删除自己');
|
||||
}
|
||||
if (!Auth::isSuperAdmin() && array_diff($ids, Auth::getScopeAdminIds())) {
|
||||
return $this->fail('无数据权限');
|
||||
}
|
||||
$this->model->whereIn($primary_key, $ids)->each(function (AdminModel $admin) {
|
||||
$admin->delete();
|
||||
});
|
||||
AdminRole::whereIn('admin_id', $ids)->each(function (AdminRole $admin_role) {
|
||||
$admin_role->delete();
|
||||
});
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\common\Auth;
|
||||
use plugin\admin\app\common\Tree;
|
||||
use plugin\admin\app\model\AdminRole;
|
||||
use plugin\admin\app\model\AdminRule;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 角色管理
|
||||
*/
|
||||
class AdminRoleController extends Crud
|
||||
{
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedAuth = ['select'];
|
||||
|
||||
/**
|
||||
* @var AdminRole
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new AdminRole;
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
return view('admin_role/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
$id = $request->get('id');
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
$limit = 100000;
|
||||
$role_ids = Auth::getScopeRoleIds(true);
|
||||
if (!$id) {
|
||||
$where['id'] = ['symbol'=>'in', 'value1'=>$role_ids];
|
||||
} elseif (!in_array($id, $role_ids)) {
|
||||
throw new BusinessException('无权限');
|
||||
}
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
return $this->doFormat($query, $format, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
$data = $this->insertInput($request);
|
||||
$pid = $data['pid'] ?? "";
|
||||
// if (!$pid) {
|
||||
// return $this->fail('请选择父级角色组');
|
||||
// }
|
||||
if($pid){
|
||||
if (!Auth::isSuperAdmin() && !in_array($pid, Auth::getScopeRoleIds(true))) {
|
||||
return $this->fail('父级角色组超出权限范围');
|
||||
}
|
||||
$this->checkRules($pid, $data['rules'] ?? '');
|
||||
}
|
||||
$id = $this->doInsert($data);
|
||||
return $this->success("操作成功", ['id' => $id]);
|
||||
}
|
||||
return view('admin_role/update',[
|
||||
'rolelist'=> $this->model->select()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'GET') {
|
||||
$ids = Request()->get('ids');
|
||||
return view('admin_role/update',[
|
||||
'rolelist'=> $this->model->where('id','<>',$ids)->select(),
|
||||
'row' => $this->model->whereIn('id',$ids)->findOrEmpty()
|
||||
]);
|
||||
}
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
$is_supper_admin = Auth::isSuperAdmin();
|
||||
$descendant_role_ids = Auth::getScopeRoleIds();
|
||||
if (!$is_supper_admin && !in_array($id, $descendant_role_ids)) {
|
||||
return $this->fail('无数据权限');
|
||||
}
|
||||
|
||||
$role = AdminRole::find($id);
|
||||
if (!$role) {
|
||||
return $this->fail('数据不存在');
|
||||
}
|
||||
$is_supper_role = $role->rules === '*';
|
||||
|
||||
// 超级角色组不允许更改rules pid 字段
|
||||
if ($is_supper_role) {
|
||||
unset($data['rules'], $data['pid']);
|
||||
}
|
||||
|
||||
if (key_exists('pid', $data) && $data['pid']>0) {
|
||||
$pid = $data['pid'];
|
||||
if (!$pid) {
|
||||
return $this->fail('请选择父级角色组');
|
||||
}
|
||||
if ($pid == $id) {
|
||||
return $this->fail('父级不能是自己');
|
||||
}
|
||||
if (!$is_supper_admin && !in_array($pid, Auth::getScopeRoleIds(true))) {
|
||||
return $this->fail('父级超出权限范围');
|
||||
}
|
||||
} else {
|
||||
$pid = $role->pid;
|
||||
}
|
||||
$data['rules'] = explode(',',$data['rules']);
|
||||
sort($data['rules']);
|
||||
if(count($data['rules'])>0 && $data['rules'][0] == 0){
|
||||
array_shift($data['rules']);
|
||||
}
|
||||
$data['rules'] = implode(',',$data['rules']);
|
||||
if (!$is_supper_role) {
|
||||
$this->checkRules($pid, $data['rules'] ?? '');
|
||||
}
|
||||
|
||||
$this->doUpdate($id, $data);
|
||||
|
||||
// 删除所有子角色组中已经不存在的权限
|
||||
if (!$is_supper_role) {
|
||||
$tree = new Tree(AdminRole::Field(['id', 'pid'])->select());
|
||||
$descendant_roles = $tree->getDescendant([$id]);
|
||||
$descendant_role_ids = array_column($descendant_roles, 'id');
|
||||
$rule_ids = $data['rules'] ? explode(',', $data['rules']) : [];
|
||||
foreach ($descendant_role_ids as $role_id) {
|
||||
$tmp_role = AdminRole::find($role_id);
|
||||
$tmp_rule_ids = $role->getRuleIds();
|
||||
$tmp_rule_ids = array_intersect(explode(',',$tmp_role->rules), $tmp_rule_ids);
|
||||
$tmp_role->rules = implode(',', $tmp_rule_ids);
|
||||
$tmp_role->save();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$ids = $this->deleteInput($request);
|
||||
if (in_array(1, $ids)) {
|
||||
return $this->fail('无法删除超级管理员角色');
|
||||
}
|
||||
if (!Auth::isSuperAdmin() && array_diff($ids, Auth::getScopeRoleIds())) {
|
||||
return $this->fail('无删除权限');
|
||||
}
|
||||
$tree = new Tree(AdminRole::select());
|
||||
$descendants = $tree->getDescendant($ids);
|
||||
if ($descendants) {
|
||||
$ids = array_merge($ids, array_column($descendants, 'id'));
|
||||
}
|
||||
$this->doDelete($ids);
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色权限
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function rules(Request $request): Response
|
||||
{
|
||||
$role_id = $request->get('id');
|
||||
if (empty($role_id)) {
|
||||
return $this->success("操作成功", []);
|
||||
}
|
||||
if (!Auth::isSuperAdmin() && !in_array($role_id, Auth::getScopeRoleIds(true))) {
|
||||
return $this->fail('角色组超出权限范围');
|
||||
}
|
||||
$rule_id_string = AdminRole::where('id', $role_id)->value('rules');
|
||||
if ($rule_id_string === '') {
|
||||
return $this->success("操作成功", []);
|
||||
}
|
||||
$rules = AdminRule::select();
|
||||
$include = [];
|
||||
if ($rule_id_string !== '*') {
|
||||
$include = explode(',', $rule_id_string);
|
||||
}
|
||||
$items = [];
|
||||
foreach ($rules as $item) {
|
||||
$items[] = [
|
||||
'name' => $item->title ?? $item->name ?? $item->id,
|
||||
'value' => (string)$item->id,
|
||||
'id' => $item->id,
|
||||
'pid' => $item->pid,
|
||||
];
|
||||
}
|
||||
$tree = new Tree($items);
|
||||
return $this->success("操作成功", $tree->getTree($include));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查权限字典是否合法
|
||||
* @param int $role_id
|
||||
* @param $rule_ids
|
||||
* @return void
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function checkRules(int|string|null $role_id, $rule_ids)
|
||||
{
|
||||
if ($rule_ids && $role_id>0) {
|
||||
$rule_ids = explode(',', $rule_ids);
|
||||
if (in_array('*', $rule_ids)) {
|
||||
throw new BusinessException('非法数据');
|
||||
}
|
||||
$rule_exists = AdminRule::whereIn('id', $rule_ids)->column('id');
|
||||
$rule_ids = $rule_exists;
|
||||
// if (count($rule_exists) != count($rule_ids)) {
|
||||
// throw new BusinessException('权限不存在');
|
||||
// }
|
||||
$rule_id_string = AdminRole::where('id', $role_id)->value('rules');
|
||||
if ($rule_id_string === '') {
|
||||
throw new BusinessException('数据超出权限范围');
|
||||
}
|
||||
if ($rule_id_string === '*') {
|
||||
return;
|
||||
}
|
||||
$legal_rule_ids = explode(',', $rule_id_string);
|
||||
if (array_diff($rule_ids, $legal_rule_ids)) {
|
||||
throw new BusinessException('数据超出权限范围');
|
||||
}
|
||||
}
|
||||
}
|
||||
public function tree(Request $request): Response
|
||||
{
|
||||
$id = $request->post('id');
|
||||
$pid = $request->post('pid');
|
||||
//$is_supper_admin = Auth::isSuperAdmin();
|
||||
$parent_rules = '*';
|
||||
if($pid){
|
||||
$parent_rules = AdminRole::where('id', $pid)->value('rules') ?:'*';
|
||||
}
|
||||
$m = AdminRule::where('status',1)
|
||||
->Field('id,title as text,pid as parent,"menu" as type')
|
||||
->order('parent asc ,id asc');
|
||||
if($parent_rules == '*'){
|
||||
$rules = $m->select();
|
||||
}else{
|
||||
$rules = $m->whereIn('id', $parent_rules)->select();
|
||||
}
|
||||
$selected_ids = '';
|
||||
if($id){
|
||||
$selected_ids = AdminRole::where('id', $id)->value('rules');
|
||||
}
|
||||
if($selected_ids == '*'){
|
||||
$rules->each(function($item){
|
||||
$item->state = ['selected'=>false];
|
||||
return $item;
|
||||
});
|
||||
}else{
|
||||
$selected_ids = explode(',',$selected_ids);
|
||||
$rules->each(function($item)use($selected_ids){
|
||||
$item->state = [
|
||||
'selected'=>in_array($item->id, $selected_ids)
|
||||
];
|
||||
return $item;
|
||||
});
|
||||
}
|
||||
$rules->push([
|
||||
'id' => 0,
|
||||
'parent' => '#',
|
||||
'text' => '全部',
|
||||
'type' => 'menu',
|
||||
]);
|
||||
|
||||
return $this->success('ok',$rules);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,460 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use Cache;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
use plugin\admin\app\common\Auth;
|
||||
use plugin\admin\app\common\Tree;
|
||||
use plugin\admin\app\common\Util;
|
||||
use plugin\admin\app\model\AdminRole;
|
||||
use plugin\admin\app\model\AdminRule;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 权限菜单
|
||||
*/
|
||||
class AdminRuleController extends Crud
|
||||
{
|
||||
/**
|
||||
* 不需要权限的方法
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['get', 'permission'];
|
||||
|
||||
/**
|
||||
* @var AdminRule
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new AdminRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
return view('admin_rule/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化树
|
||||
* @param $items
|
||||
* @return Response
|
||||
*/
|
||||
protected function formatTree($items): Response
|
||||
{
|
||||
$format_items = [];
|
||||
//$primary_key = $this->model->getPk();
|
||||
$primary_key = $this->model->getPk();
|
||||
foreach ($items as $item) {
|
||||
$item->text = $item->title;
|
||||
//$item->value = (string)$item->$primary_key;
|
||||
$item->id = $item->$primary_key;
|
||||
$item->parent = $item->pid;
|
||||
if($item->pid == 0){
|
||||
$item->parent = '#';
|
||||
}
|
||||
unset($item->pid);
|
||||
$format_items[] = $item;
|
||||
}
|
||||
$tree = new Tree($format_items);
|
||||
return $this->success("操作成功", $tree->getTree());
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限树形
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function roletree(Request $request): Response
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
$methods = [
|
||||
'select' => 'formatSelect',
|
||||
'tree' => 'formatTree',
|
||||
'table_tree' => 'formatTableTree',
|
||||
'normal' => 'formatNormal',
|
||||
];
|
||||
$format = 'normal';
|
||||
$items = $query->field('id,title as text,pid as parent,"menu" as type')->select();
|
||||
/**
|
||||
* @var AdminRule $item
|
||||
*/
|
||||
foreach ($items as $key => $item) {
|
||||
$items[$key]->state = [
|
||||
"selected" => false,
|
||||
];
|
||||
if($items[$key]->parent == 0){
|
||||
$items[$key]->parent = '#';
|
||||
}
|
||||
};
|
||||
if (method_exists($this, "afterQuery")) {
|
||||
$items = call_user_func([$this, "afterQuery"], $items);
|
||||
}
|
||||
$format_function = $methods[$format] ?? 'formatNormal';
|
||||
|
||||
return call_user_func([$this, $format_function], $items, 0);
|
||||
}
|
||||
/**
|
||||
* 查询
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
return $this->doFormat($query, $format, 'all');
|
||||
}
|
||||
/**
|
||||
* 刷新缓存
|
||||
* @param \support\Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function buildcache(Request $request){
|
||||
//缓存
|
||||
$list = $this->get($request)->rawBody();
|
||||
$list = json_decode($list,true);
|
||||
$fn = base_path() . '/plugin/admin/config/menu.php';
|
||||
file_put_contents($fn, "<?php \n return ".var_export($list['data'],true).';');
|
||||
return $this->success('ok');
|
||||
}
|
||||
/**
|
||||
* 获取菜单
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
function get(Request $request): Response
|
||||
{
|
||||
|
||||
$is_supper_admin = Auth::isSuperAdmin();
|
||||
if($is_supper_admin){
|
||||
$rules = ['*'];
|
||||
}else{
|
||||
$rules = $this->getRules(admin('roles'));
|
||||
}
|
||||
//return $this->success("操作成功", admin('roles'));
|
||||
$types = $request->get('type', '0,1');
|
||||
$types = is_string($types) ? explode(',', $types) : [0, 1];
|
||||
$items = AdminRule::order('weight', 'desc')->select();
|
||||
|
||||
$formatted_items = [];
|
||||
/**
|
||||
* @var AdminRule $item
|
||||
*/
|
||||
foreach ($items as $item) {
|
||||
$item['pid'] = (int)$item['pid'];
|
||||
$item['name'] = $item['title'];
|
||||
$item['value'] = $item['id'];
|
||||
$item['icon'] = $item->icon ? "{$item->icon}" : '';
|
||||
$formatted_items[] = $item;
|
||||
}
|
||||
|
||||
$tree = new Tree($formatted_items);
|
||||
$tree_items = $tree->getTree();
|
||||
// 超级管理员权限为 *
|
||||
if (!in_array('*', $rules)) {
|
||||
$this->removeNotContain($tree_items, 'id', $rules);
|
||||
}
|
||||
$this->removeNotContain($tree_items, 'type', $types);
|
||||
$menus = $this->empty_filter(Tree::arrayValues($tree_items));
|
||||
return $this->success("操作成功", $menus);
|
||||
}
|
||||
|
||||
private function empty_filter($menus)
|
||||
{
|
||||
return array_map(
|
||||
function ($menu) {
|
||||
if (isset($menu['children'])) {
|
||||
$menu['children'] = $this->empty_filter($menu['children']);
|
||||
}
|
||||
return $menu;
|
||||
},
|
||||
array_values(array_filter(
|
||||
$menus,
|
||||
function ($menu) {
|
||||
return $menu['type'] != 0 || isset($menu['children']) && count($this->empty_filter($menu['children'])) > 0;
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function permission(Request $request): Response
|
||||
{
|
||||
$rules = $this->getRules(admin('roles'));
|
||||
// 超级管理员
|
||||
if (in_array('*', $rules)) {
|
||||
return $this->success("操作成功", ['*']);
|
||||
}
|
||||
$keys = AdminRule::whereIn('id', $rules)->column('key');
|
||||
$permissions = [];
|
||||
foreach ($keys as $key) {
|
||||
if (!$key = Util::controllerToUrlPath($key)) {
|
||||
continue;
|
||||
}
|
||||
$code = str_replace('/', '.', trim($key, '/'));
|
||||
$permissions[] = $code;
|
||||
}
|
||||
return $this->success("操作成功", $permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步规则
|
||||
* @return void
|
||||
*/
|
||||
public function syncRules(): Response
|
||||
{
|
||||
//$items = $this->model->where('key', 'like', '%\\\\%')->get()->keyBy('key');
|
||||
$items = $this->model->whereLike('key', '%\\\\%')->select();
|
||||
$methods_in_db = [];
|
||||
$methods_in_files = [];
|
||||
/**
|
||||
* @var AdminRule $item
|
||||
*/
|
||||
foreach ($items as $item) {
|
||||
$class = $item->key;
|
||||
if (strpos($class, '@')) {
|
||||
$methods_in_db[$class] = $class;
|
||||
continue;
|
||||
}
|
||||
if (class_exists('\\plugin\\admin\\'.$class)) {
|
||||
$reflection = new \ReflectionClass('\\plugin\\admin\\'.$class);
|
||||
$properties = $reflection->getDefaultProperties();
|
||||
$no_need_auth = array_merge($properties['noNeedLogin'] ?? [], $properties['noNeedAuth'] ?? []);
|
||||
$class = str_replace('plugin\admin\\','',$reflection->getName());
|
||||
$pid = $item->id;
|
||||
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
|
||||
foreach ($methods as $method) {
|
||||
$method_name = $method->getName();
|
||||
if (strtolower($method_name) === 'index' || strpos($method_name, '__') === 0 || in_array($method_name, $no_need_auth)) {
|
||||
continue;
|
||||
}
|
||||
$name = "$class@$method_name";
|
||||
|
||||
$methods_in_files[$name] = $name;
|
||||
$title = Util::getCommentFirstLine($method->getDocComment()) ?: $method_name;
|
||||
/**
|
||||
* @var AdminRule $menu
|
||||
*/
|
||||
$menu = $items[$name] ?? [];
|
||||
if ($menu) {
|
||||
if ($menu->title != $title) {
|
||||
AdminRule::where('key', $name)->update(['title' => $title]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(!AdminRule::where('key', $name)->count('id')) {
|
||||
$menu = new AdminRule;
|
||||
$menu->pid = $pid;
|
||||
$menu->key = $name;
|
||||
$menu->title = $title;
|
||||
$menu->type = 2;
|
||||
$menu->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 从数据库中删除已经不存在的方法
|
||||
$menu_names_to_del = array_diff($methods_in_db, $methods_in_files);
|
||||
if ($menu_names_to_del) {
|
||||
AdminRule::whereIn('key', $menu_names_to_del)->delete();
|
||||
}
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询前置方法
|
||||
* @param Request $request
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function selectInput(Request $request): array
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = parent::selectInput($request);
|
||||
// 允许通过type=0,1格式传递菜单类型
|
||||
$types = $request->get('type');
|
||||
if ($types && is_string($types)) {
|
||||
$where['type'] = ['symbol'=>'in', 'value1'=>explode(',', $types)];
|
||||
}
|
||||
// 默认weight排序
|
||||
if (!$field) {
|
||||
$field = 'weight';
|
||||
$order = 'desc';
|
||||
}
|
||||
return [$where, $format, $limit, $field, $order];
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'GET') {
|
||||
$RuleList = $this->model->where('status',1)->order('id asc')->select();
|
||||
$tree = new Tree($RuleList);
|
||||
return view('admin_rule/update',[
|
||||
'RuleList' => $tree->getTree()
|
||||
]);
|
||||
}
|
||||
$data = $this->insertInput($request);
|
||||
if (empty($data['type'])) {
|
||||
$data['type'] = strpos($data['key'], '\\') ? 1 : 0;
|
||||
}
|
||||
$data['key'] = str_replace('\\\\', '\\', $data['key']);
|
||||
$key = $data['key'] ?? '';
|
||||
if ($this->model->where('key', $key)->count('id')) {
|
||||
return $this->fail("菜单标识 $key 已经存在");
|
||||
}
|
||||
$data['pid'] = empty($data['pid']) ? 0 : $data['pid'];
|
||||
$this->doInsert($data);
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'GET') {
|
||||
$RuleList = $this->model->order('id asc')->select();
|
||||
$ids = Request()->get('ids');
|
||||
$tree = new Tree($RuleList);
|
||||
return view('admin_rule/update',[
|
||||
'RuleList' => $tree->getTree(),
|
||||
'row' => $this->model->where('id',$ids)->find()
|
||||
]);
|
||||
}
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
if (!$row = $this->model->find($id)) {
|
||||
return $this->json(2, '记录不存在');
|
||||
}
|
||||
if (isset($data['pid'])) {
|
||||
$data['pid'] = $data['pid'] ?: 0;
|
||||
if ($data['pid'] == $row['id']) {
|
||||
return $this->json(2, '不能将自己设置为上级菜单');
|
||||
}
|
||||
}
|
||||
if (isset($data['key'])) {
|
||||
$data['key'] = str_replace('\\\\', '\\', $data['key']);
|
||||
}
|
||||
$this->doUpdate($id, $data);
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$ids = $this->deleteInput($request);
|
||||
// 子规则一起删除
|
||||
$delete_ids = $children_ids = $ids;
|
||||
while($children_ids) {
|
||||
$children_ids = $this->model->whereIn('pid', $children_ids)->column('id');
|
||||
$delete_ids = array_merge($delete_ids, $children_ids);
|
||||
}
|
||||
$this->doDelete($delete_ids);
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除不包含某些数据的数组
|
||||
* @param $array
|
||||
* @param $key
|
||||
* @param $values
|
||||
* @return void
|
||||
*/
|
||||
protected function removeNotContain(&$array, $key, $values)
|
||||
{
|
||||
foreach ($array as $k => &$item) {
|
||||
if (!is_array($item)) {
|
||||
continue;
|
||||
}
|
||||
if (!$this->arrayContain($item, $key, $values)) {
|
||||
unset($array[$k]);
|
||||
} else {
|
||||
if (!isset($item['children'])) {
|
||||
continue;
|
||||
}
|
||||
$this->removeNotContain($item['children'], $key, $values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断数组是否包含某些数据
|
||||
* @param $array
|
||||
* @param $key
|
||||
* @param $values
|
||||
* @return bool
|
||||
*/
|
||||
protected function arrayContain(&$array, $key, $values): bool
|
||||
{
|
||||
if (!is_array($array)) {
|
||||
return false;
|
||||
}
|
||||
if (isset($array[$key]) && in_array($array[$key], $values)) {
|
||||
return true;
|
||||
}
|
||||
if (!isset($array['children'])) {
|
||||
return false;
|
||||
}
|
||||
foreach ($array['children'] as $item) {
|
||||
if ($this->arrayContain($item, $key, $values)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限规则
|
||||
* @param $roles
|
||||
* @return array
|
||||
*/
|
||||
protected function getRules($roles): array
|
||||
{
|
||||
$rules_strings = $roles ? AdminRole::whereIn('id', $roles)->column('rules') : [];
|
||||
$rules = [];
|
||||
foreach ($rules_strings as $rule_string) {
|
||||
if (!$rule_string) {
|
||||
continue;
|
||||
}
|
||||
$rules = array_merge($rules, explode(',', $rule_string));
|
||||
}
|
||||
return $rules;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use Exception;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use support\think\Db;
|
||||
|
||||
/**
|
||||
* 内容管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
*/
|
||||
class ArchivesController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* Archives模型对象
|
||||
* @var \app\model\Archives
|
||||
*/
|
||||
protected $model = null;
|
||||
protected $type = 'default';
|
||||
protected $tpl = 'archives';
|
||||
protected $relationSearch = ['category'];
|
||||
protected $noNeedAuth = ['check_element_available', 'suggestion','tags', 'flag'];
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new \app\model\Archives;
|
||||
|
||||
$flagList = $this->model->getFlagList();
|
||||
$this->assignconfig("flagList", $flagList);
|
||||
$this->assign("flagList", $flagList);
|
||||
|
||||
$statusList = $this->model->getStatusList();
|
||||
$this->assign("statusList", $statusList);
|
||||
$this->assignconfig("statusList", $statusList);
|
||||
|
||||
$categoryList = $this->model->getCategoryOptions($this->type);
|
||||
$this->assign("categoryList", $categoryList);
|
||||
$this->assignconfig("categoryList", $categoryList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 查看
|
||||
*/
|
||||
public function index(Request $request):Response
|
||||
{
|
||||
return view($this->tpl.'/index');
|
||||
}
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() != 'POST') {
|
||||
return view($this->tpl.'/update',[
|
||||
'row' => [
|
||||
'type' => $this->type,
|
||||
'flag' => '',
|
||||
'status' => 1
|
||||
],
|
||||
]);
|
||||
}
|
||||
$params = $request->post();
|
||||
if (empty($params)) {
|
||||
return $this->fail(__('Parameter %name% can not be empty', []));
|
||||
}
|
||||
$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()->validate($validate);
|
||||
}
|
||||
$result = $this->model->save($params);
|
||||
Db::commit();
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
return $this->fail($e->getMessage());
|
||||
}
|
||||
if ($result === false) {
|
||||
return $this->fail(__('No rows were inserted'));
|
||||
}
|
||||
return $this->success();
|
||||
}
|
||||
/**
|
||||
* 编辑
|
||||
*
|
||||
* @param mixed $ids
|
||||
* @return string
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() == 'POST') {
|
||||
$params = $request->post();
|
||||
if (empty($params)) {
|
||||
return $this->fail(__('Parameter %name% can not be empty', []));
|
||||
}
|
||||
$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;
|
||||
$this->model->validateFailException()->validate($validate);
|
||||
}
|
||||
$result = $this->model->exists(true)->save($params);
|
||||
Db::commit();
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
return $this->fail($e->getMessage());
|
||||
}
|
||||
if ($result === false) {
|
||||
return $this->fail(__('No rows were inserted'));
|
||||
}
|
||||
return $this->success();
|
||||
//return parent::update($request);
|
||||
}
|
||||
$ids = $request->get('ids');
|
||||
$row = $this->model->whereIn('id',$ids)->find();
|
||||
if (!$row) {
|
||||
return $this->fail(__('No Results were found'));
|
||||
}
|
||||
$addon = Db::name('content')->where('id', $row['id'])->find();
|
||||
if ($addon) {
|
||||
$row->setAddonData($addon);
|
||||
}
|
||||
|
||||
return view($this->tpl.'/update',[
|
||||
'row' => $row
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁
|
||||
* @param string $ids
|
||||
*/
|
||||
public function delete(Request $request):Response
|
||||
{
|
||||
$ids = $request->get('ids');
|
||||
\app\model\Archives::event('after_delete', function ($row) {
|
||||
//删除副表
|
||||
Db::name('content')->where("id", $row['id'])->delete();
|
||||
});
|
||||
return parent::delete($request);
|
||||
}
|
||||
/**
|
||||
* 加入标签
|
||||
* @param string $ids
|
||||
*/
|
||||
public function tags(Request $request):Response
|
||||
{
|
||||
$ids = $request->get('ids');
|
||||
if ($request->method() != 'POST') {
|
||||
return $this->fail(__("Invalid parameters"));
|
||||
}
|
||||
if ($ids) {
|
||||
$tags = $request->post('tags');
|
||||
$newTagsArr = array_filter(explode(',', $tags));
|
||||
if ($newTagsArr) {
|
||||
$pk = $this->model->getPk();
|
||||
$archivesList = $this->model->where($pk, 'in', $ids)->select();
|
||||
/**
|
||||
* @var \app\model\Archives $item
|
||||
*/
|
||||
foreach ($archivesList as $index => $item) {
|
||||
$tagsArr = explode(',', $item->tags);
|
||||
$tagsArr = array_merge($tagsArr, $newTagsArr);
|
||||
$item->save(['tags' => implode(',', array_unique(array_filter($tagsArr)))]);
|
||||
}
|
||||
return $this->success();
|
||||
} else {
|
||||
return $this->fail(__('标签数据不能为空'));
|
||||
}
|
||||
}
|
||||
return $this->fail(__('Please select at least one row'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改标志
|
||||
* @param string $ids
|
||||
*/
|
||||
public function flag(Request $request):Response
|
||||
{
|
||||
if ($request->method() != 'POST') {
|
||||
return $this->fail(__("Invalid parameters"));
|
||||
}
|
||||
$ids = $request->get('ids');
|
||||
if ($ids) {
|
||||
$type = $request->post('type');
|
||||
$flag = $request->post('flag');
|
||||
$changeFlagArr = array_filter(explode(',', $flag));
|
||||
if ($changeFlagArr) {
|
||||
$pk = $this->model->getPk();
|
||||
$archivesList = $this->model->where($pk, 'in', $ids)->select();
|
||||
/**
|
||||
* @var \app\model\Archives $item
|
||||
*/
|
||||
foreach ($archivesList as $index => $item) {
|
||||
$flagArr = explode(',', $item->flag);
|
||||
if ($type == 'add') {
|
||||
$flagArr = array_merge($flagArr, $changeFlagArr);
|
||||
} else {
|
||||
$flagArr = array_diff($flagArr, $changeFlagArr);
|
||||
}
|
||||
$item->save(['flag' => implode(',', array_unique(array_filter($flagArr)))]);
|
||||
}
|
||||
return$this->success();
|
||||
} else {
|
||||
return$this->fail(__('标志数据不能为空'));
|
||||
}
|
||||
}
|
||||
return$this->fail(__('Please select at least one row'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 检测元素是否可用
|
||||
* @internal
|
||||
*/
|
||||
public function check_element_available(Request $request):Response
|
||||
{
|
||||
$id = $request->get('id');
|
||||
$name = $request->get('name');
|
||||
$value = $request->get('value');
|
||||
$name = substr($name, 4, -1);
|
||||
if (!$name) {
|
||||
return $this->fail(__('Parameter %name% can not be empty', ['name'=>'name']));
|
||||
}
|
||||
$model = $this->model;
|
||||
if ($id) {
|
||||
$model = $model->where('id', '<>', $id);
|
||||
}
|
||||
$exist = $model->where($name, $value)->find();
|
||||
if ($exist) {
|
||||
return $this->fail(__('The data already exist'));
|
||||
} else {
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索建议
|
||||
* @internal
|
||||
*/
|
||||
public function suggestion(Request $request) :Response
|
||||
{
|
||||
$q = trim($request->get("q"));
|
||||
$id = trim($request->get("id/d"));
|
||||
$list = [];
|
||||
$result = $this->model->where("title", "like", "%{$q}%")->where('id', '<>', $id)->limit(10)->order("id", "desc")->select();
|
||||
/**
|
||||
* @var \app\model\Archives $item
|
||||
*/
|
||||
foreach ($result as $index => $item) {
|
||||
$item['image'] = $item['image'] ? $item['image'] : '/assets/img/noimage.png';
|
||||
$list[] = ['id' => $item['id'], 'image' => cdnurl($item['image']), 'title' => $item['title'], 'create_date' => date('Y-m-d H:i:s',$item->created_at), 'status' => $item['status'], 'status_text' => $item['status_text'], 'deletetime' => $item['deletetime']];
|
||||
}
|
||||
return json($list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use support\think\Db;
|
||||
|
||||
/**
|
||||
* 内容管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
*/
|
||||
class ArticleController extends ArchivesController
|
||||
{
|
||||
protected $type = 'article';
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use support\think\Db;
|
||||
use Shopwwi\WebmanFilesystem\Facade\Storage;
|
||||
|
||||
/**
|
||||
* 附件管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
*/
|
||||
class AttachmentController extends Crud
|
||||
{
|
||||
function list(Request $request)
|
||||
{
|
||||
|
||||
return view('', [
|
||||
|
||||
]);
|
||||
}
|
||||
function feupload(Request $request): Response
|
||||
{
|
||||
|
||||
$file = current($request->file());
|
||||
if (!$file || !$file->isValid()) {
|
||||
return $this->fail('未找到文件');
|
||||
}
|
||||
$data = $this->base($request, '/upload/files/' . date('Ymd'));
|
||||
return json([
|
||||
'link' => $data['url'],
|
||||
]);
|
||||
}
|
||||
function upload(Request $request): Response
|
||||
{
|
||||
|
||||
$file = current($request->file());
|
||||
if (!$file || !$file->isValid()) {
|
||||
return $this->fail('未找到文件');
|
||||
}
|
||||
$data = $this->base($request, '/upload/files/' . date('Ymd'));
|
||||
//cp($data);
|
||||
return $this->json(0, '上传成功', [
|
||||
'url' => $data['realpath'],
|
||||
'name' => $data['name'],
|
||||
'fullurl' => $data['url'],
|
||||
'size' => $data['size'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传数据
|
||||
* @param Request $request
|
||||
* @param $relative_dir
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function base(Request $request, $relative_dir): array
|
||||
{
|
||||
// 适配器 local默认是存储在runtime目录下 public默认是存储在public目录下
|
||||
// 可访问的静态文件建议public
|
||||
// 默认适配器是local
|
||||
//Storage::adapter('public');
|
||||
$relative_dir = ltrim($relative_dir, '\\/');
|
||||
$file = current($request->file());
|
||||
try {
|
||||
if (!$file || !$file->isValid()) {
|
||||
throw new \support\exception\BusinessException('未找到上传文件', 400);
|
||||
}
|
||||
|
||||
$ext = $file->getUploadExtension() ?: null;
|
||||
$mime_type = $file->getUploadMimeType();
|
||||
$file_name = $file->getUploadName();
|
||||
$file_size = $file->getSize();
|
||||
|
||||
if (!$ext && $file_name === 'blob') {
|
||||
[$___image, $ext] = explode('/', $mime_type);
|
||||
unset($___image);
|
||||
}
|
||||
|
||||
$ext = strtolower($ext);
|
||||
$ext_forbidden_map = ['php', 'php3', 'php5', 'css', 'js', 'html', 'htm', 'asp', 'jsp'];
|
||||
if (in_array($ext, $ext_forbidden_map)) {
|
||||
throw new \support\exception\BusinessException('不支持该格式的文件上传', 400);
|
||||
}
|
||||
$mimetype = explode(',',Config('site.mimetype'));
|
||||
$result = Storage::adapter('public')
|
||||
->path($relative_dir)
|
||||
->size(1024 * 1024 * 5)
|
||||
->extYes($mimetype)
|
||||
//->extNo(['image/png'])
|
||||
->upload($file);
|
||||
} catch (\Exception $e) {
|
||||
return [
|
||||
'code' => 1,
|
||||
'msg' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
// cp($result);
|
||||
// stdClass Object
|
||||
// (
|
||||
// [adapter] => public
|
||||
// [origin_name] => OIP-C (1).jpg
|
||||
// [file_name] => upload/files/20250527/eb14c1bfe6e7a22415bbbb30dfe90ba1_6834f0974db76.jpg
|
||||
// [storage_key] => eb14c1bfe6e7a22415bbbb30dfe90ba1_6834f0974db76
|
||||
// [file_url] => //luru.oss-ap-southeast-1.aliyuncs.com/upload/files/20250527/eb14c1bfe6e7a22415bbbb30dfe90ba1_6834f0974db76.jpg
|
||||
// [size] => 15370
|
||||
// [mime_type] => image/jpeg
|
||||
// [extension] => jpg
|
||||
// [file_height] => 474
|
||||
// [file_width] => 474
|
||||
// )
|
||||
return [
|
||||
'code' => 0,
|
||||
'url' => $result->file_url,
|
||||
'name' => $result->origin_name,
|
||||
'realpath' => '/'.$result->file_name,
|
||||
'size' => $result->size,
|
||||
'mime_type' => $result->mime_type,
|
||||
'image_with' => $result->file_width,
|
||||
'image_height' => $result->file_height,
|
||||
'ext' => $result->extension,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use app\model\BalanceLog;
|
||||
use support\exception\BusinessException;
|
||||
use Webman\Http\Request;
|
||||
use support\Response;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 用户管理
|
||||
*/
|
||||
class BalanceLogController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var BalanceLog
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new BalanceLog;
|
||||
}
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
return view('balance_log/index');
|
||||
}
|
||||
protected function formatNormal($items, $total): Response
|
||||
{
|
||||
$formattedItems = array_map(function ($item) {
|
||||
if(!isset($item['user']))
|
||||
{
|
||||
$item['user'] = \app\model\User::where('id',$item->user_id)->find();
|
||||
}
|
||||
if (isset($item['user']['username'])) {
|
||||
$item['username'] = $item['user']['username'];
|
||||
} else {
|
||||
$item['username'] = '';
|
||||
}
|
||||
unset($item['user']); // 移除user数组,因为我们已经提取了username
|
||||
return $item;
|
||||
}, $items);
|
||||
//echo gettype($items);
|
||||
// $formattedItems = $items->each(function ($item) {
|
||||
// $item->username = $item->user->username ?? '';
|
||||
// });
|
||||
return json(['code' => 0, 'msg' => 'ok', 'count' => $total, 'data' => $items]);
|
||||
}
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
if(!isset($where['currency'])){
|
||||
return $this->success('',[]);
|
||||
}
|
||||
$currency = $where['currency']['value1'];
|
||||
unset($where['currency']);
|
||||
$this->model = $this->model->setSuffix('_'.$currency);
|
||||
if(isset($where['username'])){
|
||||
$User = new \app\model\User();
|
||||
$User = $this->parseOneWhere($User,'username',$where['username']);
|
||||
$ids = $User->column('id');
|
||||
$where['user_id'] = [
|
||||
'symbol' => 'in',
|
||||
'value1' => $ids
|
||||
];
|
||||
unset($where['username']);
|
||||
}
|
||||
if(isset($where['created_at'])){
|
||||
if(isset($where['created_at']['value1']) && $where['created_at']['value1']){
|
||||
$where['created_at']['value1']= strtotime($where['created_at']['value1']).'';
|
||||
}
|
||||
if(isset($where['created_at']['value2']) && $where['created_at']['value2']){
|
||||
$where['created_at']['value2']= strtotime($where['created_at']['value2']).'';
|
||||
}
|
||||
}
|
||||
if(isset($where['type'])){
|
||||
$where['type']['value1']= intval($where['type']['value1']);
|
||||
}
|
||||
if(isset($where['amount'])){
|
||||
$where['amount']['value1']= floatval($where['amount']['value1']);
|
||||
if(isset($where['amount']['value2'])){
|
||||
$where['amount']['value2']= floatval($where['amount']['value2']);
|
||||
}
|
||||
}
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
return $this->doFormat($query, $format, $limit);
|
||||
}
|
||||
public function __before_select__(Request $request){
|
||||
$this->assignconfig('balanceTypeList',\app\enum\BalanceType::toArray());
|
||||
}
|
||||
public function __before_index__(Request $request){
|
||||
$this->__before_select__($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行删除
|
||||
* @param array $ids
|
||||
* @return void
|
||||
*/
|
||||
protected function doDelete(array $ids)
|
||||
{
|
||||
if (!$ids) {
|
||||
return;
|
||||
}
|
||||
$primary_key = $this->model->getPk();
|
||||
$currency = Input('currency');
|
||||
$this->model->setSuffix('_'.strtolower($currency))->whereIn($primary_key, $ids)->delete();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use think\Model;
|
||||
use support\Response;
|
||||
use support\Request;
|
||||
/**
|
||||
* 基础控制器
|
||||
*/
|
||||
class Base
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array 前置操作方法列表
|
||||
*/
|
||||
protected $beforeActionList = [];
|
||||
/**
|
||||
* @var array 后置操作方法列表
|
||||
*/
|
||||
protected $afterActionList = [];
|
||||
/**
|
||||
* @var Model
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 无需登录及鉴权的方法
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedLogin = [];
|
||||
|
||||
/**
|
||||
* 需要登录无需鉴权的方法
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedAuth = [];
|
||||
|
||||
/**
|
||||
* 数据限制
|
||||
* null 不做限制,任何管理员都可以查看该表的所有数据
|
||||
* auth 管理员能看到自己以及自己的子管理员插入的数据
|
||||
* personal 管理员只能看到自己插入的数据
|
||||
* @var string
|
||||
*/
|
||||
protected $dataLimit = null;
|
||||
|
||||
/**
|
||||
* 数据限制字段
|
||||
*/
|
||||
protected $dataLimitField = 'admin_id';
|
||||
/**
|
||||
* 返回格式化json数据
|
||||
*
|
||||
* @param int $code
|
||||
* @param string $msg
|
||||
* @param array $data
|
||||
* @return Response
|
||||
*/
|
||||
protected function json(int $code, string $msg = 'ok', array|object $data = []): Response
|
||||
{
|
||||
return json(['code' => $code, 'data' => $data, 'msg' => $msg]);
|
||||
}
|
||||
|
||||
protected function success(string $msg = '成功', array|object $data = []): Response
|
||||
{
|
||||
return $this->json(0, $msg, $data);
|
||||
}
|
||||
|
||||
protected function fail(string $msg = '失败', array|object $data = []): Response
|
||||
{
|
||||
return $this->json(1,$msg, $data);
|
||||
}
|
||||
|
||||
protected function assign($name, $value = null)
|
||||
{
|
||||
$request = \request();
|
||||
$request->_view_vars = array_merge((array) $request->_view_vars, is_array($name) ? $name : [$name => $value]);
|
||||
}
|
||||
protected function assignconfig($name, $value = null)
|
||||
{
|
||||
$request = \request();
|
||||
$vars = $request->_view_vars;
|
||||
$vars['config'] = array_merge((array) $vars['config'], is_array($name) ? $name : [$name => $value]);
|
||||
$this->assign('config',$vars['config']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 卡密
|
||||
*/
|
||||
class CardController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \plugin\admin\app\model\Card
|
||||
*/
|
||||
protected $model = null;
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new \plugin\admin\app\model\Card();
|
||||
}
|
||||
function clearCache(){
|
||||
//cache('InfomercialIds',null);
|
||||
}
|
||||
function detail(){
|
||||
return view('',[
|
||||
|
||||
]);
|
||||
}
|
||||
function export(){
|
||||
$base_dir = rtrim(config('app.public_path', ''), '\\/');
|
||||
$base_dir = $base_dir ? $base_dir . DIRECTORY_SEPARATOR : base_path() . "/public/";
|
||||
$base_name = \app\model\Card::where('id',Input('ids'))->value('title').".xlsx";
|
||||
//return $base_name;
|
||||
$savePath = $base_dir.$base_name;
|
||||
var_dump($savePath);
|
||||
$list = \app\model\Cdkey::where('category_id',Input('ids'))->where('is_used',0)->where('status',1)->order('id asc')->field('account,expires')->select();
|
||||
$list = collect($list);
|
||||
|
||||
(new \Rap2hpoutre\FastExcel\FastExcel($list))->export($savePath);
|
||||
|
||||
return response()->download($savePath, $base_name);
|
||||
}
|
||||
|
||||
public function __after_insert__(Request $request){
|
||||
//cache('InfomercialIds',null);
|
||||
}
|
||||
public function __after_delete__(Request $request){
|
||||
$this->__after_insert__($request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\controller\Base;
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use plugin\admin\app\model\Category;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 用户管理
|
||||
*/
|
||||
class CategoryController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Category
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new Category;
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
return view('category/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
return parent::insert($request);
|
||||
}
|
||||
return view('category/update');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
return parent::update($request);
|
||||
}
|
||||
$ids = Request()->get('ids');
|
||||
return view('category/update',[
|
||||
'row' => $this->model->where('id',$ids)->find()
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 卡密
|
||||
*/
|
||||
class CdkeyController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \app\model\Cdkey
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new \app\model\Cdkey();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\model\Config as ConfigModel;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Exception;
|
||||
use support\think\Db;
|
||||
|
||||
/**
|
||||
* 系统设置
|
||||
*/
|
||||
class ConfigController extends Base
|
||||
{
|
||||
/**
|
||||
* 不需要验证权限的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['get','check','testmail'];
|
||||
|
||||
/**
|
||||
* 账户设置
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
|
||||
$_list = ConfigModel::order('id asc')->select();
|
||||
$_list = $_list->toArray();
|
||||
$list = [];
|
||||
foreach($_list as $k=>$v){
|
||||
if(!isset($list[$v['group']])){
|
||||
$list[$v['group']] = [];
|
||||
}
|
||||
// if(in_array($v['type'] ,['radio','checkbox','select','selects'] ) && $v['content']){
|
||||
// $firstLetter = substr($v['content'],0,1);
|
||||
// if($firstLetter == '[' || $firstLetter == '{'){
|
||||
// $arr = json_decode($v['content'],true);
|
||||
// }else{
|
||||
// $_content = explode("\n",$v['content']);
|
||||
// $arr = [];
|
||||
// foreach($_content as $k1=>$v1){
|
||||
// $_v1 = explode('|',$v1);
|
||||
// $arr[$_v1[0]]=$_v1[1];
|
||||
// }
|
||||
// }
|
||||
// $v['content'] = $arr;
|
||||
// }
|
||||
$list[$v['group']][$v['name']] = $v;
|
||||
}
|
||||
//echo json_encode($list,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
return view('config/index',[
|
||||
'configlist' => $list,
|
||||
'typeList' => [
|
||||
'string' => '字符串',
|
||||
'password' => __('密码'),
|
||||
'text' => __('文本'),
|
||||
'editor' => __('编辑器'),
|
||||
'number' => __('数字'),
|
||||
'date' => __('日期'),
|
||||
'time' => __('时间'),
|
||||
'datetime' => __('日期时间'),
|
||||
'datetimerange' => __('日期时间区间'),
|
||||
'select' => __('下拉列表'),
|
||||
'selects' => __('多选下拉列表'),
|
||||
'image' => __('图片'),
|
||||
'images' => __('图片(多)'),
|
||||
'file' => __('文件'),
|
||||
'files' => __('文件(多)'),
|
||||
'switch' => __('开关'),
|
||||
'checkbox' => __('复选'),
|
||||
'radio' => __('单选'),
|
||||
'city' => __('城市地区'),
|
||||
'selectpage' => __('关联表'),
|
||||
'selectpages' => __('关联表(多选)'),
|
||||
'array' => __('数组'),
|
||||
'custom' => __('自定义'),
|
||||
],
|
||||
'ruleList' => [
|
||||
'required' => '必选',
|
||||
'digits' => '数字',
|
||||
'letters' => '字母',
|
||||
'date' => '日期',
|
||||
'time' => '时间',
|
||||
'email' => '邮箱',
|
||||
'url' => '网址',
|
||||
'qq' => 'QQ号',
|
||||
'IDcard' => '身份证',
|
||||
'tel' => '座机电话',
|
||||
'mobile' => '手机号',
|
||||
'zipcode' => '邮编',
|
||||
'chinese' => '中文',
|
||||
'username' => '用户名',
|
||||
'password' => '密码'
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
$list = ConfigModel::where(1,1)->select();
|
||||
$post = $request->post();
|
||||
$saveData = [];
|
||||
/**
|
||||
* @var ConfigModel $v
|
||||
*/
|
||||
foreach ($list as $k => $v) {
|
||||
if(isset($post[$v['name']]) ){
|
||||
$value = $post[$v['name']];
|
||||
// if($v['type'] == 'selects'){
|
||||
// $value = implode(',',$value);
|
||||
// }
|
||||
$saveData[] = [
|
||||
"id"=>$v['id'],
|
||||
"type"=>$v['type'],
|
||||
"name"=>$v['name'],
|
||||
'value'=> $value
|
||||
];
|
||||
}
|
||||
}
|
||||
$Config = new ConfigModel;
|
||||
$Config->saveAll($saveData);
|
||||
$this->buildcache();
|
||||
return $this->success('保存成功');
|
||||
}
|
||||
/**
|
||||
* 更改
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
$post = $request->post('row');
|
||||
Db::startTrans();
|
||||
try {
|
||||
if($post['type'] == 'selects'){
|
||||
$post['value'] = implode(',',$post['value']);
|
||||
}
|
||||
$user = ConfigModel::create($post);
|
||||
Db::commit();
|
||||
$this->buildcache();
|
||||
return $this->success('保存成功');
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
return $this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
protected function buildcache(){
|
||||
//缓存
|
||||
$_list = ConfigModel::Field('name,value,type')->order('id asc')->select();
|
||||
$_list = $_list->toArray();
|
||||
$list = [];
|
||||
foreach($_list as $k=>$v){
|
||||
if(in_array($v['type'] ,['array']) && !is_array($v['value'])){
|
||||
$v['value'] = json_decode($v['value'], true);
|
||||
}
|
||||
if(in_array($v['type'] ,['selects']) && !is_array($v['value'])){
|
||||
$v['value'] = explode(',',$v['value']);
|
||||
}
|
||||
$list[$v['name']] = $v['value'];
|
||||
}
|
||||
|
||||
$fn = base_path(false) . '/config/site.php';
|
||||
file_put_contents($fn, "<?php \n return ".var_export($list,true).';');
|
||||
}
|
||||
|
||||
/**
|
||||
* 颜色检查
|
||||
* @param string $color
|
||||
* @return string
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function filterColor(string $color): string
|
||||
{
|
||||
if (!preg_match('/\#[a-zA-Z]6/', $color)) {
|
||||
throw new BusinessException('参数错误');
|
||||
}
|
||||
return $color;
|
||||
}
|
||||
/**
|
||||
* 配置名称检查
|
||||
* @param string $color
|
||||
* @return string
|
||||
* @throws BusinessException
|
||||
*/
|
||||
|
||||
function check(){
|
||||
$param = Request()->post('param');
|
||||
$name = Request()->get('name');
|
||||
if(ConfigModel::where($name, $param)->count('id')>0){
|
||||
return $this->fail("已被占用");
|
||||
}else{
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 测试邮件
|
||||
* @return Response
|
||||
*/
|
||||
function testmail(){
|
||||
$config = Request()->post();
|
||||
addJob([
|
||||
'email' => $config['test_mail_address'],
|
||||
'title' => '这是一封来自'.config('site.name').'的邮件',
|
||||
'body' => '这是一封来自'.config('site.name').'的校验邮件,用于校验邮件配置是否正常!',
|
||||
'config' => [
|
||||
"mail_from"=>$config['mail_from'],
|
||||
"mail_smtp_host"=>$config['mail_smtp_host'],
|
||||
"mail_smtp_user"=>$config['mail_smtp_user'],
|
||||
"mail_smtp_pass"=>$config['mail_smtp_pass'],
|
||||
"mail_smtp_port"=>$config['mail_smtp_port'],
|
||||
"mail_verify_type"=>$config['mail_verify_type'],
|
||||
]
|
||||
],'Email');
|
||||
return $this->success($config['test_mail_address']."邮件已经发送");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,595 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\common\Auth;
|
||||
use plugin\admin\app\common\Tree;
|
||||
use plugin\admin\app\common\Util;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use support\think\Model;
|
||||
use support\think\Db;
|
||||
|
||||
class Crud extends Base
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Model
|
||||
*/
|
||||
protected $model = null;
|
||||
/**
|
||||
* 是否是关联查询
|
||||
*/
|
||||
protected $relationSearch = false;
|
||||
/**
|
||||
* 是否开启Validate验证
|
||||
*/
|
||||
protected $modelValidate = false;
|
||||
|
||||
/**
|
||||
* 是否开启模型场景验证
|
||||
*/
|
||||
protected $modelSceneValidate = false;
|
||||
/**
|
||||
* Multi方法可批量修改的字段
|
||||
*/
|
||||
protected $multiFields = 'status';
|
||||
/**
|
||||
* Selectpage可显示的字段
|
||||
*/
|
||||
protected $selectpageFields = '*';
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
return $this->doFormat($query, $format, $limit);
|
||||
}
|
||||
/**
|
||||
* selectpage
|
||||
* @param \support\Request $request
|
||||
*/
|
||||
public function selectpage(Request $request)
|
||||
{
|
||||
$searchValue = $request->input('searchValue');
|
||||
$searchTable = $request->input('searchTable');
|
||||
$searchKey = $request->input('searchKey');
|
||||
$orderBy = $request->input('orderBy');
|
||||
$showField = $request->input('showField');
|
||||
$keyField = $request->input('keyField');
|
||||
$keyValue = $request->input('keyValue');
|
||||
$searchField = $request->input('searchField');
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
if($searchValue){
|
||||
$query = $query->whereIn($searchKey,$searchValue);
|
||||
}
|
||||
$list = $query->field([$showField,$keyField])->paginate($limit);
|
||||
return $this->success('ok',$list);
|
||||
}
|
||||
function index(Request $request): Response
|
||||
{
|
||||
return view();
|
||||
}
|
||||
/**
|
||||
* 添加
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if($request->method() == 'POST'){
|
||||
$data = $this->insertInput($request);
|
||||
$id = $this->doInsert($data);
|
||||
$ret = $this->success('操作成功', ['id' => $id]);
|
||||
return $ret;
|
||||
}
|
||||
return view(strtolower(get_controller_name()).'/update');
|
||||
}
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if($request->method() == 'POST'){
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
$this->doUpdate($id, $data);
|
||||
$ret = $this->success('操作成功');
|
||||
return $ret;
|
||||
}
|
||||
$vo = [];
|
||||
if (!empty($this->model) && $this->relationSearch) {
|
||||
$name = $this->model->getTable();
|
||||
$aliasName = $name . '.';
|
||||
$vo = $this->model->alias($name)->withJoin($this->relationSearch)->whereIn($aliasName.'id',$request->get('ids'))->find();
|
||||
}else{
|
||||
$vo = $this->model->whereIn('id',$request->get('ids'))->find();
|
||||
}
|
||||
|
||||
return view('',[
|
||||
'row' => $vo
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$ids = $this->deleteInput($request);
|
||||
$this->doDelete($ids);
|
||||
return $this->success('删除成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询前置
|
||||
* @param Request $request
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function selectInput(Request $request): array
|
||||
{
|
||||
$field = $request->input('sort','id');
|
||||
$order = $request->input('sortOrder', 'asc');
|
||||
$format = $request->input('format', 'normal');
|
||||
$limit = (int)$request->input('limit', $format === 'tree' ? 1000 : 10);
|
||||
$limit = $limit <= 0 ? 10 : $limit;
|
||||
$order = $order === 'asc' ? 'asc' : 'desc';
|
||||
$where = $request->input('filter',[]);
|
||||
$page = (int)$request->input('page');
|
||||
$page = $page > 0 ? $page : 1;
|
||||
$allow_column = [];
|
||||
//var_dump($this->model->getConnectionName());
|
||||
//if ($this->model->getConnection()->getDriverName() == 'mongodb') {
|
||||
if ($this->model->getConnection() != 'plugin.admin.mysql') {
|
||||
} else {
|
||||
$table = $this->model->getTable();
|
||||
|
||||
$allow_column = Util::db()->select("desc `$table`");
|
||||
if (!$allow_column) {
|
||||
throw new BusinessException('表不存在');
|
||||
}
|
||||
$allow_column = array_column($allow_column, 'Field', 'Field');
|
||||
if (!in_array($field, $allow_column)) {
|
||||
$field = null;
|
||||
}
|
||||
}
|
||||
// foreach ($where as $column => $value) {
|
||||
// if (
|
||||
// $value === '' || !isset($allow_column[$column]) ||
|
||||
// is_array($value) && (empty($value) || !in_array($value[0], ['null', 'not null']) && !isset($value[1]))
|
||||
// ) {
|
||||
// unset($where[$column]);
|
||||
// }
|
||||
// }
|
||||
// 按照数据限制字段返回数据
|
||||
if (!Auth::isSuperAdmin()) {
|
||||
if ($this->dataLimit === 'personal') {
|
||||
$where[$this->dataLimitField] = ['symbol'=>'=', 'value1'=>admin_id()];
|
||||
} elseif ($this->dataLimit === 'auth') {
|
||||
$primary_key = $this->model->getPk();
|
||||
if (!Auth::isSuperAdmin() && (!isset($where[$primary_key]) || $this->dataLimitField != $primary_key)) {
|
||||
$where[$this->dataLimitField] = ['symbol'=>'in', 'value1'=>Auth::getScopeAdminIds(true)];
|
||||
}
|
||||
}
|
||||
}
|
||||
return [$where, $format, $limit, $field, $order, $page];
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定查询where条件,并没有真正的查询数据库操作
|
||||
* @param array $where
|
||||
* @param string|null $field
|
||||
* @param string $order
|
||||
* @return Model
|
||||
*/
|
||||
protected function doSelect(array $where, string $field = null, string $order = 'desc')
|
||||
{
|
||||
$model = $this->model;
|
||||
$aliasName="";
|
||||
if (!empty($this->model) && $this->relationSearch) {
|
||||
$name = $this->model->getTable();
|
||||
$aliasName = $name . '.';
|
||||
$model = $model->alias($name)->withJoin($this->relationSearch);
|
||||
$field = false===strpos($field?:'','.') ? $aliasName.$field : $field;
|
||||
}
|
||||
foreach ($where as $column => $value) {
|
||||
$model = $this->parseOneWhere($model,$column,$value,$aliasName);
|
||||
}
|
||||
if ($field) {
|
||||
$model = $model->order($field, $order);
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
protected function parseOneWhere($model,$column,$value,$aliasName=''){
|
||||
$column = false===strpos($column,'.') ? $aliasName.$column : $column;
|
||||
if (is_array($value)) {
|
||||
$symbol = isset($value['symbol']) ? $value['symbol'] : '';
|
||||
$value1 = isset($value['value1']) ? $value['value1'] : '';
|
||||
$value2 = isset($value['value2']) ? $value['value2'] : '';
|
||||
if ($symbol === 'like' || $symbol === 'not like') {
|
||||
$model = $model->where($column, $symbol, "%$value1%");
|
||||
} elseif (in_array($symbol, ['>', '=', '<', '<>','>=','<='])) {
|
||||
$model = $model->where($column, $symbol, $value1);
|
||||
} elseif (($symbol == 'in'|| $symbol == 'not in') && !empty($value1)) {
|
||||
$valArr = $value1;
|
||||
if (is_string($value1)) {
|
||||
$valArr = explode(",", trim($value1));
|
||||
}
|
||||
if($symbol == 'in'){
|
||||
$model = $model->whereIn($column, $valArr);
|
||||
}else{
|
||||
$model = $model->whereNotIn($column, $valArr);
|
||||
}
|
||||
} elseif ($symbol == 'null') {
|
||||
$model = $model->whereNull($column);
|
||||
} elseif ($symbol == 'not null') {
|
||||
$model = $model->whereNotNull($column);
|
||||
} elseif ($symbol == 'range' && $$value1 !== '' || $value2 !== '') {
|
||||
$model = $model->whereBetween($column, [$value1, $value2]);
|
||||
}
|
||||
} else {
|
||||
$model = $model->where($column, $value);
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行真正查询,并返回格式化数据
|
||||
* @param $query
|
||||
* @param $format
|
||||
* @param $limit
|
||||
* @return Response
|
||||
*/
|
||||
protected function doFormat($query, $format, $limit,$fields="*"): Response
|
||||
{
|
||||
$methods = [
|
||||
'select' => 'formatSelect',
|
||||
'tree' => 'formatTree',
|
||||
'table_tree' => 'formatTableTree',
|
||||
'normal' => 'formatNormal',
|
||||
];
|
||||
if($this->relationSearch){
|
||||
$fields="";
|
||||
}
|
||||
if($limit == 'all'){
|
||||
$paginator = $query->field($fields)->select();
|
||||
$total = count($paginator);
|
||||
$items = $paginator;
|
||||
}else{
|
||||
//var_dump($query->field($fields)->buildSql());
|
||||
$paginator = $query->field($fields)->paginate($limit);
|
||||
$total = $paginator->total();
|
||||
$items = $paginator->items();
|
||||
}
|
||||
//var_dump($query->getlastsql());
|
||||
if (method_exists($this, "afterQuery")) {
|
||||
$items = call_user_func([$this, "afterQuery"], $items);
|
||||
}
|
||||
$format_function = $methods[$format] ?? 'formatNormal';
|
||||
return call_user_func([$this, $format_function], $items, $total);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入前置方法
|
||||
* @param Request $request
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function insertInput(Request $request): array
|
||||
{
|
||||
$data = $this->inputFilter($request->post());
|
||||
$password_filed = 'password';
|
||||
if (isset($data[$password_filed])) {
|
||||
$data[$password_filed] = Util::passwordHash($data[$password_filed]);
|
||||
}
|
||||
$password_filed = 'trade_password';
|
||||
if (isset($data[$password_filed])) {
|
||||
$data[$password_filed] = Util::passwordHash($data[$password_filed]);
|
||||
}
|
||||
|
||||
if (!Auth::isSuperAdmin()) {
|
||||
if ($this->dataLimit === 'personal') {
|
||||
$data[$this->dataLimitField] = admin_id();
|
||||
} elseif ($this->dataLimit === 'auth') {
|
||||
if (!empty($data[$this->dataLimitField])) {
|
||||
$admin_id = $data[$this->dataLimitField];
|
||||
if (!in_array($admin_id, Auth::getScopeAdminIds(true))) {
|
||||
throw new BusinessException('无数据权限');
|
||||
}
|
||||
} else {
|
||||
$data[$this->dataLimitField] = admin_id();
|
||||
}
|
||||
}
|
||||
} elseif ($this->dataLimit && empty($data[$this->dataLimitField])) {
|
||||
$data[$this->dataLimitField] = admin_id();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行插入
|
||||
* @param array $data
|
||||
* @return mixed|null
|
||||
*/
|
||||
protected function doInsert(array $data)
|
||||
{
|
||||
$primary_key = $this->model->getPk();
|
||||
$model_class = get_class($this->model);
|
||||
// $model = new $model_class;
|
||||
// foreach ($data as $key => $val) {
|
||||
// $model->{$key} = $val;
|
||||
// }
|
||||
// $model->save();
|
||||
|
||||
$model = $model_class::create($data);
|
||||
return $primary_key ? $model->$primary_key : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新前置方法
|
||||
* @param Request $request
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function updateInput(Request $request): array
|
||||
{
|
||||
$primary_key = $this->model->getPk();
|
||||
$id = $request->post($primary_key);
|
||||
$data = $this->inputFilter($request->post());
|
||||
$model = $this->model->find($id);
|
||||
if (!$model) {
|
||||
throw new BusinessException('记录不存在', 2);
|
||||
}
|
||||
|
||||
if (!Auth::isSuperAdmin()) {
|
||||
if ($this->dataLimit == 'personal') {
|
||||
if ($model->{$this->dataLimitField} != admin_id()) {
|
||||
throw new BusinessException('无数据权限');
|
||||
}
|
||||
} elseif ($this->dataLimit == 'auth') {
|
||||
$scopeAdminIds = Auth::getScopeAdminIds(true);
|
||||
$admin_ids = [
|
||||
$data[$this->dataLimitField] ?? false, // 检查要更新的数据admin_id是否是有权限的值
|
||||
$model->{$this->dataLimitField} ?? false // 检查要更新的记录的admin_id是否有权限
|
||||
];
|
||||
foreach ($admin_ids as $admin_id) {
|
||||
if ($admin_id && !in_array($admin_id, $scopeAdminIds)) {
|
||||
throw new BusinessException('无数据权限');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$password_filed = 'password';
|
||||
if (isset($data[$password_filed])) {
|
||||
// 密码为空,则不更新密码
|
||||
if ($data[$password_filed] === '') {
|
||||
unset($data[$password_filed]);
|
||||
} else {
|
||||
$data[$password_filed] = Util::passwordHash($data[$password_filed]);
|
||||
}
|
||||
}
|
||||
$password_filed = 'trade_password';
|
||||
if (isset($data[$password_filed])) {
|
||||
// 密码为空,则不更新密码
|
||||
if ($data[$password_filed] === '') {
|
||||
unset($data[$password_filed]);
|
||||
} else {
|
||||
$data[$password_filed] = Util::passwordHash($data[$password_filed]);
|
||||
}
|
||||
}
|
||||
unset($data[$primary_key]);
|
||||
return [$id, $data];
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行更新
|
||||
* @param $id
|
||||
* @param $data
|
||||
* @return void
|
||||
*/
|
||||
protected function doUpdate($id, $data)
|
||||
{
|
||||
$model = $this->model->find($id);
|
||||
foreach ($data as $key => $val) {
|
||||
$model->{$key} = $val;
|
||||
}
|
||||
$model->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对用户输入表单过滤
|
||||
* @param array $data
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function inputFilter(array $data): array
|
||||
{
|
||||
$table = config('plugin.admin.database.connections.mysql.prefix') . $this->model->getTable();
|
||||
$allow_column = Db::getFields($this->model->getTable());
|
||||
if (!$allow_column) {
|
||||
throw new BusinessException('表不存在', 2);
|
||||
}
|
||||
//$columns = array_column($allow_column, 'Type', 'Field');
|
||||
//echo json_encode($allow_column);
|
||||
foreach ($data as $col => $item) {
|
||||
if (!isset($allow_column[$col])) {
|
||||
unset($data[$col]);
|
||||
continue;
|
||||
}
|
||||
// 非字符串类型传空则为null
|
||||
if ($item === '' && strpos(strtolower($allow_column[$col]['type']), 'varchar') === false && strpos(strtolower($allow_column[$col]['type']), 'text') === false) {
|
||||
$data[$col] = null;
|
||||
}
|
||||
$data[$col] = $item;
|
||||
// if (is_array($item)) {
|
||||
// $data[$col] = implode(',', $item);
|
||||
// }
|
||||
}
|
||||
if (empty($data['created_at'])) {
|
||||
unset($data['created_at']);
|
||||
}
|
||||
if (empty($data['updated_at'])) {
|
||||
unset($data['updated_at']);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除前置方法
|
||||
* @param Request $request
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function deleteInput(Request $request): array
|
||||
{
|
||||
$primary_key = $this->model->getPk();
|
||||
if (!$primary_key) {
|
||||
throw new BusinessException('该表无主键,不支持删除');
|
||||
}
|
||||
$ids = $request->post('ids', '');
|
||||
if(!is_array($ids)){
|
||||
$ids = explode(',',$ids);
|
||||
}
|
||||
if (!Auth::isSuperAdmin()){
|
||||
$admin_ids = [];
|
||||
if ($this->dataLimit) {
|
||||
$admin_ids = $this->model->whereIn($primary_key, $ids)->column($this->dataLimitField);
|
||||
}
|
||||
if ($this->dataLimit == 'personal') {
|
||||
if (!in_array(admin_id(), $admin_ids)) {
|
||||
throw new BusinessException('无数据权限');
|
||||
}
|
||||
} elseif ($this->dataLimit == 'auth') {
|
||||
if (array_diff($admin_ids, Auth::getScopeAdminIds(true))) {
|
||||
throw new BusinessException('无数据权限');
|
||||
}
|
||||
}
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行删除
|
||||
* @param array $ids
|
||||
* @return void
|
||||
*/
|
||||
protected function doDelete(array $ids)
|
||||
{
|
||||
if (!$ids) {
|
||||
return;
|
||||
}
|
||||
$primary_key = $this->model->getPk();
|
||||
$this->model->whereIn($primary_key, $ids)->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化树
|
||||
* @param $items
|
||||
* @return Response
|
||||
*/
|
||||
protected function formatTree($items): Response
|
||||
{
|
||||
$format_items = [];
|
||||
//$primary_key = $this->model->getPk();
|
||||
$primary_key = $this->model->getPk();
|
||||
foreach ($items as $item) {
|
||||
$item->name = $this->guessName($item) ?: $item->$primary_key;
|
||||
$item->value = (string)$item->$primary_key;
|
||||
$item->id = $item->$primary_key;
|
||||
//$item->pid = $item->pid;
|
||||
$format_items[] = $item;
|
||||
}
|
||||
$tree = new Tree($format_items);
|
||||
return $this->success('ok', $tree->getTree());
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化表格树
|
||||
* @param $items
|
||||
* @return Response
|
||||
*/
|
||||
protected function formatTableTree($items): Response
|
||||
{
|
||||
$tree = new Tree($items);
|
||||
return $this->success('ok', $tree->getTree());
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化下拉列表
|
||||
* @param $items
|
||||
* @return Response
|
||||
*/
|
||||
protected function formatSelect($items): Response
|
||||
{
|
||||
$formatted_items = [];
|
||||
$primary_key = $this->model->getPk();
|
||||
foreach ($items as $item) {
|
||||
$formatted_items[] = [
|
||||
'name' => $this->guessName($item) ?: $item->$primary_key,
|
||||
'value' => $item->$primary_key
|
||||
];
|
||||
}
|
||||
return $this->success('ok', $formatted_items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用格式化
|
||||
* @param $items
|
||||
* @param $total
|
||||
* @return Response
|
||||
*/
|
||||
protected function formatNormal($items, $total): Response
|
||||
{
|
||||
return json(['code' => 0, 'msg' => 'ok', 'count' => $total, 'data' => $items]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据库后置方法,可用于修改数据
|
||||
* @param mixed $items 原数据
|
||||
* @return mixed 修改后数据
|
||||
*/
|
||||
protected function afterQuery($items)
|
||||
{
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 猜测记录名称
|
||||
* @param $item
|
||||
* @return mixed
|
||||
*/
|
||||
protected function guessName($item)
|
||||
{
|
||||
return $item->title ?? $item->name ?? $item->nickname ?? $item->username ?? $item->id;
|
||||
}
|
||||
/**
|
||||
* 批量操作
|
||||
* @param $item
|
||||
* @return mixed
|
||||
*/
|
||||
function multi(){
|
||||
$ids = Request()->post('ids');
|
||||
$params = Request()->post('params');
|
||||
parse_str($params,$s);
|
||||
if(!is_array($ids)){
|
||||
$ids = explode(',',$ids);
|
||||
}
|
||||
$this->model->whereIn('id', $ids)->update($s);
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use support\Request;
|
||||
|
||||
class DevController extends Base
|
||||
{
|
||||
|
||||
/**
|
||||
* 无需登录的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedLogin = [''];
|
||||
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['*'];
|
||||
public function index()
|
||||
{
|
||||
if(request()->method() == 'POST'){
|
||||
return '<pre>'.var_export(Request()->post(),true).'</pre>';
|
||||
}
|
||||
return view('dev/index',[
|
||||
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use support\think\Db;
|
||||
|
||||
/**
|
||||
* 礼品卡管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
*/
|
||||
class GiftController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* Product模型对象
|
||||
* @var \app\model\Gift
|
||||
*/
|
||||
protected $model = null;
|
||||
protected $type = 'default';
|
||||
protected $noNeedAuth = [];
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new \app\model\Gift;
|
||||
$statusList = $this->model->getStatusList();
|
||||
$this->assign("statusList", $statusList);
|
||||
$this->assignconfig("statusList", $statusList);
|
||||
}
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() != 'POST') {
|
||||
return view('update',[
|
||||
'row' => [
|
||||
'price' => '10',
|
||||
'max_quantity' => 0,
|
||||
'status' => 1
|
||||
]
|
||||
]);
|
||||
}
|
||||
return parent::insert($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use support\think\Db;
|
||||
|
||||
/**
|
||||
* 礼品记录
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
*/
|
||||
class GiftOrderController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* GiftOrder模型对象
|
||||
* @var \app\model\GiftOrder
|
||||
*/
|
||||
protected $model = null;
|
||||
protected $relationSearch = ['user','gift'];
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new \app\model\GiftOrder();
|
||||
$statusList = $this->model->getStatusList();
|
||||
$this->assign("statusList", $statusList);
|
||||
$this->assignconfig("statusList", $statusList);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\common\Util;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Exception;
|
||||
use support\think\Db;
|
||||
use Workerman\Worker;
|
||||
|
||||
class IndexController extends Base
|
||||
{
|
||||
|
||||
/**
|
||||
* 无需登录的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedLogin = ['index'];
|
||||
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['dashboard','upload'];
|
||||
|
||||
/**
|
||||
* 后台主页
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Exception
|
||||
*/
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
clearstatcache();
|
||||
$admin = admin();
|
||||
if (!$admin) {
|
||||
$title = config('site.name') ?? 'admin';
|
||||
$logo = cdnurl(config('site.admin_logo') ?? '/app/admin/images/logo.png');
|
||||
return view('account/login',['logo'=>$logo,'title'=>$title]);
|
||||
}
|
||||
//缓存
|
||||
$list = (new AdminRuleController())->get($request)->rawBody();
|
||||
//return $this->success($list);
|
||||
$list = json_decode($list,true);
|
||||
$menu = $list['data'];
|
||||
return view('index/index',[
|
||||
'menu' => $menu,
|
||||
'user' => admin()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 仪表板
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function dashboard(Request $request): Response
|
||||
{
|
||||
// 今日新增充值
|
||||
//$today_user_recharge_sum = Recharge::where('status',2)->whereTime('created_at', 'today')->sum('amount');
|
||||
// 7天内新增充值
|
||||
$day7_user_recharge_sum = 0;
|
||||
for ($i=7; $i >= 0; $i--) {
|
||||
$date = date('Y-m-d',strtotime('-'.$i.' days'));
|
||||
$day7_user_recharge_sum +=cache('statistics_recharge_amount_'.$date);
|
||||
}
|
||||
//$day7_user_recharge_sum = Recharge::where('status',2)->whereTime('created_at', '-7 days')->sum('amount');
|
||||
// 总用户数
|
||||
$user_count = \app\model\User::where('status',1)->count('id');
|
||||
$recharge_total = \app\model\Recharge::where('status',\app\enum\RechargeStatus::COMPLETE->value)->sum('amount');
|
||||
// mysql版本
|
||||
$withdrawl_total = \app\model\Withdrawl::where('status',\app\enum\WithdrawlStatus::COMPLETE->value)->sum('recive_amount');
|
||||
// mysql版本
|
||||
$version = Db::query('select VERSION() as version');
|
||||
$mysql_version = $version[0]['version'] ?? 'unknown';
|
||||
|
||||
// $recharge = \app\model\Recharge::where('status',2)->field("FROM_UNIXTIME(created_at, '%Y-%m-%d') AS label, sum(amount) AS value")
|
||||
// ->limit(0,30)->group("label")
|
||||
// ->select()->toArray();
|
||||
// $withdrawl = \app\model\Withdrawl::where('status',2)->field("FROM_UNIXTIME(created_at, '%Y-%m-%d') AS label, sum(recive_amount) AS value")
|
||||
// ->limit(0,30)->group("label")
|
||||
// ->select()->toArray();
|
||||
// // if(!cache('last_jiaquan_time')){
|
||||
// cache('last_jiaquan_time',strtotime('2025-02-27 00:51:00'));
|
||||
// }
|
||||
|
||||
return view('index/dashboard', [
|
||||
'today_user_recharge_sum' => formatAmount(cache('statistics_recharge_amount_'.date('Y-m-d')),0),
|
||||
'day7_user_recharge_sum' => formatAmount($day7_user_recharge_sum,0),
|
||||
'user_count' => $user_count,
|
||||
//'recharge' => $recharge,
|
||||
//'withdrawl' => $withdrawl,
|
||||
'recharge_total' => formatAmount($recharge_total,0),
|
||||
'withdrawl_total' => formatAmount($withdrawl_total,0),
|
||||
'user_score_total' => formatAmount(\app\model\User::sum('score')),
|
||||
'user_money_total' => formatAmount(\app\model\User::sum('money')),
|
||||
|
||||
'php_version' => PHP_VERSION,
|
||||
'workerman_version' => Worker::VERSION,
|
||||
'webman_version' => Util::getPackageVersion('workerman/webman-framework'),
|
||||
'admin_version' => config('plugin.admin.app.version'),
|
||||
'mysql_version' => $mysql_version,
|
||||
'os' => PHP_OS,
|
||||
]);
|
||||
}
|
||||
function clean(){
|
||||
return $this->success('');
|
||||
}
|
||||
function role_buy_lines()
|
||||
{
|
||||
$res = [];
|
||||
for ($i=7; $i >= 0; $i--) {
|
||||
$date = date('Y-m-d',strtotime('-'.$i.' days'));
|
||||
$res[$date] = [
|
||||
'amount' => cache('role_buy_amount_total_'.$date)?:0,
|
||||
'reward' => cache('role_buy_reward_total_'.$date)?:0,
|
||||
'residual' => cache('role_buy_residual_total_'.$date)?:0,
|
||||
];
|
||||
}
|
||||
return $this->success('ok',$res);
|
||||
}
|
||||
function recharge_lines()
|
||||
{
|
||||
$res = [];
|
||||
for ($i=7; $i >= 0; $i--) {
|
||||
$date = date('Y-m-d',strtotime('-'.$i.' days'));
|
||||
$res[$date] = [
|
||||
'amount' => cache('statistics_recharge_amount_'.$date)?:0,
|
||||
];
|
||||
}
|
||||
return $this->success('ok',$res);
|
||||
}
|
||||
function withdrawl_lines()
|
||||
{
|
||||
$res = [];
|
||||
for ($i=7; $i >= 0; $i--) {
|
||||
$date = date('Y-m-d',strtotime('-'.$i.' days'));
|
||||
$res[$date] = [
|
||||
'amount' => cache('statistics_withdrawl_amount_'.$date)?:0,
|
||||
];
|
||||
}
|
||||
return $this->success('ok',$res);
|
||||
}
|
||||
function money_lines()
|
||||
{
|
||||
$res = [];
|
||||
for ($i=7; $i >= 0; $i--) {
|
||||
$date = date('Y-m-d',strtotime('-'.$i.' days'));
|
||||
$res[$date] = [
|
||||
'withdrawl' => cache('statistics_withdrawl_amount_'.$date)?:0,
|
||||
'recharge' => cache('statistics_recharge_amount_'.$date)?:0,
|
||||
];
|
||||
}
|
||||
return $this->success('ok',$res);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
class InvitecodeController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* 无需登录的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedLogin = [''];
|
||||
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['*'];
|
||||
/**
|
||||
* Invitecode模型对象
|
||||
* @var \app\model\Invitecode
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new \app\model\Invitecode();
|
||||
$statusList = $this->model->getStatusList();
|
||||
$this->assign("statusList", $statusList);
|
||||
$this->assignconfig("statusList", $statusList);
|
||||
}
|
||||
public function index(Request $request):Response
|
||||
{
|
||||
return view();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use support\think\Db;
|
||||
|
||||
/**
|
||||
* 产品管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
*/
|
||||
class ProductController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* Product模型对象
|
||||
* @var \app\model\Product
|
||||
*/
|
||||
protected $model = null;
|
||||
protected $type = 'default';
|
||||
protected $noNeedAuth = [];
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new \app\model\Product;
|
||||
$statusList = $this->model->getStatusList();
|
||||
$this->assign("statusList", $statusList);
|
||||
$this->assignconfig("statusList", $statusList);
|
||||
$cycleTypeList = $this->model->getCycleTypeList();
|
||||
$this->assign("cycleTypeList", $cycleTypeList);
|
||||
$this->assignconfig("cycleTypeList", $cycleTypeList);
|
||||
$this->assign("CategoryOptions", $this->model->getCategoryOptions($this->type));
|
||||
}
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() != 'POST') {
|
||||
return view('update',[
|
||||
'row' => [
|
||||
'price' => '10',
|
||||
'interest_rate' => '10',
|
||||
'cycle_type' => 'day',
|
||||
'duration_cycle' => 1,
|
||||
'billing_cycle' => 1,
|
||||
'max_quantity' => 0,
|
||||
'interest_type' => 30,
|
||||
'status' => 1
|
||||
]
|
||||
]);
|
||||
}
|
||||
return parent::insert($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\controller\Base;
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use app\model\ProductOrder;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 工作室
|
||||
*/
|
||||
class ProductOrderController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ProductOrder
|
||||
*/
|
||||
protected $model = null;
|
||||
protected $relationSearch = ['user','product'];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new ProductOrder();
|
||||
$statusList = $this->model->getStatusList();
|
||||
$this->assign("statusList", $statusList);
|
||||
$this->assignconfig("statusList", $statusList);
|
||||
}
|
||||
public function team(Request $request): Response
|
||||
{
|
||||
return view();
|
||||
}
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
return view('productorder/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
return parent::insert($request);
|
||||
}
|
||||
return view('productorder/update',[
|
||||
'row' => \plugin\admin\app\model\User::findOrEmpty(0)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
$this->doUpdate($id, $data);
|
||||
$ret = $this->success('操作成功');
|
||||
return $ret;
|
||||
}
|
||||
$ids = Request()->get('ids');
|
||||
$user = $this->model->where('id',$ids)->find();
|
||||
return view('productorder/update',[
|
||||
'row' => $user
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Symfony\Component\Console\Input\Input;
|
||||
use support\think\Db;
|
||||
|
||||
/**
|
||||
* 问卷管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
*/
|
||||
class QuestionnaireController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* Product模型对象
|
||||
* @var \app\model\Questionnaire
|
||||
*/
|
||||
protected $model = null;
|
||||
protected $noNeedAuth = [];
|
||||
protected $relationSearch = ['category'];
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new \app\model\Questionnaire;
|
||||
$statusList = $this->model->getStatusList();
|
||||
$this->assign("statusList", $statusList);
|
||||
$this->assignconfig("statusList", $statusList);
|
||||
|
||||
$categoryList = $this->model->getCategoryOptions();
|
||||
$this->assign("categoryList", $categoryList);
|
||||
$this->assignconfig("categoryList", $categoryList);
|
||||
}
|
||||
/**
|
||||
* selectpage
|
||||
* @param \support\Request $request
|
||||
*/
|
||||
function selectpage(Request $request)
|
||||
{
|
||||
$searchValue = $request->input('searchValue');
|
||||
$searchTable = $request->input('searchTable');
|
||||
$searchKey = $request->input('searchKey');
|
||||
$orderBy = $request->input('orderBy');
|
||||
$showField = $request->input('showField');
|
||||
$keyField = $request->input('keyField');
|
||||
$keyValue = $request->input('keyValue');
|
||||
$searchField = $request->input('searchField');
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
$ids = \app\model\Product::distinct(true)->column('questionnaire_id');
|
||||
if($keyValue && $keyField){
|
||||
$query = $query->whereIn($keyField,$keyValue);
|
||||
$ids =array_diff([$keyValue?:0],$ids);
|
||||
}
|
||||
if($showField && input($showField)){
|
||||
$query = $query->whereLike($showField,'%'.input($showField).'%');
|
||||
}
|
||||
//log_alert($ids,'cansnow');
|
||||
$query = $query->whereNotIn('id',$ids);
|
||||
$list = $query->field([$showField,$keyField])->paginate($limit);
|
||||
//log_alert($query->getLastSql(),'cansnow');
|
||||
|
||||
return $this->success('ok',$list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\controller\Crud;
|
||||
|
||||
/**
|
||||
* 用户充值
|
||||
*/
|
||||
class RechargeController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \app\model\Recharge
|
||||
*/
|
||||
protected $model = null;
|
||||
protected $relationSearch = ['user'];
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new \app\model\Recharge();
|
||||
$statusList = $this->model->getStatusList();
|
||||
$this->assign("statusList", $statusList);
|
||||
$this->assignconfig("statusList", $statusList);
|
||||
$this->assign("networkList", $this->model->getNetworkList());
|
||||
$this->assignconfig("networkList", $this->model->getNetworkList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use support\think\Db;
|
||||
|
||||
/**
|
||||
* 单页管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
*/
|
||||
class SinglePageController extends ArchivesController
|
||||
{
|
||||
protected $type = 'page';
|
||||
protected $tpl = 'singlepage';
|
||||
protected $relationSearch = [];
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use plugin\admin\app\model\User as UserModel;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 工作室
|
||||
*/
|
||||
class StudioController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var UserModel
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new UserModel();
|
||||
$groupList = [
|
||||
['value'=>0,'label'=>"普通用户"],
|
||||
['value'=>1,'label'=>"工作室"],
|
||||
];
|
||||
$roleList = \app\model\UserRole::order('id','desc')->column('name as label,id as value');
|
||||
$this->assign('groupList',$groupList);
|
||||
$this->assignconfig('groupList',$groupList);
|
||||
$this->assign('roleList',$roleList);
|
||||
$this->assignconfig('roleList',$roleList);
|
||||
}
|
||||
public function team(Request $request): Response
|
||||
{
|
||||
return view();
|
||||
}
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
$this->model = $this->model->with(['referrer','role'])->where('wa_user.group',1);
|
||||
return parent::select($request);
|
||||
}
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
return view('user/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
return parent::insert($request);
|
||||
}
|
||||
return view('user/update',[
|
||||
'row' => UserModel::findOrEmpty(0)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
$this->doUpdate($id, $data);
|
||||
$ret = $this->success('操作成功');
|
||||
return $ret;
|
||||
}
|
||||
$ids = Request()->get('ids');
|
||||
$user = $this->model->where('id',$ids)->find();
|
||||
return view('user/update',[
|
||||
'row' => $user
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use plugin\admin\app\model\User;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 用户管理
|
||||
*/
|
||||
class TeamController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new User();
|
||||
}
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
//log_alert($where);
|
||||
$user_id = 0;
|
||||
if($where['user_id']['value1']){
|
||||
$user_id = $where['user_id']['value1'];
|
||||
}
|
||||
if($where['type']['value1'] == 'child'){
|
||||
$list = \app\model\UserTeam::alias('ut')
|
||||
->join('user u', 'ut.descendant_id = u.id')
|
||||
->where('ut.ancestor_id', $user_id)
|
||||
->field('ut.depth,u.*')
|
||||
->order('ut.depth asc')
|
||||
->paginate(10);
|
||||
}
|
||||
if($where['type']['value1'] == 'tree'){
|
||||
$list = \app\model\UserTeam::alias('ut')
|
||||
->join('user u', 'ut.ancestor_id = u.id')
|
||||
->where('ut.descendant_id', $user_id)
|
||||
->field('ut.depth,u.*')
|
||||
->order('ut.depth asc')
|
||||
->paginate(10);
|
||||
}
|
||||
$total = $list->total();
|
||||
$items = $list->items();
|
||||
return $this->formatNormal($items,$total);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\controller\Base;
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use plugin\admin\app\model\User as UserModel;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 用户管理
|
||||
*/
|
||||
class UserController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var UserModel
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new UserModel();
|
||||
$groupList = [
|
||||
['value'=>0,'label'=>"普通用户"],
|
||||
['value'=>1,'label'=>"内部用户"],
|
||||
['value'=>2,'label'=>"联盟商"],
|
||||
];
|
||||
$roleList = \app\model\UserRole::order('id','desc')->column('name as label,id as value');
|
||||
$this->assign('groupList',$groupList);
|
||||
$this->assignconfig('groupList',$groupList);
|
||||
$this->assign('roleList',$roleList);
|
||||
$this->assignconfig('roleList',$roleList);
|
||||
}
|
||||
public function team(Request $request): Response
|
||||
{
|
||||
return view();
|
||||
}
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
$this->model = $this->model->with(['referrer','role']);
|
||||
return parent::select($request);
|
||||
}
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
return view('user/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
return parent::insert($request);
|
||||
}
|
||||
return view('user/update',[
|
||||
'row' => UserModel::findOrEmpty(0)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
$this->doUpdate($id, $data);
|
||||
$ret = $this->success('操作成功');
|
||||
return $ret;
|
||||
}
|
||||
$ids = Request()->get('ids');
|
||||
$user = $this->model->where('id',$ids)->find();
|
||||
return view('user/update',[
|
||||
'row' => $user
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\common\Auth;
|
||||
use plugin\admin\app\common\Tree;
|
||||
use app\model\UserRole as UserRoleModel;
|
||||
use app\model\UserRule as UserRuleModel;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 角色管理
|
||||
*/
|
||||
class UserRoleController extends Crud
|
||||
{
|
||||
/**
|
||||
* 不需要鉴权的方法
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedAuth = ['select'];
|
||||
|
||||
/**
|
||||
* @var UserRoleModel
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new UserRoleModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
return view('user_role/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
$limit = 100000;
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
return $this->doFormat($query, $format, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
$data = $this->insertInput($request);
|
||||
$pid = $data['pid'] ?? null;
|
||||
// if (!$pid) {
|
||||
// return $this->fail('请选择父级角色组');
|
||||
// }
|
||||
if($pid){
|
||||
if (!Auth::isSuperAdmin() && !in_array($pid, Auth::getScopeRoleIds(true))) {
|
||||
return $this->fail('父级角色组超出权限范围');
|
||||
}
|
||||
}
|
||||
$this->checkRules($pid, $data['rules'] ?? '');
|
||||
|
||||
$id = $this->doInsert($data);
|
||||
return $this->success("操作成功", ['id' => $id]);
|
||||
}
|
||||
return view('user_role/update',[
|
||||
'rolelist'=> $this->model->select()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'GET') {
|
||||
return view('user_role/update',[
|
||||
'rolelist'=> $this->model->select(),
|
||||
'row' => $this->model->whereIn('id',Request()->get('ids'))->findOrEmpty()
|
||||
|
||||
]);
|
||||
}
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
$is_supper_admin = Auth::isSuperAdmin();
|
||||
$descendant_role_ids = Auth::getScopeRoleIds();
|
||||
$role = UserRoleModel::find($id);
|
||||
if (!$role) {
|
||||
return $this->fail('数据不存在');
|
||||
}
|
||||
|
||||
if (key_exists('pid', $data) && $data['pid']) {
|
||||
$pid = $data['pid'];
|
||||
// if (!$pid) {
|
||||
// return $this->fail('请选择父级角色组');
|
||||
// }
|
||||
if ($pid == $id) {
|
||||
return $this->fail('父级不能是自己');
|
||||
}
|
||||
if (!$is_supper_admin && !in_array($pid, Auth::getScopeRoleIds(true))) {
|
||||
return $this->fail('父级超出权限范围');
|
||||
}
|
||||
} else {
|
||||
$pid = $role->pid;
|
||||
}
|
||||
$this->checkRules($pid, $data['rules'] ?? '');
|
||||
$this->doUpdate($id, $data);
|
||||
|
||||
// 删除所有子角色组中已经不存在的权限
|
||||
$tree = new Tree(UserRoleModel::field(['id', 'pid'])->select());
|
||||
$descendant_roles = $tree->getDescendant([$id]);
|
||||
$descendant_role_ids = array_column($descendant_roles, 'id');
|
||||
$rule_ids = $data['rules'] ? explode(',', $data['rules']) : [];
|
||||
foreach ($descendant_role_ids as $role_id) {
|
||||
$tmp_role = UserRoleModel::find($role_id);
|
||||
$tmp_rule_ids = $role->getRuleIds();
|
||||
$tmp_rule_ids = array_intersect(explode(',',$tmp_role->rules), $tmp_rule_ids);
|
||||
$tmp_role->rules = implode(',', $tmp_rule_ids);
|
||||
$tmp_role->save();
|
||||
}
|
||||
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$ids = $this->deleteInput($request);
|
||||
if (in_array(1, $ids)) {
|
||||
return $this->fail('无法删除超级管理员角色');
|
||||
}
|
||||
if (!Auth::isSuperAdmin() && array_diff($ids, Auth::getScopeRoleIds())) {
|
||||
return $this->fail('无删除权限');
|
||||
}
|
||||
$tree = new Tree(UserRoleModel::select());
|
||||
$descendants = $tree->getDescendant($ids);
|
||||
if ($descendants) {
|
||||
$ids = array_merge($ids, array_column($descendants, 'id'));
|
||||
}
|
||||
$this->doDelete($ids);
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色权限
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function rules(Request $request): Response
|
||||
{
|
||||
$role_id = $request->get('id');
|
||||
if (empty($role_id)) {
|
||||
return $this->success("操作成功", []);
|
||||
}
|
||||
if (!Auth::isSuperAdmin() && !in_array($role_id, Auth::getScopeRoleIds(true))) {
|
||||
return $this->fail('角色组超出权限范围');
|
||||
}
|
||||
$rule_id_string = UserRoleModel::where('id', $role_id)->value('rules');
|
||||
if ($rule_id_string === '') {
|
||||
return $this->success("操作成功", []);
|
||||
}
|
||||
$rules = UserRuleModel::select();
|
||||
$include = [];
|
||||
if ($rule_id_string !== '*') {
|
||||
$include = explode(',', $rule_id_string);
|
||||
}
|
||||
$items = [];
|
||||
foreach ($rules as $item) {
|
||||
$items[] = [
|
||||
'name' => $item->title ?? $item->name ?? $item->id,
|
||||
'value' => (string)$item->id,
|
||||
'id' => $item->id,
|
||||
'pid' => $item->pid,
|
||||
];
|
||||
}
|
||||
$tree = new Tree($items);
|
||||
return $this->success("操作成功", $tree->getTree($include));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查权限字典是否合法
|
||||
* @param int $role_id
|
||||
* @param $rule_ids
|
||||
* @return void
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function checkRules(int|string|null $role_id, $rule_ids)
|
||||
{
|
||||
if ($rule_ids && $role_id>0) {
|
||||
$rule_ids = explode(',', $rule_ids);
|
||||
if (in_array('*', $rule_ids)) {
|
||||
throw new BusinessException('非法数据');
|
||||
}
|
||||
$rule_exists = UserRuleModel::whereIn('id', $rule_ids)->column('id');
|
||||
if (count($rule_exists) != count($rule_ids)) {
|
||||
throw new BusinessException('权限不存在');
|
||||
}
|
||||
$rule_id_string = UserRoleModel::where('id', $role_id)->value('rules');
|
||||
if ($rule_id_string === '') {
|
||||
throw new BusinessException('数据超出权限范围');
|
||||
}
|
||||
if ($rule_id_string === '*') {
|
||||
return;
|
||||
}
|
||||
$legal_rule_ids = explode(',', $rule_id_string);
|
||||
if (array_diff($rule_ids, $legal_rule_ids)) {
|
||||
throw new BusinessException('数据超出权限范围');
|
||||
}
|
||||
}
|
||||
}
|
||||
public function tree(Request $request): Response
|
||||
{
|
||||
$id = $request->post('id');
|
||||
$pid = $request->post('pid');
|
||||
$parent_rules = '*';
|
||||
if($pid){
|
||||
$parent_rules = UserRoleModel::where('id', $pid)->value('rules') ?: '';
|
||||
}
|
||||
if($parent_rules == '*'){
|
||||
$rules = UserRuleModel::where('status',1)->Field('id,title as text,pid as parent,"menu" as type')->select();
|
||||
}else{
|
||||
$rules = UserRuleModel::whereIn('id', $parent_rules)->where('status',1)->Field('id,title as text,pid as parent,"menu" as type')->select();
|
||||
}
|
||||
$selected_ids = '';
|
||||
if($id){
|
||||
$selected_ids = UserRoleModel::where('id', $id)->value('rules') ?:'';
|
||||
}
|
||||
if($selected_ids == '*'){
|
||||
$rules->each(function($item){
|
||||
$item->state = ['selected'=>false];
|
||||
return $item;
|
||||
});
|
||||
}else{
|
||||
$selected_ids = explode(',',$selected_ids);
|
||||
$rules->each(function($item)use($selected_ids){
|
||||
$state = ['selected'=>false];
|
||||
if(in_array($item->id, $selected_ids)){
|
||||
$state['selected'] = true;
|
||||
}
|
||||
$item->state = $state;
|
||||
// if($item->parent == 0){
|
||||
// $item->parent = '#';
|
||||
// }
|
||||
return $item;
|
||||
});
|
||||
}
|
||||
$rules->push([
|
||||
'id' => 0,
|
||||
'parent' => '#',
|
||||
'text' => '全部',
|
||||
'type' => 'menu',
|
||||
]);
|
||||
|
||||
return $this->success('ok',$rules);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
use plugin\admin\app\common\Tree;
|
||||
use plugin\admin\app\common\Util;
|
||||
use app\model\UserRole;
|
||||
use app\model\UserRule;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 权限菜单
|
||||
*/
|
||||
class UserRuleController extends Crud
|
||||
{
|
||||
/**
|
||||
* 不需要权限的方法
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $noNeedAuth = ['get', 'permission'];
|
||||
|
||||
/**
|
||||
* @var UserRule
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new UserRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
return view('user_rule/index');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 权限树形
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function roletree(Request $request): Response
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
$methods = [
|
||||
'select' => 'formatSelect',
|
||||
'tree' => 'formatTree',
|
||||
'table_tree' => 'formatTableTree',
|
||||
'normal' => 'formatNormal',
|
||||
];
|
||||
$format = 'normal';
|
||||
$items = $query->field('id,title as text,pid as parent,"menu" as type')->select();
|
||||
/**
|
||||
* @var UserRule $item
|
||||
*/
|
||||
foreach ($items as $key => $item) {
|
||||
$items[$key]->state = [
|
||||
"selected" => false,
|
||||
];
|
||||
if($items[$key]->parent == 0){
|
||||
$items[$key]->parent = '#';
|
||||
}
|
||||
};
|
||||
if (method_exists($this, "afterQuery")) {
|
||||
$items = call_user_func([$this, "afterQuery"], $items);
|
||||
}
|
||||
$format_function = $methods[$format] ?? 'formatNormal';
|
||||
|
||||
return call_user_func([$this, $format_function], $items, 0);
|
||||
}
|
||||
/**
|
||||
* 查询
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException
|
||||
*/
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = $this->selectInput($request);
|
||||
$query = $this->doSelect($where, $field, $order);
|
||||
return $this->doFormat($query, $format, 'all');
|
||||
//return parent::select($request);
|
||||
}
|
||||
/**
|
||||
* 获取菜单
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
function get(Request $request): Response
|
||||
{
|
||||
$rules = $this->getRules(admin('roles'));
|
||||
$types = $request->get('type', '0,1');
|
||||
$types = is_string($types) ? explode(',', $types) : [0, 1];
|
||||
$items = UserRule::order('weight', 'desc')->select();
|
||||
|
||||
$formatted_items = [];
|
||||
/**
|
||||
* @var UserRule $item
|
||||
*/
|
||||
foreach ($items as $item) {
|
||||
$item['pid'] = (int)$item['pid'];
|
||||
$item['name'] = $item['title'];
|
||||
$item['value'] = $item['id'];
|
||||
$item['icon'] = $item['icon'] ? "layui-icon {$item->icon}" : '';
|
||||
$formatted_items[] = $item;
|
||||
}
|
||||
|
||||
$tree = new Tree($formatted_items);
|
||||
$tree_items = $tree->getTree();
|
||||
// 超级管理员权限为 *
|
||||
if (!in_array('*', $rules)) {
|
||||
$this->removeNotContain($tree_items, 'id', $rules);
|
||||
}
|
||||
$this->removeNotContain($tree_items, 'type', $types);
|
||||
$menus = $this->empty_filter(Tree::arrayValues($tree_items));
|
||||
return $this->success("操作成功", $menus);
|
||||
}
|
||||
|
||||
private function empty_filter($menus)
|
||||
{
|
||||
return array_map(
|
||||
function ($menu) {
|
||||
if (isset($menu['children'])) {
|
||||
$menu['children'] = $this->empty_filter($menu['children']);
|
||||
}
|
||||
return $menu;
|
||||
},
|
||||
array_values(array_filter(
|
||||
$menus,
|
||||
function ($menu) {
|
||||
return $menu['type'] != 0 || isset($menu['children']) && count($this->empty_filter($menu['children'])) > 0;
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function permission(Request $request): Response
|
||||
{
|
||||
$rules = $this->getRules(admin('roles'));
|
||||
// 超级管理员
|
||||
if (in_array('*', $rules)) {
|
||||
return $this->success("操作成功", ['*']);
|
||||
}
|
||||
$keys = UserRule::whereIn('id', $rules)->column('key');
|
||||
$permissions = [];
|
||||
foreach ($keys as $key) {
|
||||
if (!$key = Util::controllerToUrlPath($key)) {
|
||||
continue;
|
||||
}
|
||||
$code = str_replace('/', '.', trim($key, '/'));
|
||||
$permissions[] = $code;
|
||||
}
|
||||
return $this->success("操作成功", $permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据类同步规则到数据库
|
||||
* @return void
|
||||
*/
|
||||
public function syncRules(): Response
|
||||
{
|
||||
//$items = $this->model->where('key', 'like', '%\\\\%')->get()->keyBy('key');
|
||||
$items = $this->model->whereLike('key', '%\\\\%')->select();
|
||||
$methods_in_db = [];
|
||||
$methods_in_files = [];
|
||||
/**
|
||||
* @var UserRule $item
|
||||
*/
|
||||
foreach ($items as $item) {
|
||||
$class = $item->key;
|
||||
if (strpos($class, '@')) {
|
||||
$methods_in_db[$class] = $class;
|
||||
continue;
|
||||
}
|
||||
if (class_exists($class)) {
|
||||
$reflection = new \ReflectionClass($class);
|
||||
$properties = $reflection->getDefaultProperties();
|
||||
$no_need_auth = array_merge($properties['noNeedLogin'] ?? [], $properties['noNeedAuth'] ?? []);
|
||||
$class = $reflection->getName();
|
||||
$pid = $item->id;
|
||||
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
|
||||
foreach ($methods as $method) {
|
||||
$method_name = $method->getName();
|
||||
if (strtolower($method_name) === 'index' || strpos($method_name, '__') === 0 || in_array($method_name, $no_need_auth)) {
|
||||
continue;
|
||||
}
|
||||
$name = "$class@$method_name";
|
||||
|
||||
$methods_in_files[$name] = $name;
|
||||
$title = Util::getCommentFirstLine($method->getDocComment()) ?: $method_name;
|
||||
/**
|
||||
* @var UserRule $item
|
||||
*/
|
||||
$menu = $items[$name] ?? [];
|
||||
if ($menu) {
|
||||
if ($menu->title != $title) {
|
||||
UserRule::where('key', $name)->update(['title' => $title]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$menu = new UserRule;
|
||||
$menu->pid = $pid;
|
||||
$menu->key = $name;
|
||||
$menu->title = $title;
|
||||
$menu->type = 2;
|
||||
$menu->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
// 从数据库中删除已经不存在的方法
|
||||
$menu_names_to_del = array_diff($methods_in_db, $methods_in_files);
|
||||
if ($menu_names_to_del) {
|
||||
UserRule::whereIn('key', $menu_names_to_del)->delete();
|
||||
}
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询前置方法
|
||||
* @param Request $request
|
||||
* @return array
|
||||
* @throws BusinessException
|
||||
*/
|
||||
protected function selectInput(Request $request): array
|
||||
{
|
||||
[$where, $format, $limit, $field, $order] = parent::selectInput($request);
|
||||
// 允许通过type=0,1格式传递菜单类型
|
||||
$types = $request->get('type');
|
||||
if ($types && is_string($types)) {
|
||||
$where['type'] = ['symbol'=>'in', 'value1'=>explode(',', $types)];
|
||||
}
|
||||
// 默认weight排序
|
||||
if (!$field) {
|
||||
$field = 'weight';
|
||||
$order = 'desc';
|
||||
}
|
||||
return [$where, $format, $limit, $field, $order];
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'GET') {
|
||||
$RuleList = $this->model->where('status',1)->order('id asc')->select();
|
||||
$tree = new Tree($RuleList);
|
||||
return view('user_rule/update',[
|
||||
'RuleList' => $tree->getTree()
|
||||
]);
|
||||
}
|
||||
$data = $this->insertInput($request);
|
||||
if (empty($data['type'])) {
|
||||
$data['type'] = strpos($data['key'], '\\') ? 1 : 0;
|
||||
}
|
||||
$data['key'] = str_replace('\\\\', '\\', $data['key']);
|
||||
$key = $data['key'] ?? '';
|
||||
if ($this->model->where('key', $key)->count('id')) {
|
||||
return $this->fail("菜单标识 $key 已经存在");
|
||||
}
|
||||
$data['pid'] = empty($data['pid']) ? 0 : $data['pid'];
|
||||
$this->doInsert($data);
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'GET') {
|
||||
$RuleList = $this->model->where('status',1)->order('id asc')->select();
|
||||
$ids = Request()->get('ids');
|
||||
$tree = new Tree($RuleList);
|
||||
return view('user_rule/update',[
|
||||
'RuleList' => $tree->getTree(),
|
||||
'row' => $this->model->where('id',$ids)->find()
|
||||
]);
|
||||
}
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
if (!$row = $this->model->find($id)) {
|
||||
return $this->json(2, '记录不存在');
|
||||
}
|
||||
if (isset($data['pid'])) {
|
||||
$data['pid'] = $data['pid'] ?: 0;
|
||||
if ($data['pid'] == $row['id']) {
|
||||
return $this->json(2, '不能将自己设置为上级菜单');
|
||||
}
|
||||
}
|
||||
if (isset($data['key'])) {
|
||||
$data['key'] = str_replace('\\\\', '\\', $data['key']);
|
||||
}
|
||||
$this->doUpdate($id, $data);
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$ids = $this->deleteInput($request);
|
||||
// 子规则一起删除
|
||||
$delete_ids = $children_ids = $ids;
|
||||
while($children_ids) {
|
||||
$children_ids = $this->model->whereIn('pid', $children_ids)->column('id');
|
||||
$delete_ids = array_merge($delete_ids, $children_ids);
|
||||
}
|
||||
$this->doDelete($delete_ids);
|
||||
return $this->success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除不包含某些数据的数组
|
||||
* @param $array
|
||||
* @param $key
|
||||
* @param $values
|
||||
* @return void
|
||||
*/
|
||||
protected function removeNotContain(&$array, $key, $values)
|
||||
{
|
||||
foreach ($array as $k => &$item) {
|
||||
if (!is_array($item)) {
|
||||
continue;
|
||||
}
|
||||
if (!$this->arrayContain($item, $key, $values)) {
|
||||
unset($array[$k]);
|
||||
} else {
|
||||
if (!isset($item['children'])) {
|
||||
continue;
|
||||
}
|
||||
$this->removeNotContain($item['children'], $key, $values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断数组是否包含某些数据
|
||||
* @param $array
|
||||
* @param $key
|
||||
* @param $values
|
||||
* @return bool
|
||||
*/
|
||||
protected function arrayContain(&$array, $key, $values): bool
|
||||
{
|
||||
if (!is_array($array)) {
|
||||
return false;
|
||||
}
|
||||
if (isset($array[$key]) && in_array($array[$key], $values)) {
|
||||
return true;
|
||||
}
|
||||
if (!isset($array['children'])) {
|
||||
return false;
|
||||
}
|
||||
foreach ($array['children'] as $item) {
|
||||
if ($this->arrayContain($item, $key, $values)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限规则
|
||||
* @param $roles
|
||||
* @return array
|
||||
*/
|
||||
protected function getRules($roles): array
|
||||
{
|
||||
$rules_strings = $roles ? UserRole::whereIn('id', $roles)->column('rules') : [];
|
||||
$rules = [];
|
||||
foreach ($rules_strings as $rule_string) {
|
||||
if (!$rule_string) {
|
||||
continue;
|
||||
}
|
||||
$rules = array_merge($rules, explode(',', $rule_string));
|
||||
}
|
||||
return $rules;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use app\model\User;
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 用户提现
|
||||
*/
|
||||
class WithdrawlController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \plugin\admin\app\model\Withdrawl
|
||||
*/
|
||||
protected $model = null;
|
||||
protected $relationSearch = ['user'];
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new \plugin\admin\app\model\Withdrawl();
|
||||
$statusList = $this->model->getStatusList();
|
||||
$this->assign("statusList", $statusList);
|
||||
$this->assignconfig("statusList", $statusList);
|
||||
$networkList = $this->model->getNetworkList();
|
||||
$this->assign("networkList", $networkList);
|
||||
$this->assignconfig("networkList", $networkList);
|
||||
}
|
||||
|
||||
protected function formatNormal($items, $total): Response
|
||||
{
|
||||
return json([
|
||||
'code' => 0,
|
||||
'msg' => 'ok',
|
||||
'count' => $total,
|
||||
'data' => $items,
|
||||
'wait_amount' => \app\model\Withdrawl::where('status',0)->sum('recive_amount'),
|
||||
'wait_count' => \app\model\Withdrawl::where('status',0)->count('id')
|
||||
]);
|
||||
}
|
||||
function multi(){
|
||||
$ids = Request()->post('ids');
|
||||
$params = Request()->post('params');
|
||||
parse_str($params,$s);
|
||||
if(!is_array($ids)){
|
||||
$ids = explode(',',$ids);
|
||||
}
|
||||
//$this->model = new \app\model\Withdrawl1;
|
||||
|
||||
foreach ($ids as $id){
|
||||
if(\app\model\Withdrawl::where('id',$id)->value('status')!=$s['status']){
|
||||
$this->doUpdate($id, $s);
|
||||
}
|
||||
}
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use plugin\admin\app\controller\Base;
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use app\model\WorkRecord;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 工作室
|
||||
*/
|
||||
class WorkRecordController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var WorkRecord
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new WorkRecord();
|
||||
$groupList = [
|
||||
['value'=>0,'label'=>"普通用户"],
|
||||
['value'=>1,'label'=>"工作室"],
|
||||
];
|
||||
$roleList = \app\model\UserRole::order('id','desc')->column('name as label,id as value');
|
||||
$this->assign('groupList',$groupList);
|
||||
$this->assignconfig('groupList',$groupList);
|
||||
$this->assign('roleList',$roleList);
|
||||
$this->assignconfig('roleList',$roleList);
|
||||
}
|
||||
public function team(Request $request): Response
|
||||
{
|
||||
return view();
|
||||
}
|
||||
public function select(Request $request): Response
|
||||
{
|
||||
$this->model = $this->model->with(['product','questionnaire']);
|
||||
return parent::select($request);
|
||||
}
|
||||
/**
|
||||
* 浏览
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
return view('productorder/index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function insert(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
return parent::insert($request);
|
||||
}
|
||||
return view('productorder/update',[
|
||||
'row' => \plugin\admin\app\model\User::findOrEmpty(0)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws BusinessException|Throwable
|
||||
*/
|
||||
public function update(Request $request): Response
|
||||
{
|
||||
if ($request->method() === 'POST') {
|
||||
[$id, $data] = $this->updateInput($request);
|
||||
$this->doUpdate($id, $data);
|
||||
$ret = $this->success('操作成功');
|
||||
return $ret;
|
||||
}
|
||||
$ids = Request()->get('ids');
|
||||
$user = $this->model->where('id',$ids)->find();
|
||||
return view('productorder/update',[
|
||||
'row' => $user
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\controller;
|
||||
|
||||
use app\model\User;
|
||||
use plugin\admin\app\controller\Crud;
|
||||
use support\exception\BusinessException;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 用户宣传
|
||||
*/
|
||||
class XuanchuanController extends Crud
|
||||
{
|
||||
|
||||
/**
|
||||
* @var \app\model\UserXuanchuan
|
||||
*/
|
||||
protected $model = null;
|
||||
protected $relationSearch = ['user'];
|
||||
/**
|
||||
* 构造函数
|
||||
* @return void
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
$this->model = new \app\model\UserXuanchuan();
|
||||
$statusList = $this->model->getStatusList();
|
||||
$this->assign("statusList", $statusList);
|
||||
$this->assignconfig("statusList", $statusList);
|
||||
$typeList = $this->model->getTypeList();
|
||||
$this->assign("typeList", $typeList);
|
||||
$this->assignconfig("typeList", $typeList);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace plugin\admin\app\exception;
|
||||
|
||||
use Throwable;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
|
||||
/**
|
||||
* Class Handler
|
||||
* @package support\exception
|
||||
*/
|
||||
class Handler extends \support\exception\Handler
|
||||
{
|
||||
public function render(Request $request, Throwable $exception): Response
|
||||
{
|
||||
$debug = Config("app.debug");
|
||||
$data = [
|
||||
'msg' => \nl2br((string)$exception->getMessage()),
|
||||
'code' => $exception->getCode(),
|
||||
'line' => $exception->getLine(),
|
||||
'previous' => $exception->getPrevious(),
|
||||
'trace' => $exception->getTrace(),
|
||||
'trace_str' => $exception->getTraceAsString(),
|
||||
'file' => $exception->getFile(),
|
||||
'exception' => $exception,
|
||||
'method' => $request->method(),
|
||||
'url' => $request->path(),
|
||||
'post' => $request->post(),
|
||||
'get' => $request->get(),
|
||||
'header' => $request->header()
|
||||
];
|
||||
if(method_exists($exception,'getData')){
|
||||
$data['data'] = $exception->getData();
|
||||
}
|
||||
if ($request->expectsJson()) {
|
||||
$json = [
|
||||
'code' => $data['code'] ?: 500,
|
||||
'msg' => $debug ? $data['msg'] : 'Server internal error1',
|
||||
'type' => 'failed',
|
||||
'data' => $data
|
||||
];
|
||||
$debug && $json['traces'] = (string)$exception;
|
||||
return new Response(200, ['Content-Type' => 'application/json'],
|
||||
\json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
}
|
||||
$error = $debug ? \nl2br((string)$exception) : 'Server internal error2';
|
||||
return view('common/error',$data);
|
||||
//return new Response(500, [], $error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
/**
|
||||
* Here is your custom functions.
|
||||
*/
|
||||
|
||||
use plugin\admin\app\model\Admin;
|
||||
use plugin\admin\app\model\AdminAccess;
|
||||
|
||||
error_reporting(E_ALL & ~E_WARNING);
|
||||
function url($path='',$args=[]){
|
||||
$pos = strpos($path,'/');
|
||||
if($pos === 0){
|
||||
$path = substr($path,1);
|
||||
}elseif($pos===false){
|
||||
$class = get_controller_name();
|
||||
$path = $class.'/'.$path;
|
||||
}else{
|
||||
//return $path;
|
||||
}
|
||||
if($args){
|
||||
if(is_array($args)){
|
||||
$args = http_build_query($args);
|
||||
}
|
||||
$path = $path.'?'.$args;
|
||||
}
|
||||
return '/app/admin/'.$path;
|
||||
}
|
||||
/**
|
||||
* 当前管理员id
|
||||
* @return integer|null
|
||||
*/
|
||||
function admin_id(): ?int
|
||||
{
|
||||
return session('admin.id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前管理员
|
||||
* @param null|array|string $fields
|
||||
* @return array|mixed|null
|
||||
* @throws Exception
|
||||
*/
|
||||
function admin($fields = null)
|
||||
{
|
||||
refresh_admin_session();
|
||||
if (!$admin = session('admin')) {
|
||||
return null;
|
||||
}
|
||||
if ($fields === null) {
|
||||
return $admin;
|
||||
}
|
||||
if (is_array($fields)) {
|
||||
$results = [];
|
||||
foreach ($fields as $field) {
|
||||
$results[$field] = $admin[$field] ?? null;
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
return $admin[$fields] ?? null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 刷新当前管理员session
|
||||
* @param bool $force
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
function refresh_admin_session(bool $force = false)
|
||||
{
|
||||
$admin_session = session('admin');
|
||||
if (!$admin_session) {
|
||||
return null;
|
||||
}
|
||||
$admin_id = $admin_session['id'];
|
||||
$time_now = time();
|
||||
// session在2秒内不刷新
|
||||
$session_ttl = 2;
|
||||
$session_last_update_time = session('admin.session_last_update_time', 0);
|
||||
if (!$force && $time_now - $session_last_update_time < $session_ttl) {
|
||||
return null;
|
||||
}
|
||||
$session = request()->session();
|
||||
$admin = Admin::find($admin_id);
|
||||
if (!$admin) {
|
||||
$session->forget('admin');
|
||||
return null;
|
||||
}
|
||||
$admin = $admin->toArray();
|
||||
$admin['password'] = md5($admin['password']);
|
||||
$admin_session['password'] = $admin_session['password'] ?? '';
|
||||
if ($admin['password'] != $admin_session['password']) {
|
||||
$session->forget('admin');
|
||||
return null;
|
||||
}
|
||||
// 账户被禁用
|
||||
if ($admin['status'] != 1) {
|
||||
$session->forget('admin');
|
||||
return;
|
||||
}
|
||||
$admin['roles'] = AdminAccess::where('admin_id', $admin_id)->column('role_id');
|
||||
$admin['session_last_update_time'] = $time_now;
|
||||
$session->set('admin', $admin);
|
||||
}
|
||||
|
||||
function getConfig($name=''){
|
||||
if($name){
|
||||
if(strpos($name,'.')>-1){
|
||||
$name = explode('.',$name);
|
||||
if(Config('site.'.$name[0].'.type') == 'array'){
|
||||
return Config('site.'.$name[0].'.value.'.$name[1]);
|
||||
}else{
|
||||
return Config('site.'.$name[0].'.value');
|
||||
}
|
||||
}else{
|
||||
return Config('site.'.$name);
|
||||
}
|
||||
}else{
|
||||
return Config('site');
|
||||
}
|
||||
}
|
||||
function null($var='',$defaut=''){
|
||||
if(isset($var) && !$var){
|
||||
return $defaut;
|
||||
}else{
|
||||
return $var;
|
||||
}
|
||||
}
|
||||
function buildFileInput($name='',$value='',$type='image',$limit=0,$allowChoose=true){
|
||||
$tpl = '
|
||||
<input id="c-'.$name.'" class="form-control" size="50" name="'.$name.'" type="hidden" value="'.null($value).'" data-tip="头像">
|
||||
<ul class="list-inline clearfix lyear-uploads-pic" data-template="preview" id="p-'.$name.'">
|
||||
<li nodelete class="col-xs-4 col-sm-3 col-md-2">
|
||||
<a class="pic-add faupload" style="height: auto;border: 0;" id="add-pic-btn" href="#!" title="点击上传" data-input-id="c-'.$name.'" data-mimetype="'.$type.'" data-multiple="'.($limit>0 ?'true' : 'false').'" data-preview-id="p-'.$name.'"></a>';
|
||||
if($allowChoose){
|
||||
$tpl.=' <a class="pic-add fachoose" style="height: auto;border: 0;" id="choose-pic-btn" href="#!" title="选择文件" data-input-id="c-'.$name.'" data-mimetype="image/*" data-multiple="'.($limit>0 ?'true' : 'false').'" data-preview-id="p-'.$name.'"></a>';
|
||||
}
|
||||
$tpl.='
|
||||
</li>
|
||||
</ul>';
|
||||
return $tpl;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\middleware;
|
||||
|
||||
use plugin\admin\api\Auth;
|
||||
use ReflectionException;
|
||||
use support\exception\BusinessException;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
use Webman\MiddlewareInterface;
|
||||
|
||||
class AccessControl implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param callable $handler
|
||||
* @return Response
|
||||
* @throws ReflectionException|BusinessException
|
||||
*/
|
||||
public function process(Request $request, callable $handler): Response
|
||||
{
|
||||
$controller = $request->controller;
|
||||
$action = $request->action;
|
||||
$request->controller_name = get_controller_name();
|
||||
$request->action_name = get_action_name();
|
||||
|
||||
$code = 0;
|
||||
$msg = '';
|
||||
if (!Auth::canAccess($controller, $action, $code, $msg)) {
|
||||
if ($request->expectsJson()) {
|
||||
$response = json(['code' => $code, 'msg' => $msg, 'data' => []]);
|
||||
} else {
|
||||
if ($code === 401) {
|
||||
$response = response('<script> if (self !== top) { parent.location = "/app/admin"; }</script>',401);
|
||||
//$response = response('',301, ['Location'=> '/app/admin/index/index']);
|
||||
} else {
|
||||
$request->app = '';
|
||||
$request->plugin = 'admin';
|
||||
$response = view('common/403')->withStatus(403);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$config = Config('site');
|
||||
$config['debug'] = config('app.debug');
|
||||
$config['controller'] = $request->controller_name;
|
||||
$config['action'] = $request->action_name;
|
||||
$config['moduleurl'] = '/app/admin';
|
||||
$request->_view_vars = array_merge((array) $request->_view_vars,[
|
||||
'user' => session('admin'),
|
||||
'config' => $config
|
||||
]);
|
||||
$response = $request->method() == 'OPTIONS' ? response('') : $handler($request);
|
||||
$response->withBody(str_replace([
|
||||
'__SELF__'
|
||||
],[
|
||||
request()->path()
|
||||
//url(request()->action)
|
||||
],$response->rawBody()))->getStatusCode();
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\model;
|
||||
|
||||
use app\model\Base;
|
||||
|
||||
/**
|
||||
* @property integer $id ID(主键)
|
||||
* @property string $username 用户名
|
||||
* @property string $nickname 昵称
|
||||
* @property string $password 密码
|
||||
* @property string $avatar 头像
|
||||
* @property string $email 邮箱
|
||||
* @property string $mobile 手机
|
||||
* @property string $totp_secret totp_secret
|
||||
* @property string $created_at 创建时间
|
||||
* @property string $updated_at 更新时间
|
||||
* @property string $login_at 登录时间
|
||||
* @property string $roles 角色
|
||||
* @property integer $status 状态 0正常 1禁用
|
||||
*/
|
||||
class Admin extends Base
|
||||
{
|
||||
public static function onAfterWrite($row)
|
||||
{
|
||||
if(!$row->totp_secret){
|
||||
$totp = \OTPHP\TOTP::create();
|
||||
$row->totp_secret = $totp->getSecret();
|
||||
$row->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\model;
|
||||
|
||||
use app\model\Base;
|
||||
|
||||
/**
|
||||
* @property integer $id ID(主键)
|
||||
* @property string $admin_id 管理员id
|
||||
* @property string $role_id 角色id
|
||||
*/
|
||||
class AdminAccess extends Base
|
||||
{
|
||||
protected function getOptions(): array{
|
||||
return array_merge(parent::getOptions(),[
|
||||
'autoWriteTimestamp' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\model;
|
||||
use app\model\Base;
|
||||
|
||||
/**
|
||||
* @property integer $id 主键(主键)
|
||||
* @property string $name 角色名
|
||||
* @property string $rules 权限
|
||||
* @property string $created_at 创建时间
|
||||
* @property string $updated_at 更新时间
|
||||
* @property integer $pid 上级id
|
||||
*/
|
||||
class AdminRole extends Base
|
||||
{
|
||||
|
||||
public function setRulesAttr($v='',$row=[])
|
||||
{
|
||||
if(is_array($v)){
|
||||
return implode(',',$v);
|
||||
}
|
||||
return $v;
|
||||
}
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRuleIds()
|
||||
{
|
||||
return $this->rules ? explode(',', $this->rules) : [];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\model;
|
||||
|
||||
use app\model\Base;
|
||||
use support\Model;
|
||||
class AdminRule extends Base
|
||||
{
|
||||
protected function getOptions(): array{
|
||||
return array_merge(parent::getOptions(),[
|
||||
'insert' => [
|
||||
'status' => 1
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\model;
|
||||
|
||||
class Card extends \app\model\Card
|
||||
{
|
||||
|
||||
public static function onAfterInsert($row)
|
||||
{
|
||||
|
||||
Hook('card.create',$row);
|
||||
// $data = [];
|
||||
// for ($i=0; $i < $row['total']; $i++) {
|
||||
// array_push($data,[
|
||||
// 'type' => $row['type'],
|
||||
// 'category_id' => $row['id'],
|
||||
// 'account' => \support\Random::uuid(),
|
||||
// 'password' => '123456',
|
||||
// 'expires' => $row['expires'],
|
||||
// 'days' => $row['days'],
|
||||
// 'is_used' => 0,
|
||||
// 'use_time' => 0,
|
||||
// 'status' => 1,
|
||||
// 'created_at' => time(),
|
||||
// 'updated_at' => time(),
|
||||
// ]);
|
||||
// }
|
||||
|
||||
// $Cdkey = new \app\model\Cdkey;
|
||||
// $Cdkey->saveAll($data);
|
||||
}
|
||||
function setExpiresAttr($v="",$row=[]){
|
||||
return strtotime($v);
|
||||
}
|
||||
function build(){
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\model;
|
||||
|
||||
class Category extends \app\model\Category
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\model;
|
||||
use app\model\Base;
|
||||
|
||||
/**
|
||||
* @property integer $id (主键)
|
||||
* @property string $name 键
|
||||
* @property mixed $value 值
|
||||
*/
|
||||
class Config extends Base
|
||||
{
|
||||
public function setValueAttr($v,$row){
|
||||
log_alert($row);
|
||||
if(is_array($v)){
|
||||
return json_encode($v,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
return $v;
|
||||
}
|
||||
public function getContentAttr($v,$row){
|
||||
if(in_array($row['type'] ,['radio','checkbox','select','selects'] ) && $row['content']){
|
||||
$firstLetter = substr($row['content'],0,1);
|
||||
if($firstLetter == '[' || $firstLetter == '{'){
|
||||
$arr = json_decode($row['content'],true);
|
||||
}else{
|
||||
$_content = explode("\r\n",$row['content']);
|
||||
$arr = [];
|
||||
foreach($_content as $k1=>$v1){
|
||||
$_v1 = explode('|',$v1);
|
||||
$arr[$_v1[0]]=$_v1[1];
|
||||
}
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
return $v;
|
||||
}
|
||||
public function getValueAttr($v,$row){
|
||||
if(in_array( $row['type'] ,['checkbox','select','selects'])){
|
||||
return json_decode($v,true);
|
||||
}
|
||||
return $v;
|
||||
}
|
||||
public function setSettingAttr($v){
|
||||
if(is_array($v)){
|
||||
return json_encode($v,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
return $v;
|
||||
}
|
||||
function setRuleAttr($v,$row){
|
||||
if(is_array($v)){
|
||||
return implode(';',$v);
|
||||
}else{
|
||||
return $v;
|
||||
}
|
||||
}
|
||||
function getSettingAttr($v='',$row=[]){
|
||||
if($v){
|
||||
return json_decode($v,true);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
// function setValueAttr($v,$row){
|
||||
// if(is_array($v)){
|
||||
// return implode(',',$v);
|
||||
// }else{
|
||||
// return $v;
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\model;
|
||||
use support\think\Db;
|
||||
|
||||
/**
|
||||
* @property integer $id 主键(主键)
|
||||
* @property string $username 用户名
|
||||
* @property string $nickname 昵称
|
||||
* @property string $password 密码
|
||||
* @property string $sex 性别
|
||||
* @property string $avatar 头像
|
||||
* @property string $email 邮箱
|
||||
* @property string $mobile 手机
|
||||
* @property integer $level 等级
|
||||
* @property string $birthday 生日
|
||||
* @property integer $money 余额
|
||||
* @property integer $score 积分
|
||||
* @property string $last_time 登录时间
|
||||
* @property string $last_ip 登录ip
|
||||
* @property string $join_time 注册时间
|
||||
* @property string $join_ip 注册ip
|
||||
* @property string $token token
|
||||
* @property string $created_at 创建时间
|
||||
* @property string $updated_at 更新时间
|
||||
* @property integer $role_id 角色
|
||||
* @property integer $status 禁用
|
||||
*/
|
||||
class User extends \app\model\User
|
||||
{
|
||||
|
||||
public static function onAfterUpdate($row){
|
||||
$changeData = $row->getChangedData();
|
||||
$orgData = $row->getOrigin();
|
||||
foreach(Config('site.allow_currencys') as $currency){
|
||||
if(isset($changeData[$currency])){
|
||||
$cha = $changeData[$currency] - $orgData[$currency];
|
||||
if($cha!=0){
|
||||
\support\Log::channel('cansnow')->alert('管理员手动修改用户余额:'.$row->id.',货币:'.$currency.',修改金额:'.$cha.',原始数据:'.$orgData[$currency].',修改后的数据:'.$changeData[$currency]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public static function onAfterDelete($row)
|
||||
{
|
||||
Db::name('address')->where('user_id',$row->id)->delete();
|
||||
Db::name('recharge')->where('user_id',$row->id)->delete();
|
||||
Db::name('record')->where('user_id',$row->id)->delete();
|
||||
Db::name('withdrawl')->where('user_id',$row->id)->delete();
|
||||
Db::name('user_extend')->where('user_id',$row->id)->delete();
|
||||
Db::name('user_team')->where('descendant_id|ancestor_id','=',$row->id)->delete();
|
||||
Db::name('withdrawl')->where('user_id',$row->id)->delete();
|
||||
(new \app\model\BalanceLog)->setSuffix('_money')->where('user_id',(int)$row->id)->delete();
|
||||
(new \app\model\BalanceLog)->setSuffix('_score')->where('user_id',(int)$row->id)->delete();
|
||||
(new \app\model\BalanceLog)->setSuffix('_currency1')->where('user_id',(int)$row->id)->delete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace plugin\admin\app\model;
|
||||
/**
|
||||
* @property integer $id 主键(主键)
|
||||
* @property string $title 标题
|
||||
* @property string $icon 图标
|
||||
* @property string $key 标识
|
||||
* @property integer $pid 上级菜单
|
||||
* @property string $created_at 创建时间
|
||||
* @property string $updated_at 更新时间
|
||||
* @property string $href url
|
||||
* @property integer $type 类型
|
||||
* @property integer $weight 排序
|
||||
*/
|
||||
class Withdrawl extends \app\model\Withdrawl
|
||||
{
|
||||
public static function onAfterUpdate($row)
|
||||
{
|
||||
$changedData = $row->getChangedData();
|
||||
if(isset($changedData['status'])){
|
||||
if ( $changedData['status']==\app\enum\WithdrawlStatus::TRANSFERRING->value) {
|
||||
Hook('withdrawl.transfering',$row);
|
||||
}else if ( $changedData['status']==\app\enum\WithdrawlStatus::COMPLETE->value) {
|
||||
Hook('withdrawl.success',$row);
|
||||
}else if ( $changedData['status']==\app\enum\WithdrawlStatus::REJECT->value) {
|
||||
\app\model\User::money($row->user_id,$row->deduction_amount,\app\enum\BalanceType::WITHDRAWAL_REJECT,$row->id);
|
||||
Hook('withdrawl.reject',$row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<ul class="nav nav-tabs page-tabs" role="tablist">
|
||||
<li class="active"> <a href="#base" role="tab" data-toggle="tab">基本信息</a> </li>
|
||||
<li> <a href="#safe" role="tab" data-toggle="tab">安全设置</a> </li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<!-- 基本信息 -->
|
||||
<div class="tab-pane fade in active" id="base">
|
||||
<form class="form-horizontal" action="{:url('update')}" method="post">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">昵称</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="nickname" value="{$row.nickname|null}" data-rule="required" placeholder="请输入昵称" autocomplete="off" class="form-control">
|
||||
</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 col-md-6">
|
||||
<input type="text" name="email" value="{$row.email|null}" placeholder="请输入邮箱" autocomplete="off" class="form-control" data-rule="email">
|
||||
</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 col-md-6">
|
||||
<input type="text" name="mobile" value="{$row.mobile|null}" placeholder="请输入联系电话" autocomplete="off" class="form-control">
|
||||
</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 col-md-6">
|
||||
<button type="submit" class="btn btn-primary">提交</button>
|
||||
<button type="reset" class="btn btn-warning">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade in" id="safe">
|
||||
<form class="form-horizontal" action="{:url('password')}" method="post">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">原始密码</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="password" name="old_password" data-rule="required;password" placeholder="请输入原始密码" autocomplete="off" class="form-control">
|
||||
</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 col-md-6">
|
||||
<input type="password" name="password" data-rule="required;password" placeholder="请输入新密码" autocomplete="off" class="form-control">
|
||||
</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 col-md-6">
|
||||
<input type="password" name="password_confirm" data-rule="required;password" placeholder="请再次输入新密码" autocomplete="off" class="form-control">
|
||||
</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 col-md-6">
|
||||
<button type="submit" class="btn btn-primary">提交</button>
|
||||
<button type="reset" class="btn btn-warning">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,98 @@
|
||||
{layout name="layout"}
|
||||
<style>
|
||||
.lyear-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.lyear-login {
|
||||
display: flex !important;
|
||||
min-height: 100vh;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
}
|
||||
|
||||
.lyear-login:after {
|
||||
content: '';
|
||||
min-height: inherit;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.login-center {
|
||||
background: #fff;
|
||||
min-width: 29.25rem;
|
||||
padding: 2.14286em 3.57143em;
|
||||
border-radius: 3px;
|
||||
margin: 2.85714em;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
margin-bottom: 1.5rem !important;
|
||||
}
|
||||
|
||||
.login-center .has-feedback.feedback-left .form-control {
|
||||
padding-left: 38px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.login-center .has-feedback.feedback-left .form-control-feedback {
|
||||
left: 0;
|
||||
right: auto;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
z-index: 4;
|
||||
color: #dcdcdc;
|
||||
}
|
||||
|
||||
.login-center .has-feedback.feedback-left.row .form-control-feedback {
|
||||
left: 15px;
|
||||
}
|
||||
</style>
|
||||
<div class="row lyear-wrapper" style="background-image: url(__IMG__/login-bg/3.jpg); background-size: cover;margin-top: -15px;">
|
||||
<div class="lyear-login">
|
||||
<div class="login-center">
|
||||
<div class="login-header text-center">
|
||||
<a href="javascript:;" style="font-size: 22px;display: flex;align-items: center;justify-content: center;"> <img alt="{$title}" src="{$logo}" width="32" class="m-r-5" />{$title}</a>
|
||||
</div>
|
||||
<form action="{:url('account/login')}" method="post" id="loginform" valid>
|
||||
<div class="form-group has-feedback feedback-left">
|
||||
<span class="mdi mdi-account form-control-feedback" aria-hidden="true"></span>
|
||||
<input type="text" placeholder="请输入您的用户名" class="form-control" name="username" id="username" data-rule="required" data-msg="请输入您的用户名"/>
|
||||
</div>
|
||||
<!-- <div class="form-group has-feedback feedback-left">
|
||||
<span class="mdi mdi-lock form-control-feedback" aria-hidden="true"></span>
|
||||
<input type="password" placeholder="请输入密码" class="form-control" id="password" name="password" data-rule="required;password" />
|
||||
</div> -->
|
||||
<div class="form-group has-feedback feedback-left">
|
||||
<span class="mdi mdi-lock form-control-feedback" aria-hidden="true"></span>
|
||||
<input type="code" placeholder="请输入OTOP验证码" class="form-control" id="code" name="code" data-rule="required;length(4)" data-msg="请输入OTOP验证码" />
|
||||
</div>
|
||||
{if Config('site.admin_login_captcha')}
|
||||
<div class="form-group has-feedback feedback-left row">
|
||||
<div class="col-xs-7">
|
||||
<span class="mdi mdi-check-all form-control-feedback" aria-hidden="true"></span>
|
||||
<input type="text" name="captcha" class="form-control" placeholder="验证码" data-rule="required;length(4)">
|
||||
</div>
|
||||
<div class="col-xs-5">
|
||||
<img src="{:url('account/captcha/login')}" class="pull-right codeImage" id="captcha"
|
||||
style="cursor: pointer;" onclick="this.src=this.src+'?d='+Math.random();" title="点击刷新"
|
||||
alt="captcha">
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="form-group">
|
||||
<label class="lyear-checkbox checkbox-primary m-t-10">
|
||||
<input type="checkbox" name="keep" value="1"><span>5天内自动登录</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-block btn-primary" type="submit">立即登录</button>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<footer class="col-sm-12 text-center">
|
||||
<p class="m-b-0">Copyright © {:Date('Y')} <a href="/">{$title}</a>. All right reserved</p>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,33 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form action="__SELF__" method="post" class="row form-horizontal">
|
||||
<div class="form-group">
|
||||
<label for="old_password" class="control-label col-xs-12 col-sm-2">密码</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="password" class="form-control" id="old_password" name="old_password" value="" placeholder="请输入旧密码" data-rule="required;password" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password" class="control-label col-xs-12 col-sm-2">密码</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="password" class="form-control" id="password" name="password" value="" placeholder="请输入新密码" data-rule="required;password" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password_confirm" class="control-label col-xs-12 col-sm-2">密码</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="password" class="form-control" id="password_confirm" name="password_confirm" value="" placeholder="请再次输入新密码" data-rule="required;password" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2"></label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">确 定</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{include file="common/file_preview" id="preview" /}
|
||||
@@ -0,0 +1,66 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form action="__SELF__" method="post" class="row form-horizontal">
|
||||
{if Request()->action == 'update'}
|
||||
<input type="hidden" name="id" value="{$row.id}" />
|
||||
{/if}
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">角色</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<select name="roles" id="roles" class="form-control" data-value="{$row.roles|null}"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="username" class="control-label col-xs-12 col-sm-2">用户名</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="username" name="username" value="{$row.username|null}" placeholder="请输入用户名" data-rule="required;username" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="nickname" class="control-label col-xs-12 col-sm-2">昵称</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="nickname" name="nickname" value="{$row.nickname|null}" placeholder="请输入昵称" data-rule="required" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password" class="control-label col-xs-12 col-sm-2">密码</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="password" class="form-control" id="password" name="password" value="{$row.username|null}" placeholder="请输入密码" data-rule="required;password" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email" class="control-label col-xs-12 col-sm-2">邮箱</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="email" name="email" value="{$row.email|null}" placeholder="请输入邮箱" data-rule="required;email" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="mobile" class="control-label col-xs-12 col-sm-2">手机</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="mobile" name="mobile" value="{$row.mobile|null}" placeholder="请输入手机" data-rule="required;phone" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">头像</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input id="c-avatar" class="form-control" size="50" name="avatar" type="hidden" value="{$row.avatar|null}" data-tip="头像">
|
||||
<ul class="list-inline clearfix lyear-uploads-pic" data-template="preview" id="p-avatar">
|
||||
<li nodelete class="col-xs-4 col-sm-3 col-md-2">
|
||||
<a class="pic-add faupload" style="height: auto;border: 0;" permission="app.admin.upload.avatar" id="add-pic-btn" href="#!" title="点击上传" data-input-id="c-avatar" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp,image/webp" data-multiple="false" data-preview-id="p-avatar"></a>
|
||||
<a class="pic-add fachoose" style="height: auto;border: 0;" permission="app.admin.upload.attachment" id="choose-pic-btn" href="#!" title="选择文件" data-input-id="c-avatar" data-mimetype="image/*" data-multiple="false" data-preview-id="p-avatar"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2"></label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">确 定</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{include file="common/file_preview" id="preview" /}
|
||||
@@ -0,0 +1,29 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_add" class="btn btn-primary m-r-5 btn-add" data-url="{:url('insert')}" data-title="新增">
|
||||
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-success m-r-5 btn-disabled disabled btn-multi" data-params="status=1">
|
||||
<span class="mdi mdi-check" aria-hidden="true"></span>启用
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-warning m-r-5 btn-disabled disabled btn-multi" data-params="status=0">
|
||||
<span class="mdi mdi-block-helper" aria-hidden="true"></span>禁用
|
||||
</a>
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
<!-- <a id="btn-refresh" class="btn btn-info btn-ajax {if $refreshBalance}btn-disabled disabled{/if}" data-url="{:url('address/refresh_balance')}" data-success="window.refreshBalanceSuccess">
|
||||
{if $refreshBalance}
|
||||
<span class="mdi mdi-loading mdi-spin " aria-hidden="true"></span>
|
||||
{else /}
|
||||
<span class="mdi mdi-refresh" aria-hidden="true"></span>
|
||||
{/if}
|
||||
刷新余额
|
||||
</a> -->
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,62 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" action="__SELF__" method="post">
|
||||
<input type="hidden" name="id" value="{$row.id|null}" />
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">network</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="network" value="{$row.network|null}" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">地址</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="address" value="{$row.address|null}" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">余额</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="balance" value="{$row.balance|null}" class="form-control" />
|
||||
</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 col-md-6">
|
||||
<input type="text" name="private_key" value="{$row.private_key|null}" class="form-control" />
|
||||
</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 col-md-6">
|
||||
{if $row.using}
|
||||
<input type="text" value="{$row.using|null}" class="form-control" />
|
||||
{else /}
|
||||
未使用
|
||||
{/if}
|
||||
</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 col-md-6">
|
||||
{volist name="statusList" id="rvo"}
|
||||
<label class="lyear-radio radio-primary radio-inline">
|
||||
<input type="radio" name="status" {if $row.status == $key} checked{/if} value="{$key}">
|
||||
<span>{$rvo}</span>
|
||||
</label>
|
||||
{/volist}
|
||||
</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 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">提交</button>
|
||||
<button type="reset" class="btn btn-warning m-r-5">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,22 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_add" class="btn btn-primary m-r-5 btn-add" data-url="/app/admin/Admin/insert" data-title="新增">
|
||||
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-success m-r-5 btn-disabled disabled btn-multi" data-params="status=1">
|
||||
<span class="mdi mdi-check" aria-hidden="true"></span>启用
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-warning m-r-5 btn-disabled disabled btn-multi" data-params="status=0">
|
||||
<span class="mdi mdi-block-helper" aria-hidden="true"></span>禁用
|
||||
</a>
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,84 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form action="__SELF__" method="post" class="row form-horizontal">
|
||||
{if Request()->action == 'update'}
|
||||
<input type="hidden" name="id" value="{$row.id}" />
|
||||
{/if}
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">角色</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<select name="roles" id="roles" class="form-control" data-value="{$row.roles|null}"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="username" class="control-label col-xs-12 col-sm-2">用户名</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="username" name="username" value="{$row.username|null}" placeholder="请输入用户名" data-rule="required;username" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="nickname" class="control-label col-xs-12 col-sm-2">昵称</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="nickname" name="nickname" value="{$row.nickname|null}" placeholder="请输入昵称" data-rule="required" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password" class="control-label col-xs-12 col-sm-2">密码</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="password" class="form-control" id="password" name="password" value="" placeholder="请输入密码" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password" class="control-label col-xs-12 col-sm-2">密钥</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="totp_secret" name="totp_secret" value="{$row.totp_secret}" readonly />
|
||||
<br />
|
||||
{php}
|
||||
$totp = \OTPHP\TOTP::create($row->totp_secret);
|
||||
$totp->setLabel($row->username);
|
||||
$totp->setIssuer('问卷');
|
||||
$qrCodeUri =$totp->getProvisioningUri();
|
||||
if(!$row->totp_secret){
|
||||
$row->totp_secret = $totp->getSecret();
|
||||
$row->save();
|
||||
}
|
||||
echo '<img src="https://api.qrtool.cn/?text='.urlencode($qrCodeUri).'" width="150" />';
|
||||
{/php}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email" class="control-label col-xs-12 col-sm-2">邮箱</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="email" name="email" value="{$row.email|null}" placeholder="请输入邮箱" data-rule="required;email" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="mobile" class="control-label col-xs-12 col-sm-2">手机</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="mobile" name="mobile" value="{$row.mobile|null}" placeholder="请输入手机" data-rule="required;phone" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">头像</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input id="c-avatar" class="form-control" size="50" name="avatar" type="hidden" value="{$row.avatar|null}" data-tip="杀杀杀">
|
||||
<ul class="list-inline clearfix lyear-uploads-pic" data-template="preview" id="p-avatar">
|
||||
<li nodelete class="col-xs-4 col-sm-3 col-md-2">
|
||||
<a class="pic-add faupload" style="height: auto;border: 0;" permission="app.admin.upload.avatar" id="add-pic-btn" href="#!" title="点击上传" data-input-id="c-avatar" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp,image/webp" data-multiple="false" data-preview-id="p-avatar"></a>
|
||||
<a class="pic-add fachoose" style="height: auto;border: 0;" permission="app.admin.upload.attachment" id="choose-pic-btn" href="#!" title="选择文件" data-input-id="c-avatar" data-mimetype="image/*" data-multiple="false" data-preview-id="p-avatar"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2"></label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">确 定</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{include file="common/file_preview" id="preview" /}
|
||||
@@ -0,0 +1,19 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="toolbar toolbar-btn-action">
|
||||
<button id="btn_add" type="button" class="btn btn-primary m-r-5 btn-add" data-url="{:url('insert')}" data-title="新增" permission="app.admin.role.insert">
|
||||
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
|
||||
</button>
|
||||
<button id="btn_add" type="button" class="btn btn-info m-r-5" permission="app.admin.role.update">
|
||||
<span class="mdi mdi-pencil" aria-hidden="true"></span>编辑
|
||||
</button>
|
||||
<button id="btn_delete" type="button" class="btn btn-danger btn-del" permission="app.admin.role.delete">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" method="post" action="__SELF__" role="form">
|
||||
<input type="hidden" name="id" value="{$row.id|null}" />
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">父级:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<select name="pid" class="form-control selectpicker">
|
||||
<option value="">无</option>
|
||||
{foreach name="rolelist" item="vo"}
|
||||
<option value="{$vo.id}" {if $vo.id==$row.pid}selected{/if}>{$vo.name}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</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 col-md-6">
|
||||
<input type="text" name="name" value="{$row.name|null}" data-rule="required" class="form-control">
|
||||
</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 col-md-6">
|
||||
<div class="table-responsive" style="min-height: 500px;">
|
||||
<div id="treeview"></div>
|
||||
</div>
|
||||
<input type="hidden" name="rules" value="{$row.rules|null}" data-rule="required" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="extend" class="control-label col-xs-12 col-sm-2"></label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">确 定</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
{layout name="layout"}
|
||||
<link href="/app/admin/libs/jquery-treegrid/jquery.treegrid.min.css" rel="stylesheet" />
|
||||
<div class="card">
|
||||
<div class="toolbar toolbar-btn-action">
|
||||
<button type="button" class="btn btn-primary m-r-5 btn-add" data-url="{:url('insert')}" data-title="新增" permission="app.admin.adminrule.insert">
|
||||
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
|
||||
</button>
|
||||
<button id="btn_delete" type="button" class="btn btn-danger m-r-5 btn-del" permission="app.admin.adminrule.delete">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning m-r-5 pop btn-ajax" href="{:url('buildcache')}" permission="app.admin.adminrule.insert">
|
||||
<span class="mdi mdi-refresh" aria-hidden="true"></span>缓存
|
||||
</button>
|
||||
<button type="button" class="btn btn-info pop btn-ajax" href="{:url('syncRules')}" permission="app.admin.adminrule.syncRules">
|
||||
<span class="mdi mdi-cloud-sync" aria-hidden="true"></span>同步
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,87 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" action="__SELF__" method="post">
|
||||
<input type="hidden" name="id" value="{$row.id|null}" />
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">标题</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="title" required lay-verify="required" value="{$row.title|null}" class="form-control">
|
||||
</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 col-md-6">
|
||||
<input type="text" name="key" required lay-verify="required" value="{$row.key|null}" class="form-control">
|
||||
</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 col-md-6">
|
||||
<select name="pid" id="pid" value="{$row.pid|null}" class="form-control">
|
||||
<option value="0" {if !$row.pid}selected{/if}>顶级菜单</option>
|
||||
{foreach name="RuleList" item="vo"}
|
||||
<option value="{$vo.id}" {if $row.pid==$vo.id }selected{/if}>{$vo.title}</option>
|
||||
{if $vo.children}
|
||||
{foreach name="vo.children" item="vo1"}
|
||||
<option value="{$vo1.id}" {if $row.pid==$vo1.id }selected{/if}> {$vo1.title}</option>
|
||||
{/foreach}
|
||||
{/if}
|
||||
{/foreach}
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">url</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="href" value="{$row.href|null}" class="form-control">
|
||||
</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 col-md-6">
|
||||
<div class="input-group input-groupp-md">
|
||||
<span class="input-group-addon"><i class="{$row.icon|null}" id="icon-style"></i></span>
|
||||
<input type="text" class="form-control" id="icon" name="icon" value="{$row.icon|null}" />
|
||||
<a href="javascript:;" class="btn-search-icon input-group-addon">搜索图标</a>
|
||||
</div>
|
||||
</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 col-md-6">
|
||||
<label class="lyear-radio radio-inline radio-primary">
|
||||
<input type="radio" name="type" value="0" {if $row.type==0 || !$row.type}checked{/if}><span>目录</span>
|
||||
</label>
|
||||
<label class="lyear-radio radio-inline radio-primary">
|
||||
<input type="radio" name="type" value="1" {if $row.type==1}checked{/if}><span>菜单</span>
|
||||
</label>
|
||||
<label class="lyear-radio radio-inline radio-primary">
|
||||
<input type="radio" name="type" value="2" {if $row.type==2}checked{/if}><span>权限</span>
|
||||
</label>
|
||||
</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 col-md-6">
|
||||
<input type="number" name="weight" value="{$row.weight|null=0}" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="extend" class="control-label col-xs-12 col-sm-2"></label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">确 定</button>
|
||||
<button type="reset" class="btn btn-danger m-r-5">重 置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{include file="common/chooseicontpl" /}
|
||||
@@ -0,0 +1,21 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_add" class="btn btn-primary m-r-5 btn-add" data-url="{:url('insert')}" data-title="新增">
|
||||
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-success m-r-5 btn-disabled disabled btn-multi" data-params="status=1">
|
||||
<span class="mdi mdi-check" aria-hidden="true"></span>启用
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-warning m-r-5 btn-disabled disabled btn-multi" data-params="status=0">
|
||||
<span class="mdi mdi-block-helper" aria-hidden="true"></span>禁用
|
||||
</a>
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,211 @@
|
||||
{layout name="layout"}
|
||||
<style>
|
||||
.autocomplete-searchtitle {
|
||||
padding: 0px 8px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.autocomplete-suggestions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.autocomplete-searchtitle .media {
|
||||
border-bottom: 1px solid #eee;
|
||||
margin-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.autocomplete-searchtitle .media:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.autocomplete-searchtitle .media h4.media-heading {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.autocomplete-searchtitle .media .text-muted {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.autocomplete-searchtitle .media:hover {
|
||||
background: #fefefe;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.form-archives>.row>.col-md-3 {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.form-archives>.row>.col-md-3 .form-group .control-label {}
|
||||
}
|
||||
|
||||
.panel-intro {
|
||||
box-shadow: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<link href="__JS__/libs/jquery-tags-input/jquery.tagsinput.min.css?v={$Think.config.site.version}" rel="stylesheet">
|
||||
<!-- <script src="/static/libs/froala/js/languages/zh_cn.js"></script> -->
|
||||
<script type="text/html" id="headertpl">
|
||||
<div class="px-2">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="alert" style="border-radius: 0;color: #0084ff; background: rgba(0, 132, 255, 0.1);margin-bottom:0;">
|
||||
共找到以下几篇相关文章:
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="itemtpl">
|
||||
<div class="media">
|
||||
<a class="" href="<%=item.url%>" target="_blank">
|
||||
<div class="media-left">
|
||||
<img src="<%=item.image%>" style="width: 50px; height: 50px;">
|
||||
</div>
|
||||
|
||||
<div class="media-body">
|
||||
<h4 class="media-heading"><%=#replace(item.title)%></h4>
|
||||
<div class="text-muted"><%=#formatter.status.call(context, item.status, item)%></div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
|
||||
|
||||
<input name="id" type="hidden" value="{$row.id}">
|
||||
<input name="type" type="hidden" value="{$row.type}">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">分类:</label>
|
||||
<div class="col-xs-12 col-sm-8">
|
||||
<select name="category_id" class="form-control selectpicker">
|
||||
{volist name="$categoryList" id="cvo"}
|
||||
<option value="{$cvo.id}" {if $row['category_id']== $cvo.id}selected{/if}>{$cvo.title}</option>
|
||||
{/volist}
|
||||
</select>
|
||||
</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">
|
||||
<input id="c-title" data-rule="required;length(2~100)" class="form-control" name="title"
|
||||
type="text" value="{$row.title|htmlentities}" data-suggestion-url="{:url('archives/suggestion')}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">封面:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input id="c-image" class="form-control" size="50" name="image" type="hidden" value="{$row.image|default=''}" data-tip="image">
|
||||
<ul class="list-inline clearfix lyear-uploads-pic" data-template="preview" id="p-image">
|
||||
<li nodelete class="col-xs-4 col-sm-3 col-md-2">
|
||||
<a class="pic-add faupload" style="height: auto;border: 0;" permission="app.admin.upload.image" id="add-pic-btn" href="#!" title="点击上传" data-input-id="c-image" data-mimetype="image/*" data-multiple="false" data-preview-id="p-image"></a>
|
||||
<a class="pic-add fachoose" style="height: auto;border: 0;" permission="app.admin.upload.attachment" id="choose-pic-btn" href="#!" title="选择文件" data-input-id="c-image" data-mimetype="image/*" data-multiple="false" data-preview-id="p-image"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">图集:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input id="c-images" class="form-control" size="50" name="images" type="hidden" value="{$row.images|default=''}" data-tip="images">
|
||||
<ul class="list-inline clearfix lyear-uploads-pic" data-template="preview" id="p-images">
|
||||
<li nodelete class="col-xs-4 col-sm-3 col-md-2">
|
||||
<a class="pic-add faupload" style="height: auto;border: 0;" permission="app.admin.upload.images" id="add-pic-btn" href="#!" title="点击上传" data-input-id="c-images" data-mimetype="image/*" data-multiple="false" data-preview-id="p-images"></a>
|
||||
<a class="pic-add fachoose" style="height: auto;border: 0;" permission="app.admin.upload.attachment" id="choose-pic-btn" href="#!" title="选择文件" data-input-id="c-images" data-mimetype="image/*" data-multiple="false" data-preview-id="p-images"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" data-field="intro">
|
||||
<label for="c-intro" class="control-label col-xs-12 col-sm-2">简介:</label>
|
||||
<div class="col-xs-12 col-sm-8">
|
||||
<textarea id="c-intro" data-rule="required;length(0~560)" class="form-control" name="intro"
|
||||
rows="5">{$row.intro}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--@formatter:off-->
|
||||
<div class="form-group" data-field="content">
|
||||
<label for="c-content" class="control-label col-xs-12 col-sm-2">正文:</label>
|
||||
<div class="col-xs-12 col-sm-8">
|
||||
<textarea id="c-content" data-rule="required;length(100~20000)" class="form-control editor" name="content" data-role="editor"
|
||||
rows="15">{$row.content}</textarea>
|
||||
<div style="margin-top:5px;">
|
||||
<a href="javascript:" class="btn btn-xs btn-info btn-getimage" data-toggle="tooltip"
|
||||
data-title="将提取内容第一张图作为缩略图"><i class="fa fa-camera"></i> {:__('提取缩略图')}</a>
|
||||
<a href="javascript:" class="btn btn-xs btn-info btn-getimages" data-toggle="tooltip"
|
||||
data-title="将提取内容前4张图作为组图"><i class="fa fa-camera"></i> {:__('提取组图')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--@formatter:on-->
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">标签:</label>
|
||||
<div class="col-xs-12 col-sm-8">
|
||||
<input id="c-tags" class="form-control" data-role="tagsinput" name="tags" type="text" value="{$row.tags|htmlentities}">
|
||||
</div>
|
||||
</div>
|
||||
<!--@formatter:off-->
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">标识:</label>
|
||||
<div class="col-xs-12 col-sm-8">
|
||||
<!--@formatter:off-->
|
||||
<select id="c-flag" class="form-control selectpicker" multiple name="flag[]">
|
||||
{foreach name="flagList" item="vo"}
|
||||
<option value="{$key}" data-subtext="{$key}" {in name="key" value="$row.flag" }selected{/in}>{$vo}
|
||||
</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
<!--@formatter:on-->
|
||||
</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">
|
||||
<input id="c-weigh" data-rule="required;integer(+)" class="form-control" name="weigh" type="number"
|
||||
value="{$row.weigh|default=0}">
|
||||
</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">
|
||||
<input id="c-views" data-rule="required;integer(+)" min="0" class="form-control" name="views" type="number"
|
||||
value="{$row.views|default=0}">
|
||||
</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">
|
||||
<input id="c-memo" class="form-control" name="memo" type="text" value="{$row.memo|htmlentities}">
|
||||
</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">
|
||||
{foreach name="statusList" item="vo"}
|
||||
<label for="status-{$key}" class="lyear-radio radio-inline radio-primary">
|
||||
<input id="status-{$key}" name="status" type="radio" value="{$key}" {in name="key" value="$row.status" }checked{/in} />
|
||||
<span>{$vo}</span>
|
||||
</label>
|
||||
{/foreach}
|
||||
</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 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">提交</button>
|
||||
<button type="reset" class="btn btn-warning m-r-5">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{include file="common/file_preview" id="preview"/}
|
||||
@@ -0,0 +1,20 @@
|
||||
{layout name="layout"}
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<ul class="nav nav-tabs page-tabs" id="filter_currency">
|
||||
{volist name=":Config('site.allow_balance_log')" id="cvo"}
|
||||
<li {if $cvo == 'money'}class="active" {/if} data-currency="{$cvo}"> <a href="javascript:;">{:__($cvo)}</a> </li>
|
||||
{/volist}
|
||||
</ul>
|
||||
<div class="tab-pane fade in form-horizontal">
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
<!-- <input type="text" class="form-control js-datepicker" style="display: inline-block;width: 150px;" /> -->
|
||||
</div>
|
||||
<div class="card-body" style="padding-top: 0px;">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action" style="display: hide;">
|
||||
<a id="btn_edit" class="btn btn-success m-r-5 btn-disabled disabled btn-multi" data-params="status=1">
|
||||
<span class="mdi mdi-check" aria-hidden="true"></span>启用
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-warning m-r-5 btn-disabled disabled btn-multi" data-params="status=0">
|
||||
<span class="mdi mdi-block-helper" aria-hidden="true"></span>禁用
|
||||
</a>
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
<a class="btn btn-info" target="_blank" href="{:url('card/export')}?ids={:Request()->get('ids')}">
|
||||
<span class="mdi mdi-export" aria-hidden="true"></span>导出
|
||||
</a>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_add" class="btn btn-primary m-r-5 btn-add" data-url="{:url('insert')}" data-title="新增">
|
||||
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-success m-r-5 btn-disabled disabled btn-multi" data-params="status=1">
|
||||
<span class="mdi mdi-check" aria-hidden="true"></span>启用
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-warning m-r-5 btn-disabled disabled btn-multi" data-params="status=0">
|
||||
<span class="mdi mdi-block-helper" aria-hidden="true"></span>禁用
|
||||
</a>
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,64 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
|
||||
<form class="form-horizontal" action="__SELF__" method="post">
|
||||
<input type="hidden" name="id" value="{$row.id|null}" />
|
||||
<input type="hidden" name="user_id" value="{$row.user_id|default=1}" />
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">标题</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="title" value="{$row.title|null}" data-rule="required;length(2~100)" class="form-control">
|
||||
</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 col-md-6">
|
||||
<select name="type" class="form-control">
|
||||
{volist name=":config('site.cdkey_category')" id="tvo"}
|
||||
<option value="{$key}">{$tvo}</option>
|
||||
{/volist}
|
||||
</select>
|
||||
</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 col-md-6">
|
||||
<input type="text" name="total" value="{$row.total|null}" data-rule="required;number(0~4)" class="form-control" />
|
||||
</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 col-md-6">
|
||||
<input type="text" name="days" value="{$row.days|null=0}" data-rule="required;number" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group hidden">
|
||||
<label class="control-label col-xs-12 col-sm-2">有效期</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="expires" value="{$row.expires|default=date('Y-m-d H:i:s')}" data-rule="required;datetime" class="form-control datetimepicker" />
|
||||
</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 col-md-6">
|
||||
<label class="lyear-switch switch-primary">
|
||||
<input type="checkbox" name="status" {if !$row.status} checked{/if} value="1">
|
||||
<span></span>
|
||||
</label>
|
||||
</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 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">提交</button>
|
||||
<button type="reset" class="btn btn-info m-r-5">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_add" class="btn btn-primary m-r-5 btn-add" data-url="{:url('insert')}" data-title="新增">
|
||||
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-success m-r-5 btn-disabled disabled btn-multi" data-params="status=1">
|
||||
<span class="mdi mdi-check" aria-hidden="true"></span>启用
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-warning m-r-5 btn-disabled disabled btn-multi" data-params="status=0">
|
||||
<span class="mdi mdi-block-helper" aria-hidden="true"></span>禁用
|
||||
</a>
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,39 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
|
||||
<form class="form-horizontal" action="__SELF__" method="post">
|
||||
<input type="hidden" name="id" value="{$row.id|null}" />
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">分类</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<select name="type" class="form-control" value="{$row.type}">
|
||||
{volist name="config.categorytype" id="cvo"}
|
||||
<option value="{$key}"{if $row.type == $key}selected{/if}>{$cvo}</option>
|
||||
{/volist}
|
||||
</select>
|
||||
</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 col-md-6">
|
||||
<input type="text" name="name" value="{$row.name|null}" data-rule="required;length(2~64)" class="form-control">
|
||||
</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 col-md-6">
|
||||
<input type="text" name="title" value="{$row.title|null}" data-rule="required;length(2~100)" class="form-control">
|
||||
</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 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">提交</button>
|
||||
<button type="reset" class="btn btn-warning m-r-5">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
{layout name="layout"}
|
||||
<section class="error-page">
|
||||
<div class="error-box">
|
||||
<div class="error-body text-center">
|
||||
<h1>403</h1>
|
||||
<h4>抱歉,你无权访问该页面</h4>
|
||||
<a href="{:url('index/index')}" class="btn btn-primary ">返回首页</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,10 @@
|
||||
{layout name="layout"}
|
||||
<section class="error-page">
|
||||
<div class="error-box">
|
||||
<div class="error-body text-center">
|
||||
<h1>404</h1>
|
||||
<h4>很抱歉,但是那个页面看起来已经不存在了。</h4>
|
||||
<a href="{:url('index/index')}" class="btn btn-primary ">返回首页</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
<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">搜索图标</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="mdi mdi-<%=iconlist[i]%>"></i>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</script>
|
||||
@@ -0,0 +1,496 @@
|
||||
{layout name="layout"}
|
||||
<script type="text/javascript" src="__JS__/../libs/jquery.min.js"></script>
|
||||
<style>
|
||||
.debug-page {
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.debug-header {
|
||||
background: #fbcbcf;
|
||||
color: white;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.debug-section {
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.debug-code {
|
||||
/* background: #272822;
|
||||
color: #f8f8f2; */
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
overflow-x: auto;
|
||||
white-space: pre;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.debug-code::before {
|
||||
content: '点击复制';
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: white;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.debug-code:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.debug-info {
|
||||
line-height: 30px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.debug-info-label {
|
||||
font-weight: bold;
|
||||
color: #495057;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.debug-trace {
|
||||
counter-reset: line;
|
||||
}
|
||||
|
||||
.debug-trace-line {
|
||||
position: relative;
|
||||
padding-left: 3.5em;
|
||||
transition: background-color 0.2s;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.debug-trace-line::before {
|
||||
counter-increment: line;
|
||||
content: counter(line);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 2.5em;
|
||||
text-align: right;
|
||||
color: #6c757d;
|
||||
border-right: 1px solid #dee2e6;
|
||||
padding-right: 1em;
|
||||
}
|
||||
.debug-trace-line.noline::before,.noline .debug-trace-line::before {
|
||||
content: '';
|
||||
width: 0;
|
||||
}
|
||||
.debug-trace-line.noline,.noline .debug-trace-line {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.debug-trace-line:hover {
|
||||
background: #f8f9fa;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.debug-trace-file {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.debug-trace-line-number {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.debug-trace-function {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.debug-trace-class {
|
||||
color: #6f42c1;
|
||||
}
|
||||
|
||||
.debug-actions {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link {
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-item.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
background: white;
|
||||
border: 1px solid #dee2e6;
|
||||
border-top: none;
|
||||
border-radius: 0 0 4px 4px;
|
||||
padding: 20px;
|
||||
max-height: calc(100vh - 250px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background-color: #fff3cd;
|
||||
padding: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* 暗色模式 */
|
||||
.dark-mode {
|
||||
background: #1a1a1a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-mode .debug-section,
|
||||
.dark-mode .tab-content {
|
||||
background: #2d2d2d;
|
||||
}
|
||||
|
||||
.dark-mode .nav-tabs {
|
||||
border-color: #444;
|
||||
}
|
||||
|
||||
.dark-mode .nav-tabs .nav-link {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-mode .nav-tabs .nav-link.active {
|
||||
background: #2d2d2d;
|
||||
border-color: #444;
|
||||
}
|
||||
|
||||
.dark-mode .debug-code {
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.dark-mode .debug-trace-line:hover {
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.dark-mode .debug-info-label {
|
||||
color: #adb5bd;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
// 搜索功能
|
||||
function toggleSearch() {
|
||||
const $searchBox = $('#search-box');
|
||||
$searchBox.toggle();
|
||||
if ($searchBox.is(':visible')) {
|
||||
$('#search-input').focus();
|
||||
}
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
$('#search-input').val('');
|
||||
removeHighlights();
|
||||
}
|
||||
|
||||
$('#search-input').on('input', function() {
|
||||
const searchText = $(this).val().toLowerCase();
|
||||
removeHighlights();
|
||||
if (searchText) {
|
||||
highlightText(searchText);
|
||||
}
|
||||
});
|
||||
|
||||
function highlightText(text) {
|
||||
const $content = $('.tab-content');
|
||||
const regex = new RegExp(text, 'gi');
|
||||
|
||||
$content.contents().each(function() {
|
||||
if (this.nodeType === Node.TEXT_NODE &&
|
||||
!$(this).parent().is('script, style')) {
|
||||
const $parent = $(this).parent();
|
||||
const newText = this.textContent.replace(regex, match =>
|
||||
`<span class="highlight">${match}</span>`
|
||||
);
|
||||
if (newText !== this.textContent) {
|
||||
$parent.html(newText);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeHighlights() {
|
||||
$('.highlight').each(function() {
|
||||
const $parent = $(this).parent();
|
||||
$parent.html($parent.text());
|
||||
});
|
||||
}
|
||||
|
||||
const errorInfo = {
|
||||
message: '{$msg}',
|
||||
code: '{$code}',
|
||||
file: '{$file}',
|
||||
line: '{$line}',
|
||||
trace: `{$trace_str}`,
|
||||
request: {
|
||||
method: '{$method}',
|
||||
url: '{$url}',
|
||||
param: {:json_encode($post,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)},
|
||||
get: {:json_encode($get,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)},
|
||||
header: {:json_encode($header,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)},
|
||||
},
|
||||
environment: {
|
||||
php_version: '{$Think.PHP_VERSION}',
|
||||
thinkphp_version: '{$Think.THINK_VERSION}',
|
||||
os: '{$Think.PHP_OS}',
|
||||
debug_time: '{$Think.APP_DEBUG_TIME}'
|
||||
}
|
||||
};
|
||||
// 复制功能
|
||||
function fallbackCopyToClipboard(text) {
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.left = '-999999px';
|
||||
textArea.style.top = '-999999px';
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
alert('已复制到剪贴板');
|
||||
} catch (err) {
|
||||
console.error('复制失败:', err);
|
||||
alert('复制失败,请手动复制');
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
navigator.clipboard.writeText(text)
|
||||
.then(() => alert('已复制到剪贴板'))
|
||||
.catch(() => fallbackCopyToClipboard(text));
|
||||
} else {
|
||||
fallbackCopyToClipboard(text);
|
||||
}
|
||||
}
|
||||
|
||||
function copyErrorInfo() {
|
||||
copyToClipboard(JSON.stringify(errorInfo, null, 2));
|
||||
}
|
||||
|
||||
// 导出功能
|
||||
function exportError() {
|
||||
const blob = new Blob([JSON.stringify(errorInfo, null, 2)], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `error-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.json`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
// 暗色模式
|
||||
function toggleDarkMode() {
|
||||
$('body').toggleClass('dark-mode');
|
||||
localStorage.setItem('debug-dark-mode', $('body').hasClass('dark-mode'));
|
||||
}
|
||||
|
||||
// 初始化
|
||||
$(document).ready(function() {
|
||||
// 检查暗色模式
|
||||
if (localStorage.getItem('debug-dark-mode') === 'true') {
|
||||
$('body').addClass('dark-mode');
|
||||
}
|
||||
|
||||
// 为所有代码块添加点击复制功能
|
||||
$('.debug-code').on('click', function() {
|
||||
copyToClipboard($(this).text());
|
||||
});
|
||||
|
||||
// 记住最后打开的标签页
|
||||
const lastTab = localStorage.getItem('debug-last-tab');
|
||||
if (lastTab) {
|
||||
const $tab = $(`#${lastTab}-tab`);
|
||||
if ($tab.length) {
|
||||
$tab.tab('show');
|
||||
}
|
||||
}
|
||||
|
||||
// 保存标签页状态
|
||||
$('.nav-link').on('shown.bs.tab', function(e) {
|
||||
localStorage.setItem('debug-last-tab', $(e.target).attr('id').replace('-tab', ''));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div class="debug-page">
|
||||
<!-- 错误标题 -->
|
||||
<div class="debug-header">
|
||||
<h4 class="mb-0">
|
||||
<i class="fas fa-exclamation-triangle mr-2"></i>
|
||||
{$msg}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<!-- 标签页导航 -->
|
||||
<ul class="nav nav-tabs" id="debugTabs" role="tablist">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" id="basic-tab" data-toggle="tab" href="#basic" role="tab">基本信息</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="request-tab" data-toggle="tab" href="#request" role="tab">请求信息</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="trace-tab" data-toggle="tab" href="#trace" role="tab">堆栈跟踪</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="env-tab" data-toggle="tab" href="#env" role="tab">环境信息</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="details-tab" data-toggle="tab" href="#details" role="tab">详细错误</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- 标签页内容 -->
|
||||
<div class="tab-content" id="debugTabContent">
|
||||
<!-- 基本信息 -->
|
||||
<div class="tab-pane fade active in" id="basic" role="tabpanel">
|
||||
<div class="debug-info">
|
||||
<span class="debug-info-label">错误代码:</span>
|
||||
<span class="badge badge-danger">{$code}</span>
|
||||
</div>
|
||||
<div class="debug-info" style="display: flex;align-items: center;">
|
||||
<span class="debug-info-label">SQL:</span>
|
||||
<div class="debug-trace noline" style="flex:1;">
|
||||
<div class="debug-trace-line">
|
||||
<span class="debug-trace-file" onclick="copyToClipboard('{$file}')">{$file}</span>
|
||||
<span class="debug-trace-line-number">:{$line}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{if isset($data) && isset($data['Database Status'])}
|
||||
<div class="debug-info" style="display: flex;align-items: center;">
|
||||
<span class="debug-info-label">SQL:</span>
|
||||
<pre class="debug-code" style="flex:1;">{$data['Database Status']['Error SQL']}</pre>
|
||||
</div>
|
||||
{/if}
|
||||
{if condition="$previous"}
|
||||
<div class="debug-info">
|
||||
<span class="debug-info-label">上一个错误:</span>
|
||||
<span>{$previous}</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- 请求信息 -->
|
||||
<div class="tab-pane fade" id="request" role="tabpanel">
|
||||
<div class="debug-info">
|
||||
<span class="debug-info-label">请求方法:</span>
|
||||
<span class="badge badge-info">{$method}</span>
|
||||
</div>
|
||||
<div class="debug-info">
|
||||
<span class="debug-info-label">请求URL:</span>
|
||||
<span onclick="copyToClipboard('{$url}')">{$url}</span>
|
||||
</div>
|
||||
<div class="debug-info">
|
||||
<span class="debug-info-label">GET参数:</span>
|
||||
<pre class="debug-code">{:json_encode($get,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)}</pre>
|
||||
</div>
|
||||
<div class="debug-info">
|
||||
<span class="debug-info-label">POST参数:</span>
|
||||
<pre class="debug-code">{:json_encode($post,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)}</pre>
|
||||
</div>
|
||||
<div class="debug-info">
|
||||
<span class="debug-info-label">请求头:</span>
|
||||
<pre class="debug-code">{:json_encode($header,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 堆栈跟踪 -->
|
||||
<div class="tab-pane fade" id="trace" role="tabpanel">
|
||||
<div class="debug-trace">
|
||||
{volist name="trace" id="item"}
|
||||
<div class="debug-trace-line">
|
||||
<span class="debug-trace-file" onclick="copyToClipboard('{$item.file}')">{$item.file}</span>
|
||||
<span class="debug-trace-line-number">:{$item.line}</span>
|
||||
<span class="debug-trace-function">{$item.function}</span>
|
||||
{if condition="$item.class"}
|
||||
<span class="debug-trace-class">{$item.class}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/volist}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 环境信息 -->
|
||||
<div class="tab-pane fade" id="env" role="tabpanel">
|
||||
<div class="debug-info">
|
||||
<span class="debug-info-label">PHP版本:</span>
|
||||
<span>{$Think.PHP_VERSION}</span>
|
||||
</div>
|
||||
<div class="debug-info">
|
||||
<span class="debug-info-label">ThinkPHP版本:</span>
|
||||
<span>{$Think.THINK_VERSION}</span>
|
||||
</div>
|
||||
<div class="debug-info">
|
||||
<span class="debug-info-label">服务器系统:</span>
|
||||
<span>{$Think.PHP_OS}</span>
|
||||
</div>
|
||||
<div class="debug-info">
|
||||
<span class="debug-info-label">运行时间:</span>
|
||||
<span>{$Think.APP_DEBUG_TIME}秒</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 详细错误信息 -->
|
||||
<div class="tab-pane fade" id="details" role="tabpanel">
|
||||
<pre class="debug-code">{$trace_str}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 调试工具 -->
|
||||
<div class="debug-actions">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary" onclick="window.location.reload()">
|
||||
<i class="fas fa-sync-alt"></i> 刷新
|
||||
</button>
|
||||
<button class="btn btn-secondary" onclick="history.back()">
|
||||
<i class="fas fa-arrow-left"></i> 返回
|
||||
</button>
|
||||
<button class="btn btn-info" onclick="copyErrorInfo()">
|
||||
<i class="fas fa-copy"></i> 复制
|
||||
</button>
|
||||
<button class="btn btn-warning" onclick="toggleSearch()">
|
||||
<i class="fas fa-search"></i> 搜索
|
||||
</button>
|
||||
<button class="btn btn-success" onclick="exportError()">
|
||||
<i class="fas fa-file-export"></i> 导出
|
||||
</button>
|
||||
<button class="btn btn-dark" onclick="toggleDarkMode()">
|
||||
<i class="fas fa-moon"></i> 暗色
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<div id="search-box" class="position-fixed" style="display: none; top: 20px; right: 20px; z-index: 1001;">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="search-input" placeholder="搜索错误信息...">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-secondary" onclick="clearSearch()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
<script type="text/html" id="[id]">
|
||||
<li class="col-xs-4 col-sm-3 col-md-2 previewitem">
|
||||
<figure>
|
||||
<% if (suffix =='jpg' || suffix == 'png' || suffix=='gif' || suffix=='svg' || suffix=='webp'){ %>
|
||||
<img src="<%= fullurl%>" width="120" data-url="<%= url%>" alt="图片一" onerror="this.src=Fast.api.fixurl('ajax/icon') + '?suffix=<%= suffix%>;this.onerror=null;" class="img-responsive">
|
||||
<% }else{ %>
|
||||
<img src="__IMG__/ext/<%= suffix%>.svg" width="120" data-url="__IMG__/ext/<%= suffix%>.svg" alt="图片一" class="img-responsive">
|
||||
<% }%>
|
||||
<figcaption>
|
||||
<a class="btn btn-round btn-square btn-primary btn-preview" href="javascript:;"><i class="mdi mdi-eye"></i></a>
|
||||
<a class="btn btn-round btn-square btn-danger btn-remove" href="javascript:;"><i class="mdi mdi-delete"></i></a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</li>
|
||||
</script>
|
||||
@@ -0,0 +1,353 @@
|
||||
{layout name="layout"}
|
||||
<style>
|
||||
.tab-pane {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-pane.active {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<ul class="nav nav-tabs page-tabs">
|
||||
{volist name="configlist" id="cvo"}
|
||||
<li {if $i==1}class="active" {/if}> <a
|
||||
href="#{$key}">{:Config('site.configgroup.'.$key)}</a> </li>
|
||||
{/volist}
|
||||
<li> <a href="#addform"><i class="mdi mdi-plus"></i></a> </li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<form class="form-horizontal" action="{:url('config/update')}" method="post" id="settingsform">
|
||||
{foreach name="configlist" item="cvo" key="k" }
|
||||
<div class="tab-pane fade in {if $k=='basic'}active{/if}" id="{$k}">
|
||||
{volist name="cvo" id="item" key="ckey"}
|
||||
{if $item.is_show}
|
||||
<div class="form-group" data-favisible="{$item.visible}">
|
||||
<label class="col-sm-3 col-md-2 col-xs-12 control-label" for="{$item.name}">{$item.title}</label>
|
||||
<div class="col-sm-6 col-md-6 col-xs-12">
|
||||
{switch $item.type}
|
||||
{case string}
|
||||
<input class="form-control" type="text" id="{$item.name}" name="{$item.name}"
|
||||
value="{$item.value|htmlentities}" placeholder="请输入{$item.title}"
|
||||
{if $item.rule}data-rule="{$item.rule}"{/if} data-tip="{$item.tip}" {$item.extend} />
|
||||
{/case}
|
||||
{case password}
|
||||
<input class="form-control" type="password" id="{$item.name}" name="{$item.name}"
|
||||
value="{$item.value}" placeholder="请输入{$item.title}" {if $item.rule}data-rule="{$item.rule}"{/if}
|
||||
data-tip="{$item.tip}" {$item.extend} />
|
||||
{/case}
|
||||
{case text}
|
||||
<textarea {$item.extend} name="{$item.name}" class="form-control" {if $item.rule}data-rule="{$item.rule}"{/if}
|
||||
rows="5" data-tip="{$item.tip}">{$item.value|htmlentities}</textarea>
|
||||
{/case}
|
||||
{case editor}
|
||||
<textarea {$item.extend} name="{$item.name}" id="editor-{$item.name}"
|
||||
class="form-control editor" {if $item.rule}data-rule="{$item.rule}"{/if} rows="5"
|
||||
data-tip="{$item.tip}">{$item.value|htmlentities}</textarea>
|
||||
{/case}
|
||||
{case array}
|
||||
<dl class="list-group fieldlist" {$item.extend} data-name="{$item.name}">
|
||||
<dd class="list-group-item m-b-5">
|
||||
<b style="width: 110px;display: inline-block;">{:isset($item["setting"]["key"])&&$item["setting"]["key"]?$item["setting"]["key"]:__('键')}</b>
|
||||
<b>{:isset($item["setting"]["value"])&&$item["setting"]["value"]?$item["setting"]["value"]:__('值')}</b>
|
||||
</dd>
|
||||
<dd>
|
||||
<a href="javascript:;" class="btn btn-sm btn-success btn-append m-t-5">
|
||||
<i class="fa fa-plus"></i> {:__('添加')}
|
||||
</a>
|
||||
</dd>
|
||||
</dl>
|
||||
<textarea name="{$item.name}" class="form-control hide" cols="30" rows="5">{$item.value|raw}</textarea>
|
||||
{/case}
|
||||
{case date}
|
||||
<input class="form-control datetimepicker" data-date-format="YYYY-MM-DD" type="text"
|
||||
id="{$item.name}" name="{$item.name}" value="{$item.value}"
|
||||
placeholder="请输入{$item.title}" {if $item.rule}data-rule="{$item.rule}"{/if} data-tip="{$item.tip}"
|
||||
{$item.extend} />
|
||||
{/case}
|
||||
{case time}
|
||||
<input class="form-control datetimepicker" data-date-format="HH:mm:ss" type="text"
|
||||
id="{$item.name}" name="{$item.name}" value="{$item.value}"
|
||||
placeholder="请输入{$item.title}" {if $item.rule}data-rule="{$item.rule}"{/if} data-tip="{$item.tip}"
|
||||
{$item.extend} />
|
||||
{/case}
|
||||
{case datetime}
|
||||
<input class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="text" id="{$item.name}" name="{$item.name}" value="{$item.value}"
|
||||
placeholder="请输入{$item.title}" {if $item.rule}data-rule="{$item.rule}"{/if} data-tip="{$item.tip}"
|
||||
{$item.extend} />
|
||||
{/case}
|
||||
{case datetimerange}
|
||||
<input class="form-control datetimerange" type="text" id="{$item.name}"
|
||||
name="{$item.name}" value="{$item.value}" placeholder="请输入{$item.title}"
|
||||
{if $item.rule}data-rule="{$item.rule}"{/if} data-tip="{$item.tip}" {$item.extend} />
|
||||
{/case}
|
||||
{case number}
|
||||
<input class="form-control" type="number"
|
||||
id="{$item.name}" name="{$item.name}" value="{$item.value}"
|
||||
placeholder="请输入{$item.title}" {if $item.rule}data-rule="{$item.rule}"{/if} data-tip="{$item.tip}"
|
||||
{$item.extend} />
|
||||
{/case}
|
||||
{case checkbox}
|
||||
<div class="controls-box">
|
||||
{html:checkbox options="$item.content" value="$item.value" name="$item.name" /}
|
||||
</div>
|
||||
{/case}
|
||||
{case radio}
|
||||
<div class="controls-box">
|
||||
{html:radio options="$item.content" value="$item.value" name="$item.name" /}
|
||||
</div>
|
||||
{/case}
|
||||
{case value="select" break="0"}{/case}
|
||||
{case value="selects"}
|
||||
<select {$item.extend} name="{$item.name}{$item.type=='selects'?'[]':''}"
|
||||
class="form-control selectpicker" data-tip="{$item.tip}" {$item.type=='selects'
|
||||
?'multiple':''}>
|
||||
{foreach name="$item['content']" item="vo"}
|
||||
<option value="{$key}" {in name="key" value="$item.value" }selected{/in}>{:__($vo)}
|
||||
</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
{/case}
|
||||
{case value="image" break="0"}{/case}
|
||||
{case value="images"}
|
||||
<div class="form-inline">
|
||||
<!-- <input id="c-{$item.name}" class="form-control" size="50" name="{$item.name}" type="text" value="{$item.value|htmlentities}" data-tip="{$item.tip}">
|
||||
<span><button type="button" id="faupload-{$item.name}" class="btn btn-danger faupload" data-input-id="c-{$item.name}" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp,image/webp" data-multiple="{$item.type=='image'?'false':'true'}" data-preview-id="p-{$item.name}"><i class="fa fa-upload"></i>{:__('Upload')}</button></span>
|
||||
<span><button type="button" id="fachoose-{$item.name}" class="btn btn-primary fachoose" data-input-id="c-{$item.name}" data-mimetype="image/*" data-multiple="{$item.type=='image'?'false':'true'}"><i class="fa fa-list"></i> {:__('Choose')}</button></span> -->
|
||||
{html:upload name="$item.name" value="$item.value" tip="$item.tip" /}
|
||||
</div>
|
||||
{/case}
|
||||
{case value="file" break="0"}{/case}
|
||||
{case value="files"}
|
||||
<div class="form-inline">
|
||||
<input id="c-{$item.name}" class="form-control" size="50" name="{$item.name}" type="text" value="{$item.value|htmlentities}" data-tip="{$item.tip}">
|
||||
<span><button type="button" id="faupload-{$item.name}" class="btn btn-danger faupload" data-input-id="c-{$item.name}" data-multiple="{$item.type=='file'?'false':'true'}"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
|
||||
<span><button type="button" id="fachoose-{$item.name}" class="btn btn-primary fachoose" data-input-id="c-{$item.name}" data-multiple="{$item.type=='file'?'false':'true'}"><i class="fa fa-list"></i> {:__('Choose')}</button></span>
|
||||
</div>
|
||||
{/case}
|
||||
{case switch}
|
||||
<div class="form-control" style="display: flex;align-items: center;border:0;">
|
||||
{html:switch name="$item.name" value="$item.value" /}
|
||||
</div>
|
||||
{/case}
|
||||
{case bool}
|
||||
<div class="controls-box">
|
||||
<label class="lyear-radio radio-inline radio-primary">
|
||||
<input type="radio" name="{$item.name}" value="1" data-tip="{$item.tip}"
|
||||
{$item.value?'checked':''}><span>是</span>
|
||||
</label>
|
||||
<label class="lyear-radio radio-inline radio-primary">
|
||||
<input type="radio" name="{$item.name}" value="0" data-tip="{$item.tip}"
|
||||
{$item.value?'':'checked'}><span>否</span>
|
||||
</label>
|
||||
</div>
|
||||
{/case}
|
||||
{case city}
|
||||
<div style="position:relative">
|
||||
<input {$item.extend} type="text" name="{$item.name}" id="c-{$item.name}"
|
||||
value="{$item.value|htmlentities}" class="form-control"
|
||||
data-toggle="city-picker" data-tip="{$item.tip}" {if $item.rule}data-rule="{$item.rule}"{/if} />
|
||||
</div>
|
||||
{/case}
|
||||
{case value="selectpage" break="0"}{/case}
|
||||
{case value="selectpages"}
|
||||
<input {$item.extend} type="text" name="{$item.name}" id="c-{$item.name}"
|
||||
value="{$item.value|htmlentities}" class="form-control selectpage"
|
||||
data-source="{:url($item.setting.table.'/selectpage')}?id={$item.id}"
|
||||
data-primary-key="{$item.setting.primarykey}" data-field="{$item.setting.field}"
|
||||
data-multiple="{$item.type=='selectpage'?'false':'true'}" data-tip="{$item.tip}"
|
||||
{if $item.rule}data-rule="{$item.rule}"{/if} />
|
||||
{/case}
|
||||
{case custom}
|
||||
{$item.extend}
|
||||
{/case}
|
||||
{/switch}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/volist}
|
||||
{if $k=='email'}
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3 col-md-2 col-xs-12">测试发送</label>
|
||||
<div class="col-sm-6 col-md-6 col-xs-12">
|
||||
<div class="input-group">
|
||||
<input class="form-control" name="test_mail_address" />
|
||||
<div class="input-group-btn"><button id="test_send_mail_btn" type="button" class="btn btn-primary m-r-5">测试</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3 col-md-2 col-xs-12"></label>
|
||||
<div class="col-sm-6 col-md-6 col-xs-12">
|
||||
<button type="submit" class="btn btn-primary m-r-5">确 定</button>
|
||||
<button type="button" class="btn btn-default"
|
||||
onclick="javascript:history.back(-1);return false;">返 回</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
</form>
|
||||
<form class="tab-pane fade in form-horizontal" action="{:url('config/insert')}" method="post"
|
||||
id="addform">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">{:__('分组')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<select name="row[group]" class="form-control selectpicker">
|
||||
{foreach name=":Config('site.configgroup')" item="vo"}
|
||||
<option value="{$key}" {in name="key" value="basic" }selected{/in}>{$vo}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</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 col-md-6">
|
||||
<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">{:__('变量名')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="row_name" name="row[name]" value=""
|
||||
data-rule="s3-30" ajaxurl="{:url('config/check')}?name=name" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="title" class="control-label col-xs-12 col-sm-2">{:__('变量标题')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="row_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">{:__('关联表')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<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">{:__('存储字段')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<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">{:__('显示字段')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<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">{:__('筛选条件')}:</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">{:__('键名')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="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">{:__('键值')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="row[setting][value]" class="form-control" id="c-array-value">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value" class="control-label col-xs-12 col-sm-2">{:__('变量值')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="row_value" name="row[value]" value="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group hide" id="add-content-container">
|
||||
<label for="content" class="control-label col-xs-12 col-sm-2">{:__('数据列表')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<textarea name="row[content]" id="row_content" cols="30" rows="5" class="form-control" placeholder="value1|title1 value2|title2"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="tip" class="control-label col-xs-12 col-sm-2">{:__('提示信息')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="row_tip" name="row[tip]" value="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="rule" class="control-label col-xs-12 col-sm-2">{:__('校验规则')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<div class="input-group pull-left">
|
||||
<input type="text" class="form-control tagsinput" id="row_rule" name="row[rule]" value=""
|
||||
data-tip="校验规则使用请参考Nice-validator文档" data-delimiter=";" />
|
||||
<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>
|
||||
<!-- <select class="form-control selectpicker" name="row[rule][]" multiple data-max-options="9999" data-live-search="true">
|
||||
{volist name="ruleList" id="item"}
|
||||
<option value="{$key}">{$item}({$key})</option>
|
||||
{/volist}
|
||||
</select> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="visible" class="control-label col-xs-12 col-sm-2">{:__('可见条件')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" id="row_visible" name="row[visible]" value="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="extend" class="control-label col-xs-12 col-sm-2">{:__('扩展属性')}:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<textarea name="row[extend]" id="row_extend" cols="30" rows="5" class="form-control"
|
||||
data-tip="{:__('扩展属性')}" data-msg-extend="当类型为自定义时,扩展属性不能为空"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="extend" class="control-label col-sm-3 col-md-2 col-xs-12"></label>
|
||||
<div class="col-sm-6 col-md-6 col-xs-12">
|
||||
<button type="submit" class="btn btn-primary m-r-5">确 定</button>
|
||||
<button type="button" class="btn btn-default"
|
||||
onclick="javascript:history.back(-1);return false;">返 回</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{include file="common/file_preview" id="preview"/}
|
||||
@@ -0,0 +1,94 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" action="__SELF__" method="post" target="result">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">等级</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<select name="group" class="form-control selectpicker">
|
||||
{foreach name=":Config('site.user_group')" item="vo"}
|
||||
<option value="{$key}" {if $key==$row.group }selected{/if}>{$vo}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</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 col-md-6">
|
||||
<input type="text" name="password" value="" autocomplete="off" class="form-control" {if
|
||||
Request()->action == 'update'}placeholder="不修改密码请留空" {/if}>
|
||||
</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 col-md-6">
|
||||
<label class="lyear-radio radio-inline radio-primary">
|
||||
<input type="radio" name="sex" {if $row.sex==1 || !$row.sex}checked{/if}
|
||||
value="1"><span>男</span>
|
||||
</label>
|
||||
<label class="lyear-radio radio-inline radio-primary">
|
||||
<input type="radio" name="sex" {if $row.sex==2}checked{/if} value="2"><span>女</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">头像</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input id="c-avatar" class="form-control" size="50" name="avatar" type="hidden"
|
||||
value="{$row.avatar|default='__IMG__/user/avatar.svg'}" data-tip="头像">
|
||||
<ul class="list-inline clearfix lyear-uploads-pic" data-template="preview" id="p-avatar">
|
||||
<li nodelete class="col-xs-4 col-sm-3 col-md-2">
|
||||
<a class="pic-add faupload" style="height: auto;border: 0;"
|
||||
permission="app.admin.upload.avatar" id="add-pic-btn" href="#!" title="点击上传"
|
||||
data-input-id="c-avatar"
|
||||
data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp,image/webp"
|
||||
data-multiple="false" data-preview-id="p-avatar"></a>
|
||||
<a class="pic-add fachoose" style="height: auto;border: 0;"
|
||||
permission="app.admin.upload.attachment" id="choose-pic-btn" href="#!" title="选择文件"
|
||||
data-input-id="c-avatar" data-mimetype="image/*" data-multiple="false"
|
||||
data-preview-id="p-avatar"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 col-md-2 col-xs-12 control-label" for="is_recharge_open">充值开关</label>
|
||||
<div class="col-sm-6 col-md-6 col-xs-12">
|
||||
<div class="form-control d-flex align-items-center d-border-none">
|
||||
{html:checkbox name="is_recharge_open" value="1" /}
|
||||
</div>
|
||||
</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 col-md-6">
|
||||
<input type="text" name="birthday" id="birthday" value="{$row.birthday|null}" <input type="text"
|
||||
name="birthday" id="birthday" value="{$row.birthday|null}" data-date-debug="true"
|
||||
data-date-sideBySide="true" data-date-collapse="false" data-date-format="YYYY-MM-DD HH:mm:ss"
|
||||
autocomplete="off" class="form-control js-datepicker">
|
||||
</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 col-md-6">
|
||||
<label class="lyear-switch switch-primary">
|
||||
<input type="checkbox" name="status" {if $row.status!==0} checked{/if} value="1">
|
||||
<span></span>
|
||||
</label>
|
||||
</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 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">提交</button>
|
||||
<button type="reset" class="btn btn-warning m-r-5">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<iframe src="" name="result" width="100%" frameborder="0"></iframe>
|
||||
</div>
|
||||
{include file="common/file_preview" id="preview"/}
|
||||
@@ -0,0 +1,118 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" action="__SELF__" method="post" target="result">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">下拉框选择</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<select name="group" class="form-control selectpicker">
|
||||
{foreach name=":Config('site.currency_langs')" item="vo"}
|
||||
<option value="{$row.key}" {if $key==$row.value }selected{/if}>{$vo}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">selectpage</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="article_ids" class="form-control selectpage" value="{$row.article_ids|null}">
|
||||
</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 col-md-6">
|
||||
<input type="text" name="password" value="" autocomplete="off" class="form-control" {if
|
||||
Request()->action == 'update'}placeholder="不修改密码请留空" {/if}>
|
||||
</div>
|
||||
</div>
|
||||
<!-- cxselect -->
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">性别</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<?php
|
||||
$sex_options = ['1'=>"男","2"=>"女"];
|
||||
?>
|
||||
{html:radio name="sex" value="1" options="$sex_options" /}
|
||||
</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 col-md-6">
|
||||
<?php
|
||||
$sex_options = ['1'=>"男","2"=>"女"];
|
||||
?>
|
||||
{html:checkbox name="sex1" value="1" options="$sex_options" /}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">头像</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
|
||||
{html:upload name="cover" value="$row.avatar|default='__IMG__/user/avatar.svg'" mimetype="" multiple="" url="" maxsize="" maxcount="" multipart="" params="" /}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 col-md-2 col-xs-12 control-label" for="is_recharge_open">充值开关</label>
|
||||
<div class="col-sm-6 col-md-6 col-xs-12">
|
||||
<div class="form-control" style="display: flex;align-items: center;border:0">
|
||||
{html:switch name="is_recharge_open" value="1" /}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">favisible</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="favisible" id="favisible" value="{$row.favisible|null}"
|
||||
data-favisible="is_recharge_open=1"
|
||||
data-target="form-group"
|
||||
autocomplete="off" class="form-control">
|
||||
</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 col-md-6">
|
||||
<input type="text" name="birthday" id="birthday" value="{$row.birthday|null}" data-date-debug="true"
|
||||
data-date-sideBySide="true" data-date-collapse="false" data-date-format="YYYY-MM-DD"
|
||||
autocomplete="off" class="form-control datepicker">
|
||||
</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 col-md-6">
|
||||
<input type="text" name="time" id="time" value="{$row.time|null}" data-date-debug="true"
|
||||
data-date-sideBySide="true" data-date-collapse="false" data-date-format="HH:mm:ss"
|
||||
autocomplete="off" class="form-control js-datepicker">
|
||||
</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 col-md-6">
|
||||
<input type="text" name="created_at" id="created_at" value="{$row.created_at|null}" data-date-debug="true"
|
||||
data-date-sideBySide="true" data-date-collapse="false" data-date-format="YYYY-MM-DD HH:mm:ss"
|
||||
autocomplete="off" class="form-control datetimepicker">
|
||||
</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 col-md-6">
|
||||
<label class="lyear-switch switch-primary">
|
||||
<input type="checkbox" name="status" {if $row.status!==0} checked{/if} value="1">
|
||||
<span></span>
|
||||
</label>
|
||||
</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 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">提交</button>
|
||||
<button type="reset" class="btn btn-warning m-r-5">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<iframe src="" name="result" width="100%" frameborder="0"></iframe>
|
||||
</div>
|
||||
{include file="common/file_preview" id="preview"/}
|
||||
@@ -0,0 +1,21 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_add" class="btn btn-primary m-r-5 btn-add" data-url="{:url('insert')}" data-title="新增">
|
||||
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-success m-r-5 btn-disabled disabled btn-multi" data-params="status=1">
|
||||
<span class="mdi mdi-check" aria-hidden="true"></span>启用
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-warning m-r-5 btn-disabled disabled btn-multi" data-params="status=0">
|
||||
<span class="mdi mdi-block-helper" aria-hidden="true"></span>禁用
|
||||
</a>
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,88 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" action="__SELF__" method="post">
|
||||
<input type="hidden" name="id" value="{$row.id|null}" />
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">名称</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<input type="text" name="title" value="{$row.title|null}" class="form-control" data-rule="required;length(2~100)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">封面:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input id="c-image" class="form-control" size="50" name="image" type="hidden" value="{$row.image|default=''}" data-tip="image">
|
||||
<ul class="list-inline clearfix lyear-uploads-pic" data-template="preview" id="p-image">
|
||||
<li nodelete class="col-xs-4 col-sm-3 col-md-2">
|
||||
<a class="pic-add faupload" style="height: auto;border: 0;" permission="app.admin.upload.image" id="add-pic-btn" href="#!" title="点击上传" data-input-id="c-image" data-mimetype="image/*" data-multiple="false" data-preview-id="p-image"></a>
|
||||
<a class="pic-add fachoose" style="height: auto;border: 0;display: none;" permission="app.admin.upload.attachment" id="choose-pic-btn" href="#!" title="选择文件" data-input-id="c-image" data-mimetype="image/*" data-multiple="false" data-preview-id="p-image"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">单价</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="text" name="price" value="{$row.price|null=3.5}" class="form-control" data-rule="required;range(0.5~)" />
|
||||
<div class="input-group-addon">¥</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">库存</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="number" name="stock" value="{$row.stock|default=0}" class="form-control" min="0" data-rule="required;range(0~99)" />
|
||||
<div class="input-group-addon">份</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">用户累计限购</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="number" name="user_quantity" value="{$row.user_quantity|default=0}" class="form-control" min="0" data-rule="required;range(0~99)" />
|
||||
<div class="input-group-addon">份</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-3">备注</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<textarea type="text" name="memo" class="form-control" >{$row.memo|null}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">销量</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="number" name="sales" value="{$row.sales|default=0}" disabled class="form-control" min="0" data-rule="required;range(0~99)" />
|
||||
<div class="input-group-addon">份</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-3">状态</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
{volist name="statusList" id="rvo"}
|
||||
<label class="lyear-radio radio-primary radio-inline">
|
||||
<input type="radio" name="status" {if $row.status == $key} checked{/if} value="{$key}">
|
||||
<span>{$rvo}</span>
|
||||
</label>
|
||||
{/volist}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-3"></label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">提交</button>
|
||||
<button type="reset" class="btn btn-warning m-r-5">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{include file="common/file_preview" id="preview"/}
|
||||
@@ -0,0 +1,12 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,65 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" action="__SELF__" method="post">
|
||||
<input type="hidden" name="id" value="{$row.id|null}" />
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">产品名称</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<input type="text" value="{$row.gift.title|null}" class="form-control" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">总价</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<input type="text" value="{$row.denomination}" class="form-control" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">面额</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="text" value="{$row.denomination|null=3.5|formatAmount}" class="form-control" disabled />
|
||||
<div class="input-group-addon">¥</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">数量</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="number" value="{$row.quantity|default=1}" class="form-control" disabled />
|
||||
<div class="input-group-addon">份</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">CDKEY</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<textarea name="cdkey" class="form-control" data-rule="required">{$row.cdkey}</textarea>
|
||||
<div class="help-block">一行一个</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-3">状态</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
{volist name="statusList" id="rvo"}
|
||||
<label class="lyear-radio radio-primary radio-inline">
|
||||
<input type="radio" name="status" {if $row.status == $key} checked{/if} value="{$key}">
|
||||
<span>{$rvo}</span>
|
||||
</label>
|
||||
{/volist}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-3"></label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">提交</button>
|
||||
<button type="reset" class="btn btn-warning m-r-5">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{include file="common/file_preview" id="preview"/}
|
||||
@@ -0,0 +1,12 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,189 @@
|
||||
{layout name="layout"}
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<div class="card bg-primary">
|
||||
<div class="card-body clearfix">
|
||||
<div class="pull-right">
|
||||
<p class="h6 text-white m-t-0">今日充值</p>
|
||||
<p class="h3 text-white m-b-0 fa-1-5x"><?=$today_user_recharge_sum?></p>
|
||||
</div>
|
||||
<div class="pull-left"> <span class="img-avatar img-avatar-48 bg-translucent"><i class="mdi mdi-currency-cny fa-1-5x"></i></span> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<div class="card bg-purple">
|
||||
<div class="card-body clearfix">
|
||||
<div class="pull-right">
|
||||
<p class="h6 text-white m-t-0">7日内充值</p>
|
||||
<p class="h3 text-white m-b-0 fa-1-5x"><?=$day7_user_recharge_sum?></p>
|
||||
</div>
|
||||
<div class="pull-left"> <span class="img-avatar img-avatar-48 bg-translucent"><i
|
||||
class="mdi mdi-account fa-1-5x"></i></span> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<div class="card bg-success">
|
||||
<div class="card-body clearfix">
|
||||
<div class="pull-right">
|
||||
<p class="h6 text-white m-t-0">总充值</p>
|
||||
<p class="h3 text-white m-b-0 fa-1-5x"><?=$recharge_total?></p>
|
||||
</div>
|
||||
<div class="pull-left"> <span class="img-avatar img-avatar-48 bg-translucent"><i
|
||||
class="mdi mdi-arrow-down-bold fa-1-5x"></i></span> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<div class="card bg-danger">
|
||||
<div class="card-body clearfix">
|
||||
<div class="pull-right">
|
||||
<p class="h6 text-white m-t-0">总提现</p>
|
||||
<p class="h3 text-white m-b-0 fa-1-5x"><?=$withdrawl_total?></p>
|
||||
</div>
|
||||
<div class="pull-left"> <span class="img-avatar img-avatar-48 bg-translucent"><i
|
||||
class="mdi mdi-arrow-up-bold fa-1-5x"></i></span> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<canvas class="js-money-chartjs"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<canvas class="js-role_buy_lines"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4>用户统计</h4>
|
||||
<div class="btn-group pull-right" role="group" style="display:flex;align-items: center;">
|
||||
<button class="btn btn-xs" onclick="location.reload();"><i class="mdi mdi-refresh"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover">
|
||||
<colgroup>
|
||||
<col width="50%">
|
||||
<col>
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>渠道商数</td>
|
||||
<td>
|
||||
<?php
|
||||
echo \app\model\User::where('group',1)->count('id');
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>用户余额总和</td>
|
||||
<td>{$user_money_total}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>用户积分总和</td>
|
||||
<td>{$user_score_total}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>调研币总和</td>
|
||||
<td><?php echo \app\model\User::sum('currency1');?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>待分配总和</td>
|
||||
<td><?php echo \app\model\User::sum('currency7');?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4>系统信息</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover">
|
||||
<colgroup>
|
||||
<col width="50%">
|
||||
<col>
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>问卷成交个数</td>
|
||||
<td>
|
||||
<?php $system_question_total = cache('system_question_total');echo $system_question_total;?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>预计支出泡沫</td>
|
||||
<td>
|
||||
<?php $system_question_cha_total = 0;echo $system_question_cha_total;?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>总沉淀金额</td>
|
||||
<td>
|
||||
<?php
|
||||
$system_role_buy_amount_total = cache('system_role_buy_amount_total');
|
||||
$system_role_buy_reward_total = cache('system_role_buy_reward_total');
|
||||
$system_role_buy_residual_total = $system_role_buy_amount_total - $system_role_buy_reward_total;
|
||||
echo $system_role_buy_residual_total;
|
||||
?>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>实际泡沫</td>
|
||||
<td>
|
||||
<?php
|
||||
$cha = $system_question_cha_total - $system_role_buy_residual_total;
|
||||
if($cha>0){
|
||||
echo '<span class="text-danger">'.$cha.'</span>';
|
||||
}else{
|
||||
echo '<span class="text-success">'.$cha.'</span>';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,251 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<title>{:Config('site.name')}</title>
|
||||
<link rel="icon" href="{:Config('site.admin_logo','__IMG__/logo.png')}" type="image/ico">
|
||||
<link href="__CSS__/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="__CSS__/animate.css" rel="stylesheet" />
|
||||
<link href="__CSS__/materialdesignicons.min.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="__JS__/../libs/bootstrap-multitabs/multitabs.min.css">
|
||||
<link href="__CSS__/style.min.css" rel="stylesheet">
|
||||
<script type="text/javascript">
|
||||
var require = {
|
||||
config: {$config| json_encode=JSON_UNESCAPED_UNICODE|raw}
|
||||
};
|
||||
var user = {$user|json_encode|raw};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="lyear-layout-web">
|
||||
<div class="lyear-layout-container">
|
||||
<!--左侧导航-->
|
||||
<aside class="lyear-layout-sidebar">
|
||||
|
||||
<!-- logo -->
|
||||
<div id="logo" class="sidebar-header">
|
||||
<a href="{:url('index/index')}" style="font-size: 24px;display: flex;justify-content: center;align-items: center;">
|
||||
<img src="{:Config('site.admin_logo','__IMG__/logo.png')}" width="32" title="{:Config('site.name')}" alt="{:Config('site.name')}" style="margin-right: 10px;" />
|
||||
<span>{:Config('site.name')}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="lyear-layout-sidebar-scroll">
|
||||
|
||||
<nav class="sidebar-main">
|
||||
<ul class="nav nav-drawer">
|
||||
<li class="nav-item active"> <a class="multitabs" href="{:url('index/dashboard')}"><i class="mdi mdi-home"></i>
|
||||
<span>后台首页</span></a> </li>
|
||||
{volist name="menu" id="vo1"}
|
||||
{if $vo1.status}
|
||||
<li class="nav-item {if $vo1['children']}nav-item-has-subnav{/if}">
|
||||
{if $vo1['children']}
|
||||
<a href="javascript:void(0)"><i class="{$vo1.icon|null='mdi mdi-palette'}"></i>
|
||||
<span>{$vo1.title}</span></a>
|
||||
<ul class="nav nav-subnav">
|
||||
{volist name="vo1.children" id="vo2"}
|
||||
{if $vo2.status}
|
||||
<li> <a href="{:url($vo2.href)}" class="multitabs"><i class="m-r-5 {$vo2.icon|null='mdi mdi-blank'}"></i>{$vo2.title}</a></li>
|
||||
{/if}
|
||||
{/volist}
|
||||
</ul>
|
||||
{else /}
|
||||
<a href="{:url($vo1.href)}"><i class="{$vo1.icon|null='mdi mdi-palette'}"></i>
|
||||
<span>{$vo1.title}</span></a>
|
||||
{/if}
|
||||
</li>
|
||||
{/if}
|
||||
{/volist}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<p class="copyright">Copyright © {:date('Y')}. <br /><a target="_blank" href="/">{:Config('site.name')}</a><br />All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
<!--End 左侧导航-->
|
||||
|
||||
<!--头部信息-->
|
||||
<header class="lyear-layout-header">
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="topbar">
|
||||
|
||||
<div class="topbar-left">
|
||||
<div class="lyear-aside-toggler">
|
||||
<span class="lyear-toggler-bar"></span>
|
||||
<span class="lyear-toggler-bar"></span>
|
||||
<span class="lyear-toggler-bar"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="topbar-right">
|
||||
<li class="dropdown dropdown-profile">
|
||||
<a href="javascript:void(0)" data-toggle="dropdown">
|
||||
<img class="img-avatar img-avatar-24 m-r-10" src="__CDN__{$user.avatar|default='/app/admin/avatar.png'}"
|
||||
alt="{$user.nickname}" />
|
||||
<span>{$user.nickname} <span class="caret"></span></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li> <a class="multitabs" data-url="{:url('account/index')}" href="javascript:;"><i class="mdi mdi-account"></i> 个人信息</a> </li>
|
||||
<li> <a class="multitabs" data-url="{:url('account/index')}" href="javascript:;"><i class="mdi mdi-lock-outline"></i> 修改密码</a>
|
||||
</li>
|
||||
<li> <a href="javascript:;" data-url="{:url('index/clean')}" class="btn-ajax"><i class="mdi mdi-delete"></i> 清空缓存</a></li>
|
||||
<li class="divider"></li>
|
||||
<li> <a href="javascript:;" data-url="{:url('account/logout')}" class="btn-logout"><i class="mdi mdi-logout-variant"></i> 退出登录</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!--切换主题配色-->
|
||||
<li class="dropdown dropdown-skin">
|
||||
<span data-toggle="dropdown" class="icon-palette"><i class="mdi mdi-palette"></i></span>
|
||||
<ul class="dropdown-menu dropdown-menu-right" data-stopPropagation="true">
|
||||
<li class="drop-title">
|
||||
<p>LOGO</p>
|
||||
</li>
|
||||
<li class="drop-skin-li clearfix">
|
||||
<span class="inverse">
|
||||
<input type="radio" name="logo_bg" value="default" id="logo_bg_1" checked>
|
||||
<label for="logo_bg_1"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_2" id="logo_bg_2">
|
||||
<label for="logo_bg_2"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_3" id="logo_bg_3">
|
||||
<label for="logo_bg_3"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_4" id="logo_bg_4">
|
||||
<label for="logo_bg_4"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_5" id="logo_bg_5">
|
||||
<label for="logo_bg_5"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_6" id="logo_bg_6">
|
||||
<label for="logo_bg_6"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_7" id="logo_bg_7">
|
||||
<label for="logo_bg_7"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="logo_bg" value="color_8" id="logo_bg_8">
|
||||
<label for="logo_bg_8"></label>
|
||||
</span>
|
||||
</li>
|
||||
<li class="drop-title">
|
||||
<p>头部</p>
|
||||
</li>
|
||||
<li class="drop-skin-li clearfix">
|
||||
<span class="inverse">
|
||||
<input type="radio" name="header_bg" value="default" id="header_bg_1"
|
||||
checked>
|
||||
<label for="header_bg_1"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_2" id="header_bg_2">
|
||||
<label for="header_bg_2"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_3" id="header_bg_3">
|
||||
<label for="header_bg_3"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_4" id="header_bg_4">
|
||||
<label for="header_bg_4"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_5" id="header_bg_5">
|
||||
<label for="header_bg_5"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_6" id="header_bg_6">
|
||||
<label for="header_bg_6"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_7" id="header_bg_7">
|
||||
<label for="header_bg_7"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_8" id="header_bg_8">
|
||||
<label for="header_bg_8"></label>
|
||||
</span>
|
||||
</li>
|
||||
<li class="drop-title">
|
||||
<p>侧边栏</p>
|
||||
</li>
|
||||
<li class="drop-skin-li clearfix">
|
||||
<span class="inverse">
|
||||
<input type="radio" name="sidebar_bg" value="default" id="sidebar_bg_1"
|
||||
checked>
|
||||
<label for="sidebar_bg_1"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_2" id="sidebar_bg_2">
|
||||
<label for="sidebar_bg_2"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_3" id="sidebar_bg_3">
|
||||
<label for="sidebar_bg_3"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_4" id="sidebar_bg_4">
|
||||
<label for="sidebar_bg_4"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_5" id="sidebar_bg_5">
|
||||
<label for="sidebar_bg_5"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_6" id="sidebar_bg_6">
|
||||
<label for="sidebar_bg_6"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_7" id="sidebar_bg_7">
|
||||
<label for="sidebar_bg_7"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="sidebar_bg" value="color_8" id="sidebar_bg_8">
|
||||
<label for="sidebar_bg_8"></label>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!--切换主题配色-->
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
<!--End 头部信息-->
|
||||
|
||||
<!--页面主要内容-->
|
||||
<main class="lyear-layout-content">
|
||||
|
||||
<div id="iframe-content"></div>
|
||||
|
||||
</main>
|
||||
<!--End 页面主要内容-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="__JS__/../libs/jquery.min.js"></script>
|
||||
<!-- <script type="text/javascript" src="__JS__/../libs/bootstrap.min.js"></script> -->
|
||||
<script type="text/javascript" src="__JS__/../libs/perfect-scrollbar.min.js"></script>
|
||||
{if Request()->header('host') === env_get('server.domain','')}
|
||||
<script type="text/javascript" src="__JS__/../libs/require.js" data-main="__JS__/default.js?v=2"></script>
|
||||
{else /}
|
||||
<script type="text/javascript" src="__JS__/../libs/require.js" data-main="__JS__/default.js?v={:Config('site.admin_static_version')}"></script>
|
||||
{/if}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,62 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form action="__SELF__" method="post" class="row form-horizontal">
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">最后结算</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control datatimepicker" name="last_jiaquan_time" value="{:date('Y-m-d H:i:s',cache('last_jiaquan_time'))}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">奖池金额</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" value="{$reward_sum}" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">份数</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" value="{$reward_count}" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">每份</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" value="{:formatAmount($reward_sum / $reward_count)}" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">用户总积分</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" value="{$user_score_total|formatAmount}" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">用户总余额</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" value="{$user_money_total|formatAmount}" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="form-group">
|
||||
<label for="username" class="control-label col-xs-12 col-sm-2">增加奖池金额</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" name="amount" value="0" placeholder="减少请输入负数" />
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="form-group">
|
||||
<label for="username" class="control-label col-xs-12 col-sm-2">加权</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" class="form-control" name="percent" value="0" placeholder="小于1是百分比,大于0是具体金额" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2"></label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">确 定</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<title>{:Config('site.name')}</title>
|
||||
<link rel="icon" href="{:Config('site.admin_logo','__IMG__/logo.png')}" type="image/ico">
|
||||
<link href="__CSS__/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="__CSS__/animate.css" rel="stylesheet" />
|
||||
<link href="__CSS__/materialdesignicons.min.css" rel="stylesheet" />
|
||||
<link href="__CSS__/style.min.css" rel="stylesheet" />
|
||||
<script type="text/javascript">
|
||||
var _c = {$config| json_encode=JSON_UNESCAPED_UNICODE|raw};
|
||||
_c['cdnurl']="";
|
||||
_c['version'] = Math.random();
|
||||
var require = {
|
||||
config:_c
|
||||
};
|
||||
var user = {$user|json_encode|raw};
|
||||
</script>
|
||||
</head>
|
||||
<div class="container-fluid p-t-15">
|
||||
{__CONTENT__}
|
||||
</div>
|
||||
{if Request()->header('host') === env_get('server.domain','')}
|
||||
<script type="text/javascript" src="__JS__/../libs/require.js" data-main="__JS__/default"></script>
|
||||
{else /}
|
||||
<script type="text/javascript" src="__JS__/../libs/require.js" data-main="__JS__/default.js?v={:Config('site.version')}"></script>
|
||||
{/if}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,21 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_add" class="btn btn-primary m-r-5 btn-add" data-url="{:url('insert')}" data-title="新增">
|
||||
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-success m-r-5 btn-disabled disabled btn-multi" data-params="status=1">
|
||||
<span class="mdi mdi-check" aria-hidden="true"></span>启用
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-warning m-r-5 btn-disabled disabled btn-multi" data-params="status=0">
|
||||
<span class="mdi mdi-block-helper" aria-hidden="true"></span>禁用
|
||||
</a>
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,137 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" action="__SELF__" method="post">
|
||||
<input type="hidden" name="id" value="{$row.id|null}" />
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">产品名称</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<input type="text" name="title" value="{$row.title|null}" class="form-control" data-rule="required;length(2~100)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">封面:</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input id="c-image" class="form-control" size="50" name="image" type="hidden" value="{$row.image|default=''}" data-tip="image">
|
||||
<ul class="list-inline clearfix lyear-uploads-pic" data-template="preview" id="p-image">
|
||||
<li nodelete class="col-xs-4 col-sm-3 col-md-2">
|
||||
<a class="pic-add faupload" style="height: auto;border: 0;" permission="app.admin.upload.image" id="add-pic-btn" href="#!" title="点击上传" data-input-id="c-image" data-mimetype="image/*" data-multiple="false" data-preview-id="p-image"></a>
|
||||
<a class="pic-add fachoose" style="height: auto;border: 0;display: none;" permission="app.admin.upload.attachment" id="choose-pic-btn" href="#!" title="选择文件" data-input-id="c-image" data-mimetype="image/*" data-multiple="false" data-preview-id="p-image"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">单价</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="text" name="price" value="{$row.price|null=3.5}" class="form-control" data-rule="required;range(0.5~)" />
|
||||
<div class="input-group-addon">¥</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">问卷总量</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="number" name="total" value="{$row.total|default=0}" class="form-control" min="0" data-rule="required;range(0~99)" />
|
||||
<div class="input-group-addon">天</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">每日分配</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="number" name="assign_count" value="{$row.assign_count|default=1}" class="form-control" min="0" data-rule="required;range(0~99)" />
|
||||
<div class="input-group-addon">份</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">加速包单价</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="text" name="accelerate_price" value="{$row.accelerate_price|default=0}" class="form-control" data-rule="required;range(0~)" />
|
||||
<div class="input-group-addon">¥</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">加速包分配次数</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="text" name="accelerate_assign_times" value="{$row.accelerate_assign_times|default=0}" class="form-control" data-rule="required;range(0~)" />
|
||||
<div class="input-group-addon">次</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">加速包每日分配</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="text" name="accelerate_assign_count" value="{$row.accelerate_assign_count|default=0}" class="form-control" data-rule="required;range(0~)" />
|
||||
<div class="input-group-addon">份</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">累计收益</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<div style="display: flex;align-items: center;">
|
||||
<input type="number" name="min_score" value="{$row.min_score|default=0}" class="form-control" min="0" data-rule="required;range(0~99)" />
|
||||
<span>-</span>
|
||||
<input type="number" name="max_score" value="{$row.max_score|default=0}" class="form-control" min="0" data-rule="required;range(0~99)" />
|
||||
</div>
|
||||
<div class="input-group-addon">积分</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">用户累计限购</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="number" name="user_quantity" value="{$row.user_quantity|default=0}" class="form-control" min="0" data-rule="required;range(0~99)" />
|
||||
<div class="input-group-addon">份</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-3">备注</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<textarea type="text" name="memo" class="form-control" >{$row.memo|null}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">销量</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="number" name="sales" value="{$row.sales|default=0}" disabled class="form-control" min="0" data-rule="required;range(0~99)" />
|
||||
<div class="input-group-addon">份</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-3">状态</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
{volist name="statusList" id="rvo"}
|
||||
<label class="lyear-radio radio-primary radio-inline">
|
||||
<input type="radio" name="status" {if $row.status == $key} checked{/if} value="{$key}">
|
||||
<span>{$rvo}</span>
|
||||
</label>
|
||||
{/volist}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-3"></label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">提交</button>
|
||||
<button type="reset" class="btn btn-warning m-r-5">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{include file="common/file_preview" id="preview"/}
|
||||
@@ -0,0 +1,12 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_add" class="btn btn-primary m-r-5 btn-add" data-url="{:url('insert')}" data-title="新增">
|
||||
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-success m-r-5 btn-disabled disabled btn-multi" data-params="status=1">
|
||||
<span class="mdi mdi-check" aria-hidden="true"></span>启用
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-warning m-r-5 btn-disabled disabled btn-multi" data-params="status=0">
|
||||
<span class="mdi mdi-block-helper" aria-hidden="true"></span>禁用
|
||||
</a>
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,126 @@
|
||||
{layout name="layout"}
|
||||
<style>
|
||||
.list-group{width: 100%;margin-bottom: 0;}
|
||||
.list-group-item{border: 0;display: flex;align-items: center;width: 100%;}
|
||||
.list-group-item b{margin-right: 5px;}
|
||||
.list-group-item .btn-danger.btn-delete{margin-left: 5px;}
|
||||
.question-item{border: 1px solid #ddd;margin-bottom: 10px;}
|
||||
</style>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" action="__SELF__" method="post">
|
||||
<input type="hidden" name="id" value="{$row.id|null}" />
|
||||
<input type="hidden" name="total" value="{$row.total|null}" />
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-3">分类:</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<select name="category_id" class="form-control selectpicker">
|
||||
{volist name="$categoryList" id="cvo"}
|
||||
<option value="{$cvo.id}" {if $row['category_id']== $cvo.id}selected{/if}>{$cvo.title}</option>
|
||||
{/volist}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-3">国别:</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<select name="country" class="form-control selectpicker">
|
||||
{volist name=":Config('site.questionnaire_country')" id="cvo"}
|
||||
<option value="{$key}" {if $row['country']== $key}selected{/if}>{$cvo}</option>
|
||||
{/volist}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">调查编号</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="text" name="title" value="{$row.title|null}" class="form-control" data-rule="required;length(2~100)" />
|
||||
<div class="input-group-btn"><button class="btn btn-info" id="refresh-title" type="button"><i class="mdi mdi-refresh"></i></button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">收益</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="text" name="score" value="{$row.score|default=5000}" class="form-control" data-rule="required;range(0~10000)" />
|
||||
<div class="input-group-addon">积分</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">开始时间</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<input type="text" name="start_time" value="{$row.start_time|null|datetime}" class="form-control datetimepicker" data-rule="required" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">结束时间</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<input type="text" name="end_time" value="{$row.end_time|null|datetime}" class="form-control datetimepicker" data-rule="required" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-3">问卷</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
<div class="question-editor">
|
||||
{volist name="row.body" id="q"}
|
||||
<dl class="list-group question-item">
|
||||
<dd class="list-group-item m-b-5 question-title">
|
||||
<b></b>
|
||||
<input type="text" name="body[][question]" value="{$q.question|null}" class="form-control" data-rule="required;length(2~100)" />
|
||||
<a class="btn btn-danger btn-delete"><i class="mdi mdi-close"></i></a>
|
||||
</dd>
|
||||
<dd class="list-group-item m-b-5">
|
||||
<dl class="list-group answer-list">
|
||||
{volist name="q.answer" id="a"}
|
||||
<dd class="list-group-item answer-item">
|
||||
<b></b>
|
||||
<input type="text" name="body[][answer][]" value="{$a|null}" class="form-control" data-rule="required;length(2~100)" />
|
||||
<a class="btn btn-danger btn-delete"><i class="mdi mdi-close"></i></a>
|
||||
</dd>
|
||||
{/volist}
|
||||
<dd class="m-l-15">
|
||||
<a href="javascript:;" class="btn btn-sm btn-success btn-append-answer m-t-5 m-l-15">
|
||||
<i class="fa fa-plus"></i> {:__('添加答案')}
|
||||
</a>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
{/volist}
|
||||
<div>
|
||||
<a href="javascript:;" class="btn btn-sm btn-info btn-append-question">
|
||||
<i class="fa fa-plus"></i> {:__('添加问题')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-3">状态</label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6">
|
||||
{if !$row.id}
|
||||
{assign name="row.status" value="1"}
|
||||
{/if}
|
||||
{volist name="statusList" id="rvo"}
|
||||
<label class="lyear-radio radio-primary radio-inline">
|
||||
<input type="radio" name="status" {if $row.status == $key} checked{/if} value="{$key}">
|
||||
<span>{$rvo}</span>
|
||||
</label>
|
||||
{/volist}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-3"></label>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">提交</button>
|
||||
<button type="reset" class="btn btn-warning m-r-5">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{include file="common/file_preview" id="preview"/}
|
||||
@@ -0,0 +1,22 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_add" class="btn btn-primary m-r-5 btn-add" data-url="{:url('insert')}" data-title="新增">
|
||||
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
|
||||
</a>
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
<div class="dropdown btn-group">
|
||||
<a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i>批量操作</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=1"><i class="fa fa-eye"></i> 审核通过</a></li>
|
||||
<li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=-1"><i class="fa fa-eye-slash"></i> 驳回</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,132 @@
|
||||
{layout name="layout"}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="form-horizontal" action="__SELF__" method="post">
|
||||
<input type="hidden" name="id" value="{$row.id|null}" />
|
||||
<!-- {$row|P}
|
||||
[amount] => 200.0000000000
|
||||
[network] => TRC-20
|
||||
[address] => TPwVbUEL6KxHVLd4cj7SrgaTBy81SB7yvt
|
||||
[extra] =>
|
||||
[from] =>
|
||||
[real_amount] =>
|
||||
[txid] =>
|
||||
[pay_time] =>
|
||||
[confirmations] => 0
|
||||
[result] =>
|
||||
[reason] =>
|
||||
[status] => -2
|
||||
[created_at] => 1749488299
|
||||
[updated_at] => 1749489208 -->
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">用户名</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" value="{$row.user.username|null}" class="form-control" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">网络</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" value="{$row.network|null}" class="form-control" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">收款地址</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="address" value="{$row.address|null}" class="form-control" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="type" class="control-label col-xs-12 col-sm-2">金额</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<input type="text" name="amount" value="{$row.amount|formatAmount}" class="form-control" />
|
||||
</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 col-md-6">
|
||||
<input type="text" name="real_amount" value="{$row.real_amount|formatAmount}" class="form-control" />
|
||||
</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 col-md-6">
|
||||
<input type="text" name="from" value="{$row.from|null}" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">txid</label>
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="text" name="txid" value="{$row.txid|null}" class="form-control" />
|
||||
<div class="input-group-btn">
|
||||
<button class="btn btn-primary" type="button" id="gettxid">
|
||||
<i class="mdi mdi-spin mdi-loading" style="display: none;"></i>
|
||||
获取
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</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 col-md-6">
|
||||
<input type="text" name="pay_time" value="{$row.pay_time|datetime='Y-m-d H:i:s'}" class="form-control datetimepicker" />
|
||||
</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 col-md-6">
|
||||
<input type="text" name="confirmations" value="{$row.confirmations|null=0}" class="form-control" />
|
||||
</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 col-md-6">
|
||||
<select name="result" class="form-control selectpicker">
|
||||
<option value="SUCCESS" {if $row.result == 'SUCCESS'}selected{/if}>成功</option>
|
||||
<option value="FAIL" {if $row.result == 'FAIL'}selected{/if}>失败</option>
|
||||
</select>
|
||||
</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 col-md-6">
|
||||
<input type="text" name="reason" value="{$row.reason|null}" class="form-control" />
|
||||
</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 col-md-6">-->
|
||||
<!-- <input type="text" name="created_at" value="{$row.created_at|null}" class="form-control" />-->
|
||||
<!-- </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 col-md-6">-->
|
||||
<!-- <input type="text" name="updated_at" value="{$row.updated_at|null}" class="form-control" />-->
|
||||
<!-- </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 col-md-6">
|
||||
{volist name="statusList" id="rvo"}
|
||||
<label class="lyear-radio radio-primary radio-inline">
|
||||
<input type="radio" name="status" {if $row.status == $key} checked{/if} value="{$key}" />
|
||||
<span>{$rvo}</span>
|
||||
</label>
|
||||
{/volist}
|
||||
</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 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">提交</button>
|
||||
<button type="reset" class="btn btn-warning m-r-5">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_add" class="btn btn-primary m-r-5 btn-add" data-url="{:url('insert')}" data-title="新增">
|
||||
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-success m-r-5 btn-disabled disabled btn-multi" data-params="status=1">
|
||||
<span class="mdi mdi-check" aria-hidden="true"></span>启用
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-warning m-r-5 btn-disabled disabled btn-multi" data-params="status=0">
|
||||
<span class="mdi mdi-block-helper" aria-hidden="true"></span>禁用
|
||||
</a>
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,122 @@
|
||||
{layout name="layout"}
|
||||
<style>
|
||||
.autocomplete-searchtitle {
|
||||
padding: 0px 8px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.autocomplete-suggestions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.autocomplete-searchtitle .media {
|
||||
border-bottom: 1px solid #eee;
|
||||
margin-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.autocomplete-searchtitle .media:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.autocomplete-searchtitle .media h4.media-heading {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.autocomplete-searchtitle .media .text-muted {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.autocomplete-searchtitle .media:hover {
|
||||
background: #fefefe;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.form-archives>.row>.col-md-3 {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.form-archives>.row>.col-md-3 .form-group .control-label {}
|
||||
}
|
||||
|
||||
.panel-intro {
|
||||
box-shadow: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<link href="__JS__/libs/jquery-tags-input/jquery.tagsinput.min.css?v={$Think.config.site.version}" rel="stylesheet">
|
||||
<!-- <script src="/static/libs/froala/js/languages/zh_cn.js"></script> -->
|
||||
<script type="text/html" id="headertpl">
|
||||
<div class="px-2">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="alert" style="border-radius: 0;color: #0084ff; background: rgba(0, 132, 255, 0.1);margin-bottom:0;">
|
||||
共找到以下几篇相关文章:
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="itemtpl">
|
||||
<div class="media">
|
||||
<a class="" href="<%=item.url%>" target="_blank">
|
||||
<div class="media-left">
|
||||
<img src="<%=item.image%>" style="width: 50px; height: 50px;">
|
||||
</div>
|
||||
|
||||
<div class="media-body">
|
||||
<h4 class="media-heading"><%=#replace(item.title)%></h4>
|
||||
<div class="text-muted"><%=#formatter.status.call(context, item.status, item)%></div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
|
||||
|
||||
<input name="id" type="hidden" value="{$row.id}">
|
||||
<input name="category_id" type="hidden" value="12">
|
||||
<input name="type" type="hidden" value="{$row.type}">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-12 col-sm-2">标题:</label>
|
||||
<div class="col-xs-12 col-sm-8">
|
||||
<input id="c-title" data-rule="required;length(2~100)" class="form-control" name="title"
|
||||
type="text" value="{$row.title|htmlentities}" data-suggestion-url="{:url('archives/suggestion')}">
|
||||
</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">
|
||||
<input id="c-name" data-rule="required;length(2~100)" class="form-control" name="name"
|
||||
type="text" value="{$row.name|htmlentities}" data-suggestion-url="{:url('archives/suggestion')}">
|
||||
</div>
|
||||
</div>
|
||||
<!--@formatter:off-->
|
||||
<div class="form-group" data-field="content">
|
||||
<label for="c-content" class="control-label col-xs-12 col-sm-2">正文:</label>
|
||||
<div class="col-xs-12 col-sm-8">
|
||||
<textarea id="c-content" data-rule="required;length(100~20000)" class="form-control editor" name="content" data-role="editor"
|
||||
rows="15">{$row.content}</textarea>
|
||||
<div style="margin-top:5px;">
|
||||
<a href="javascript:" class="btn btn-xs btn-info btn-getimage" data-toggle="tooltip"
|
||||
data-title="将提取内容第一张图作为缩略图"><i class="fa fa-camera"></i> {:__('提取缩略图')}</a>
|
||||
<a href="javascript:" class="btn btn-xs btn-info btn-getimages" data-toggle="tooltip"
|
||||
data-title="将提取内容前4张图作为组图"><i class="fa fa-camera"></i> {:__('提取组图')}</a>
|
||||
</div>
|
||||
</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 col-md-6 layer-footer">
|
||||
<button type="submit" class="btn btn-primary m-r-5">提交</button>
|
||||
<button type="reset" class="btn btn-warning m-r-5">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{include file="common/file_preview" id="preview"/}
|
||||
@@ -0,0 +1,21 @@
|
||||
{layout name="layout"}
|
||||
<div class="toolbar" class="toolbar-btn-action">
|
||||
<a id="btn_add" class="btn btn-primary m-r-5 btn-add" data-url="{:url('insert')}" data-title="新增">
|
||||
<span class="mdi mdi-plus" aria-hidden="true"></span>新增
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-success m-r-5 btn-disabled disabled btn-multi" data-params="status=1">
|
||||
<span class="mdi mdi-check" aria-hidden="true"></span>启用
|
||||
</a>
|
||||
<a id="btn_edit" class="btn btn-warning m-r-5 btn-disabled disabled btn-multi" data-params="status=0">
|
||||
<span class="mdi mdi-block-helper" aria-hidden="true"></span>禁用
|
||||
</a>
|
||||
<a id="btn_delete" class="btn btn-danger btn-del btn-disabled disabled">
|
||||
<span class="mdi mdi-window-close" aria-hidden="true"></span>删除
|
||||
</a>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table id="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user