Compare commits

...

6 Commits

Author SHA1 Message Date
Karson 211a54b4c3 优化会员注册模块
优化mobilelogin默认用户名和默认昵称
2025-06-06 23:47:22 +08:00
Karson 4e48b3a736 优化gruntfile在Win下的兼容问题 2025-06-06 23:45:51 +08:00
Karson 6ceb59b9f1 优化附件列表图片生成
优化referrer默认值
2025-06-06 23:45:21 +08:00
Karson 7754cf4c3c 优化管理员日志记录 2025-06-06 23:43:46 +08:00
Karson d6ef94c573 优化列表表格导出 2025-06-06 23:43:08 +08:00
Karson 5595954d6d 优化基类排序字段配置 2025-06-06 23:42:16 +08:00
13 changed files with 124 additions and 62 deletions

View File

@ -36,7 +36,7 @@ module.exports = function (grunt) {
var matches = content.match(pattern);
if (matches) {
if (type === 'js') {
var data = matches[1].replaceAll(/(urlArgs|baseUrl):(.*)\n/gi, '');
var data = matches[1].replace(/(urlArgs|baseUrl):[^\r\n]*[\r\n]*/gi, '');
const parse = require('parse-config-file'), fs = require('fs');
require('jsonminify');

View File

@ -2,6 +2,7 @@
namespace app\admin\controller;
use app\admin\model\AdminLog;
use app\common\controller\Backend;
use app\common\exception\UploadException;
use app\common\library\Upload;
@ -73,6 +74,9 @@ class Ajax extends Backend
//必须还原upload配置,否则分片及cdnurl函数计算错误
Config::load(APP_PATH . 'extra/upload.php', 'upload');
//自定义日志标题
AdminLog::setTitle(__('Upload'));
$chunkid = $this->request->post("chunkid");
if ($chunkid) {
if (!Config::get('upload.chunking')) {

View File

@ -65,7 +65,7 @@ class Attachment extends Backend
->order($sort, $order)
->paginate($limit);
$cdnurl = preg_replace("/\/(\w+)\.php$/i", '', $this->request->root());
$cdnurl = preg_replace("/\/(\w+)\.php$/i", '', $this->request->root(true));
foreach ($list as $k => &$v) {
$v['fullurl'] = ($v['storage'] == 'local' ? $cdnurl : $this->view->config['upload']['cdnurl']) . $v['url'];
}

View File

@ -23,6 +23,7 @@ return [
'All' => '全部',
'Detail' => '详情',
'Multi' => '批量更新',
'Dragsort' => '排序',
'Setting' => '配置',
'Move' => '移动',
'Name' => '名称',

View File

@ -21,6 +21,7 @@ class AdminLog extends Model
//忽略的链接正则列表
protected static $ignoreRegex = [
'/^(.*)\/(selectpage|index)$/i',
'/^(.*)\/addon\/get_table_list$/i',
];
public static function setTitle($title)
@ -69,14 +70,7 @@ class AdminLog extends Model
$content = self::getPureContent($content);
}
$title = $title ?: self::$title;
if (!$title) {
$title = [];
$breadcrumb = Auth::instance()->getBreadcrumb($path);
foreach ($breadcrumb as $k => $v) {
$title[] = $v['title'];
}
$title = implode(' / ', $title);
}
$title = $title ?: implode(' / ', array_column(Auth::instance()->getBreadcrumb($path), 'title'));
self::create([
'title' => $title,
'content' => !is_scalar($content) ? json_encode($content, JSON_UNESCAPED_UNICODE) : $content,

View File

@ -2,7 +2,7 @@
<title>{$title|default=''}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta name="renderer" content="webkit">
<meta name="referrer" content="never">
<meta name="referrer" content="no-referrer">
<meta name="robots" content="noindex, nofollow">
<link rel="shortcut icon" href="__CDN__/assets/img/favicon.ico" />

View File

@ -102,6 +102,11 @@ class Backend extends Controller
*/
protected $excludeFields = "";
/**
* 排序字段
*/
protected $dragsortFields = 'weigh';
/**
* 导入文件首行类型
* 支持comment/name

View File

@ -133,52 +133,51 @@ class Auth
*/
public function register($username, $password, $email = '', $mobile = '', $extend = [])
{
// 检测用户名、昵称、邮箱、手机号是否存在
if (User::getByUsername($username)) {
$this->setError('Username already exist');
return false;
}
if (User::getByNickname($username)) {
$this->setError('Nickname already exist');
return false;
}
if ($email && User::getByEmail($email)) {
$this->setError('Email already exist');
return false;
}
if ($mobile && User::getByMobile($mobile)) {
$this->setError('Mobile already exist');
return false;
}
$ip = request()->ip();
$time = time();
$data = [
'username' => $username,
'password' => $password,
'email' => $email,
'mobile' => $mobile,
'level' => 1,
'score' => 0,
'avatar' => '',
];
$params = array_merge($data, [
'nickname' => preg_match("/^1[3-9]{1}\d{9}$/", $username) ? substr_replace($username, '****', 3, 4) : $username,
'salt' => Random::alnum(),
'jointime' => $time,
'joinip' => $ip,
'logintime' => $time,
'loginip' => $ip,
'prevtime' => $time,
'status' => 'normal'
]);
$params['password'] = $this->getEncryptPassword($password, $params['salt']);
$params = array_merge($params, $extend);
//账号注册时需要开启事务,避免出现垃圾数据
Db::startTrans();
try {
// 检测用户名、邮箱、手机号是否存在
if ($username && User::checkExists('username', $username)) {
$this->setError('Username already exist');
Db::rollback();
return false;
}
if ($email && User::checkExists('email', $email)) {
$this->setError('Email already exist');
Db::rollback();
return false;
}
if ($mobile && User::checkExists('mobile', $mobile)) {
$this->setError('Mobile already exist');
Db::rollback();
return false;
}
$ip = request()->ip();
$time = time();
$data = [
'username' => $username,
'password' => $password,
'email' => $email,
'mobile' => $mobile,
'level' => 1,
'score' => 0,
'avatar' => '',
];
$params = array_merge($data, [
'nickname' => preg_match("/^1[3-9]{1}\d{9}$/", $username) ? substr_replace($username, '****', 3, 4) : $username,
'salt' => Random::alnum(),
'jointime' => $time,
'joinip' => $ip,
'logintime' => $time,
'loginip' => $ip,
'prevtime' => $time,
'status' => 'normal'
]);
$params['password'] = $this->getEncryptPassword($password, $params['salt']);
$params = array_merge($params, $extend);
$user = User::create($params, true);
$this->_user = User::get($user->id);

View File

@ -82,6 +82,16 @@ class User extends Model
return $value;
}
/**
* 判断指定字段的值是否存在
* @param string $field 字段名
* @param string $value 字段值
*/
public static function checkExists($field, $value)
{
return self::lock(true)->where($field, $value)->find();
}
/**
* 变更会员余额
* @param int $money 余额

View File

@ -299,7 +299,7 @@ return [
'loginip_check' => true,
//登录页默认背景图
'login_background' => "",
//是否启用简洁导航
//是否启用简洁导航,如同时启用多级菜单导航,简洁导航将失效
'simplenav' => false,
//是否启用多级菜单导航
'multiplenav' => false,

View File

@ -224,9 +224,9 @@ class User extends Frontend
//如果已经有账号则直接登录
$ret = $this->auth->direct($user->id);
} else {
$ret = $this->auth->register($mobile, Random::alnum(), '', $mobile, []);
//如果是手机号首次注册则直接设定为已验证
$this->auth->getUser()->save(['verification' => ['email' => 0, 'mobile' => 1]]);
$username = \fast\Random::username();
$nickname = '用户' . substr($mobile, -4);
$ret = $this->auth->register($username, Random::alnum(), '', $mobile, ['nickname' => $nickname, 'verification' => ['mobile' => 1]]);
}
if ($ret) {
Sms::flush($mobile, 'mobilelogin');
@ -321,6 +321,9 @@ class User extends Frontend
return $this->view->fetch();
}
/**
* 附件管理
*/
public function attachment()
{
//设置过滤方法
@ -388,6 +391,9 @@ class User extends Frontend
return $this->view->fetch();
}
/**
* 用户协议
*/
public function agreement()
{
$this->view->assign('title', __('User agreement'));

View File

@ -89,6 +89,15 @@ class Random
}
}
/**
* 生成随机用户名
* @return string
*/
public static function username(): string
{
return 'user_'.bin2hex(random_bytes(8)) . substr(uniqid(), -8);
}
/**
* 获取全球唯一标识
* @return string

View File

@ -18,11 +18,45 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
exportTypes: ['json', 'xml', 'csv', 'txt', 'doc', 'excel'],
exportOptions: {
fileName: 'export_' + Moment().format("YYYY-MM-DD"),
preventInjection: false,
htmlContent: false,
mso: {
onMsoNumberFormat: function (cell, row, col) {
return !isNaN($(cell).text()) ? '\\@' : '';
},
var text = $(cell).text().trim();
return text !== '' && !isNaN(text) ? '\\@' : '';
}
},
onCellHtmlData: function (cell, row, col, html) {
var text = cell.text();
var path = location.pathname.match(/\/[^\/]+/)[0];
path = path.indexOf(".php") > -1 ? path : '';
if (cell.is("th")) {
return text;
} else if ($("input", cell).length > 0) {
var inputArr = [];
$("input", cell).each(function (key, item) {
inputArr.push($(item).val());
});
return inputArr.join(",");
} else if ($(">a", cell).length > 0) {
var anchorArr = [];
$(">a", cell).each(function (key, item) {
if (html.match(/ajax\/icon\?suffix=/)) {
anchorArr.push($(item).attr("href"));
} else if ($(item).find("i.fa-toggle-on").length > 0) {
anchorArr.push($(item).data("value"));
} else {
if ($(">img", item).length > 0 && !$.fn.bootstrapTable.defaults.exportOptions.htmlContent) {
anchorArr.push($(">img", item).prop("src").replace(new RegExp(path, "g"), ''));
} else {
anchorArr.push($(item).html().replace(new RegExp(path, "g"), ''));
}
}
});
return anchorArr.join(",");
} else {
return html.replace(new RegExp(path, "g"), '');
}
},
ignoreColumn: [0, 'operate'] //默认不导出第一列(checkbox)与操作(operate)列
},
@ -828,7 +862,7 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table
if (typeof this.disable !== "undefined") {
disable = typeof this.disable === "function" ? this.disable.call(this, value, row, index) : this.disable;
}
return "<a href='javascript:;' data-toggle='tooltip' title='" + __('Click to toggle') + "' class='btn-change " + (disable ? 'btn disabled no-padding' : '') + "' data-index='" + index + "' data-id='"
return "<a href='javascript:;' data-toggle='tooltip' data-value='" + value + "' title='" + __('Click to toggle') + "' class='btn-change " + (disable ? 'btn disabled no-padding' : '') + "' data-index='" + index + "' data-id='"
+ row[pk] + "' " + (url ? "data-url='" + url + "'" : "") + (confirm ? "data-confirm='" + confirm + "'" : "") + " data-params='" + this.field + "=" + (value == yes ? no : yes) + "'><i class='fa fa-toggle-on text-success text-" + color + " " + (value == yes ? '' : 'fa-flip-horizontal text-gray') + " fa-2x'></i></a>";
},
url: function (value, row, index) {