diff --git a/application/admin/command/Crud.php b/application/admin/command/Crud.php index 2d4d543a..e18dd4f5 100644 --- a/application/admin/command/Crud.php +++ b/application/admin/command/Crud.php @@ -63,7 +63,7 @@ class Crud extends Command * 以指定字符结尾的字段格式化函数 */ protected $fieldFormatterSuffix = [ - 'status' => 'status', + 'status' => ['type' => ['varchar'], 'name' => 'status'], 'icon' => 'icon', 'flag' => 'flag', 'url' => 'url', @@ -672,7 +672,7 @@ class Crud extends Command $javascriptList[] = "{checkbox: true}"; } //构造JS列信息 - $javascriptList[] = $this->getJsColumn($field, $v['DATA_TYPE'], $inputType && in_array($inputType, ['select', 'checkbox', 'radio']) ? '_text' : ''); + $javascriptList[] = $this->getJsColumn($field, $v['DATA_TYPE'], $inputType && in_array($inputType, ['select', 'checkbox', 'radio']) ? '_text' : '', $itemArr); //排序方式,如果有指定排序字段,否则按主键排序 $order = $field == $this->sortField ? $this->sortField : $order; @@ -1197,9 +1197,12 @@ EOD; /** * 获取JS列数据 * @param string $field + * @param string $datatype + * @param string $extend + * @param array $itemArr * @return string */ - protected function getJsColumn($field, $datatype = '', $extend = '') + protected function getJsColumn($field, $datatype = '', $extend = '', $itemArr = []) { $lang = mb_ucfirst($field); $formatter = ''; @@ -1236,10 +1239,41 @@ EOD; $formatter = 'label'; } } + foreach ($itemArr as $k => &$v) + { + if (substr($v, 0, 3) !== '__(') + $v = "__('" . $v . "')"; + } + unset($v); + $searchList = json_encode($itemArr); + $searchList = str_replace(['":"', '"}', ')","'], ['":', '}', '),"'], $searchList); + if ($itemArr && !$extend) + { + $html .= ", searchList: " . $searchList; + } + echo $datatype, "\n"; + if (in_array($datatype, ['date', 'datetime']) || $formatter === 'datetime') + { + $html .= ", operate:'RANGE', addclass:'datetimerange'"; + } + else if (in_array($datatype,['float', 'double', 'decimal'])) + { + $html .= ", operate:'BETWEEN'"; + } if ($formatter) $html .= ", formatter: Table.api.formatter." . $formatter . "}"; else $html .= "}"; + if ($extend) + { + $origin = str_repeat(" ", 24) . "{field: '{$field}', title: __('{$lang}'), visible:false"; + if ($searchList) + { + $origin .= ", searchList: " . $searchList; + } + $origin .= "}"; + $html = $origin . ",\n" . $html; + } return $html; } diff --git a/application/admin/command/Crud/stubs/index.stub b/application/admin/command/Crud/stubs/index.stub index dd11a92d..b06eaae8 100644 --- a/application/admin/command/Crud/stubs/index.stub +++ b/application/admin/command/Crud/stubs/index.stub @@ -6,7 +6,12 @@
- {:build_toolbar()} + + {:__('Add')} + {:__('Edit')} + {:__('Delete')} + {:__('Import')} + + \ No newline at end of file diff --git a/application/admin/view/user/group/edit.html b/application/admin/view/user/group/edit.html new file mode 100644 index 00000000..f11eb483 --- /dev/null +++ b/application/admin/view/user/group/edit.html @@ -0,0 +1,40 @@ +
+ +
+ +
+ +
+
+
+ +
+ + + +
+
+
+
+ +
+ +
+ {foreach name="statusList" item="vo"} + + {/foreach} +
+ +
+
+ +
+ diff --git a/application/admin/view/user/group/index.html b/application/admin/view/user/group/index.html new file mode 100644 index 00000000..88c40619 --- /dev/null +++ b/application/admin/view/user/group/index.html @@ -0,0 +1,28 @@ +
+ {:build_heading()} + +
+
+
+
+ + +
+
+
+ +
+
+
diff --git a/application/admin/view/user/rule/add.html b/application/admin/view/user/rule/add.html new file mode 100644 index 00000000..9393e971 --- /dev/null +++ b/application/admin/view/user/rule/add.html @@ -0,0 +1,52 @@ +
+
+ +
+ {:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')])} +
+
+ +
+ +
+ {:build_select('row[pid]', $ruledata, null, ['class'=>'form-control', 'required'=>''])} +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])} +
+
+ +
diff --git a/application/admin/view/user/rule/edit.html b/application/admin/view/user/rule/edit.html new file mode 100644 index 00000000..345b9672 --- /dev/null +++ b/application/admin/view/user/rule/edit.html @@ -0,0 +1,52 @@ +
+ +
+ +
+ {:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')], $row['ismenu'])} +
+
+
+ +
+ {:build_select('row[pid]', $ruledata, $row['pid'], ['class'=>'form-control', 'required'=>''])} +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])} +
+
+ +
diff --git a/application/admin/view/user/rule/index.html b/application/admin/view/user/rule/index.html new file mode 100644 index 00000000..ed91b6ef --- /dev/null +++ b/application/admin/view/user/rule/index.html @@ -0,0 +1,28 @@ +
+ {:build_heading()} + +
+
+
+
+ + +
+
+
+ +
+
+
diff --git a/application/admin/view/user/user/edit.html b/application/admin/view/user/user/edit.html new file mode 100644 index 00000000..a50ed2da --- /dev/null +++ b/application/admin/view/user/user/edit.html @@ -0,0 +1,144 @@ +
+ +
+ +
+ {$groupList} +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ +
+ + +
+ +
+
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + {:build_radios('row[gender]', ['1'=>__('Male'), '0'=>__('Female')], $row['gender'])} +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])} +
    +
    + +
    diff --git a/application/admin/view/user/user/index.html b/application/admin/view/user/user/index.html new file mode 100644 index 00000000..00308e8d --- /dev/null +++ b/application/admin/view/user/user/index.html @@ -0,0 +1,28 @@ +
    + {:build_heading()} + +
    +
    +
    +
    +
    + {:build_toolbar('refresh,edit,del')} + +
    + +
    +
    +
    + +
    +
    +
    diff --git a/application/api/controller/Common.php b/application/api/controller/Common.php index cbdc33df..31d30e69 100644 --- a/application/api/controller/Common.php +++ b/application/api/controller/Common.php @@ -5,6 +5,7 @@ namespace app\api\controller; use app\api\model\Area; use app\common\controller\Api; use fast\Version; +use fast\Random; use think\Config; /** @@ -13,7 +14,7 @@ use think\Config; class Common extends Api { - protected $noNeedLogin = '*'; + protected $noNeedLogin = ['init']; protected $noNeedRight = '*'; public function _initialize() @@ -24,8 +25,9 @@ class Common extends Api /** * 加载初始化 * - * 必选参数:version
    - * 可选参数:lng,lat + * @param string $version 版本号 + * @param string $lng 经度 + * @param string $lat 纬度 */ public function init() { @@ -47,4 +49,94 @@ class Common extends Api } } + /** + * 上传文件 + * + * @param File $file 文件流 + */ + public function upload() + { + $file = $this->request->file('file'); + if (empty($file)) + { + $this->error(__('No file upload or server upload limit exceeded')); + } + + //判断是否已经存在附件 + $sha1 = $file->hash(); + + $upload = Config::get('upload'); + + preg_match('/(\d+)(\w+)/', $upload['maxsize'], $matches); + $type = strtolower($matches[2]); + $typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3]; + $size = (int) $upload['maxsize'] * pow(1024, isset($typeDict[$type]) ? $typeDict[$type] : 0); + $fileInfo = $file->getInfo(); + $suffix = strtolower(pathinfo($fileInfo['name'], PATHINFO_EXTENSION)); + $suffix = $suffix ? $suffix : 'file'; + + $mimetypeArr = explode(',', $upload['mimetype']); + $typeArr = explode('/', $fileInfo['type']); + //验证文件后缀 + if ($upload['mimetype'] !== '*' && !in_array($suffix, $mimetypeArr) && !in_array($fileInfo['type'], $mimetypeArr) && !in_array($typeArr[0] . '/*', $mimetypeArr)) + { + $this->error(__('Uploaded file format is limited')); + } + $replaceArr = [ + '{year}' => date("Y"), + '{mon}' => date("m"), + '{day}' => date("d"), + '{hour}' => date("H"), + '{min}' => date("i"), + '{sec}' => date("s"), + '{random}' => Random::alnum(16), + '{random32}' => Random::alnum(32), + '{filename}' => $suffix ? substr($fileInfo['name'], 0, strripos($fileInfo['name'], '.')) : $fileInfo['name'], + '{suffix}' => $suffix, + '{.suffix}' => $suffix ? '.' . $suffix : '', + '{filemd5}' => md5_file($fileInfo['tmp_name']), + ]; + $savekey = $upload['savekey']; + $savekey = str_replace(array_keys($replaceArr), array_values($replaceArr), $savekey); + + $uploadDir = substr($savekey, 0, strripos($savekey, '/') + 1); + $fileName = substr($savekey, strripos($savekey, '/') + 1); + // + $splInfo = $file->validate(['size' => $size])->move(ROOT_PATH . '/public' . $uploadDir, $fileName); + if ($splInfo) + { + $imagewidth = $imageheight = 0; + if (in_array($suffix, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf'])) + { + $imgInfo = getimagesize($splInfo->getPathname()); + $imagewidth = isset($imgInfo[0]) ? $imgInfo[0] : $imagewidth; + $imageheight = isset($imgInfo[1]) ? $imgInfo[1] : $imageheight; + } + $params = array( + 'filesize' => $fileInfo['size'], + 'imagewidth' => $imagewidth, + 'imageheight' => $imageheight, + 'imagetype' => $suffix, + 'imageframes' => 0, + 'mimetype' => $fileInfo['type'], + 'url' => $uploadDir . $splInfo->getSaveName(), + 'uploadtime' => time(), + 'storage' => 'local', + 'sha1' => $sha1, + ); + $attachment = model("attachment"); + $attachment->data(array_filter($params)); + $attachment->save(); + \think\Hook::listen("upload_after", $attachment); + $this->success(__('Upload successful'), [ + 'url' => $uploadDir . $splInfo->getSaveName() + ]); + } + else + { + // 上传失败获取错误信息 + $this->error($file->getError()); + } + } + } diff --git a/application/api/controller/Demo.php b/application/api/controller/Demo.php index 160fe58b..7af13512 100644 --- a/application/api/controller/Demo.php +++ b/application/api/controller/Demo.php @@ -22,8 +22,6 @@ class Demo extends Api /** * 无需登录的接口 * - * 必选参数:无
    - * 可选参数:无 */ public function test1() { @@ -33,8 +31,6 @@ class Demo extends Api /** * 需要登录的接口 * - * 必选参数:token
    - * 可选参数:无 */ public function test2() { @@ -44,8 +40,6 @@ class Demo extends Api /** * 需要登录且需要验证有相应组的权限 * - * 必选参数:token
    - * 可选参数:无 */ public function test3() { diff --git a/application/api/controller/Index.php b/application/api/controller/Index.php index ecc0fd96..0bc9d9af 100644 --- a/application/api/controller/Index.php +++ b/application/api/controller/Index.php @@ -16,8 +16,6 @@ class Index extends Api /** * 首页 * - * 必选参数:无
    - * 可选参数:lng,lat */ public function index() { diff --git a/application/api/controller/Sms.php b/application/api/controller/Sms.php index 1be29b92..99c7deb4 100644 --- a/application/api/controller/Sms.php +++ b/application/api/controller/Sms.php @@ -23,35 +23,40 @@ class Sms extends Api /** * 发送验证码 * - * 必选参数:mobile,type
    - * 可选参数:无 + * @param string $mobile 手机号 + * @param string $event 事件名称 */ public function send() { $mobile = $this->request->request("mobile"); - $type = $this->request->request("type"); - $type = $type ? $type : 'register'; + $event = $this->request->request("event"); + $event = $event ? $event : 'register'; - $last = Smslib::get($mobile, $type); + $last = Smslib::get($mobile, $event); if ($last && time() - $last['createtime'] < 60) { $this->error(__('发送频繁')); } - if ($type) + if ($event) { $userinfo = User::getByMobile($mobile); - if ($type == 'register' && $userinfo) + if ($event == 'register' && $userinfo) { //已被注册 $this->error(__('已被注册')); } - else if (in_array($type, ['changepwd', 'resetpwd']) && !$userinfo) + else if (in_array($event, ['changemobile']) && $userinfo) + { + //被占用 + $this->error(__('已被占用')); + } + else if (in_array($event, ['changepwd', 'resetpwd']) && !$userinfo) { //未注册 $this->error(__('未注册')); } } - $ret = Smslib::send($mobile, '', $type); + $ret = Smslib::send($mobile, NULL, $event); if ($ret) { $this->success(__('发送成功')); @@ -65,31 +70,37 @@ class Sms extends Api /** * 检测验证码 * - * 必选参数:mobile,type,captcha
    - * 可选参数:无 + * @param string $mobile 手机号 + * @param string $event 事件名称 + * @param string $captcha 验证码 */ public function check() { $mobile = $this->request->request("mobile"); - $type = $this->request->request("type"); - $type = $type ? $type : 'register'; + $event = $this->request->request("event"); + $event = $event ? $event : 'register'; $captcha = $this->request->request("captcha"); - if ($type) + if ($event) { $userinfo = User::getByMobile($mobile); - if ($type == 'register' && $userinfo) + if ($event == 'register' && $userinfo) { //已被注册 $this->error(__('已被注册')); } - else if (in_array($type, ['changepwd', 'resetpwd']) && !$userinfo) + else if (in_array($event, ['changemobile']) && $userinfo) + { + //被占用 + $this->error(__('已被占用')); + } + else if (in_array($event, ['changepwd', 'resetpwd']) && !$userinfo) { //未注册 $this->error(__('未注册')); } } - $ret = Smslib::check($mobile, $captcha, $type); + $ret = Smslib::check($mobile, $captcha, $event); if ($ret) { $this->success(__('成功')); diff --git a/application/api/controller/User.php b/application/api/controller/User.php new file mode 100644 index 00000000..b3a63451 --- /dev/null +++ b/application/api/controller/User.php @@ -0,0 +1,330 @@ +success('', ['welcome' => $this->auth->nickname]); + } + + /** + * 会员登录 + * + * @param string $account 账号 + * @param string $password 密码 + */ + public function login() + { + $account = $this->request->request('account'); + $password = $this->request->request('password'); + if (!$account || !$password) + { + $this->error(__('Invalid parameters')); + } + $ret = $this->auth->login($account, $password); + if ($ret) + { + $data = ['userinfo' => $this->auth->getUserinfo()]; + $this->success(__('Logged in successful'), $data); + } + else + { + $this->error($this->auth->getError()); + } + } + + /** + * 手机验证码登录 + * + * @param string $mobile 手机号 + * @param string $captcha 验证码 + */ + public function mobilelogin() + { + $mobile = $this->request->request('mobile'); + $captcha = $this->request->request('captcha'); + if (!$mobile || !$captcha) + { + $this->error(__('Invalid parameters')); + } + if (!Validate::regex($mobile, "^1\d{10}$")) + { + $this->error(__('Mobile incorrect')); + } + if (!Sms::check($mobile, $captcha, 'mobilelogin')) + { + $this->error(__('Captcha invalid')); + } + $user = \app\common\model\User::getByMobile($mobile); + if ($user) + { + //如果已经有账号则直接登录 + $ret = $this->auth->direct($user->id); + } + else + { + $ret = $this->auth->register($mobile, Random::alnum(), '', $mobile, []); + } + if ($ret) + { + Sms::flush($mobile, 'mobilelogin'); + $data = ['userinfo' => $this->auth->getUserinfo()]; + $this->success(__('Logged in successful'), $data); + } + else + { + $this->error($this->auth->getError()); + } + } + + /** + * 注册会员 + * + * @param string $username 用户名 + * @param string $password 密码 + * @param string $email 邮箱 + * @param string $mobile 手机号 + */ + public function register() + { + $username = $this->request->request('username'); + $password = $this->request->request('password'); + $email = $this->request->request('email'); + $mobile = $this->request->request('mobile'); + if (!$username || !$password) + { + $this->error(__('Invalid parameters')); + } + if ($email && !Validate::is($email, "email")) + { + $this->error(__('Email incorrect')); + } + if ($mobile && !Validate::regex($mobile, "^1\d{10}$")) + { + $this->error(__('Mobile incorrect')); + } + $ret = $this->auth->register($username, $password, $email, $mobile, []); + if ($ret) + { + $data = ['userinfo' => $this->auth->getUserinfo()]; + $this->success(__('Sign up successful'), $data); + } + else + { + $this->error($this->auth->getError()); + } + } + + /** + * 注销登录 + */ + public function logout() + { + $this->auth->logout(); + $this->success(__('Logout successful')); + } + + /** + * 修改会员个人信息 + * + * @param string $avatar 头像地址 + * @param string $username 用户名 + * @param string $nickname 昵称 + * @param string $bio 个人简介 + */ + public function profile() + { + $user = $this->auth->getUser(); + $username = $this->request->request('username'); + $nickname = $this->request->request('nickname'); + $bio = $this->request->request('bio'); + $avatar = $this->request->request('avatar'); + $exists = \app\common\model\User::where('username', $username)->where('id', '<>', $this->auth->id)->find(); + if ($exists) + { + $this->error(__('Username already exists')); + } + $user->username = $username; + $user->nickname = $nickname; + $user->bio = $bio; + $user->avatar = $avatar; + $user->save(); + $this->success(); + } + + /** + * 修改邮箱 + * + * @param string $email 邮箱 + */ + public function changeemail() + { + $user = $this->auth->getUser(); + $email = $this->request->post('email'); + if (!$email) + { + $this->error(__('Invalid parameters')); + } + if (!Validate::is($email, "email")) + { + $this->error(__('Mobile incorrect')); + } + if (\app\common\model\User::where('email', $email)->where('id', '<>', $user->id)->find()) + { + $this->error(__('Email already exists')); + } + $verification = $user->verification; + $verification->email = 0; + $user->verification = $verification; + $user->email = $email; + $user->save(); + $time = time(); + $code = ['id' => $user->id, 'time' => $time, 'key' => md5(md5($user->id . $user->email . $time) . $user->salt)]; + $code = base64_encode(http_build_query($code)); + $url = url("index/user/activeemail", ['code' => $code], true, true); + $message = __('Verify email') . ":{$url}"; + Email::instance()->to($email)->subject(__('Verify email'))->message($message)->send(); + $this->success(); + } + + /** + * 修改手机号 + * + * @param string $email 手机号 + * @param string $captcha 验证码 + */ + public function changemobile() + { + $user = $this->auth->getUser(); + $mobile = $this->request->request('mobile'); + $captcha = $this->request->request('captcha'); + if (!$mobile || !$captcha) + { + $this->error(__('Invalid parameters')); + } + if (!Validate::regex($mobile, "^1\d{10}$")) + { + $this->error(__('Mobile incorrect')); + } + if (\app\common\model\User::where('mobile', $mobile)->where('id', '<>', $user->id)->find()) + { + $this->error(__('Mobile already exists')); + } + $result = Sms::check($mobile, $captcha, 'changemobile'); + if (!$result) + { + $this->error(__('Captcha invalid')); + } + $verification = $user->verification; + $verification->mobile = 1; + $user->verification = $verification; + $user->mobile = $mobile; + $user->save(); + + Sms::flush($mobile, 'changemobile'); + $this->success(); + } + + /** + * 第三方登录 + * + * @param string $platform 平台名称 + * @param string $code Code码 + */ + public function third() + { + $url = url('user/index'); + $platform = $this->request->request("platform"); + $code = $this->request->request("code"); + $config = get_addon_config('third'); + if (!$config || !isset($config[$platform])) + { + $this->error(__('Invalid parameters')); + } + $app = new \addons\third\library\Application($config); + //通过code换access_token和绑定会员 + $result = $app->{$platform}->getUserInfo(['code' => $code]); + if ($result) + { + $loginret = \addons\third\library\Service::connect($platform, $result); + if ($loginret) + { + $data = [ + 'userinfo' => $this->auth->getUserinfo(), + 'thirdinfo' => $result + ]; + $this->success(__('Logged in successful'), $data); + } + } + $this->error(__('Operation failed'), $url); + } + + /** + * 重置密码 + * + * @param string $mobile 手机号 + * @param string $newpassword 新密码 + * @param string $captcha 验证码 + */ + public function resetpwd() + { + $mobile = $this->request->request("mobile"); + $newpassword = $this->request->request("newpassword"); + $captcha = $this->request->request("captcha"); + if (!$mobile || !$newpassword || !$captcha) + { + $this->error(__('Invalid parameters')); + } + if ($mobile && !Validate::regex($mobile, "^1\d{10}$")) + { + $this->error(__('Mobile incorrect')); + } + $user = \app\common\model\User::getByMobile($mobile); + if (!$user) + { + $this->error(__('User not found')); + } + $ret = Sms::check($mobile, $captcha, 'resetpwd'); + if (!$ret) + { + $this->error(__('Captcha invalid')); + } + Sms::flush($mobile, 'resetpwd'); + + //模拟一次登录 + $this->auth->direct($user->id); + $ret = $this->auth->changepwd($newpassword, '', true); + if ($ret) + { + $this->success(__('Reset password successful')); + } + else + { + $this->error($this->auth->getError()); + } + } + +} diff --git a/application/api/controller/Validate.php b/application/api/controller/Validate.php new file mode 100644 index 00000000..c8a7a017 --- /dev/null +++ b/application/api/controller/Validate.php @@ -0,0 +1,112 @@ +request->request('email'); + $id = (int) $this->request->request('id'); + $count = User::where('email', '=', $email)->where('id', '<>', $id)->count(); + if ($count > 0) + { + $this->error(__('邮箱已经被占用')); + } + $this->success(); + } + + /** + * 检测用户名 + * + * @param string $username 用户名 + * @param string $id 会员ID + */ + public function check_username_available() + { + $email = $this->request->request('username'); + $id = (int) $this->request->request('id'); + $count = User::where('username', '=', $email)->where('id', '<>', $id)->count(); + if ($count > 0) + { + $this->error(__('用户名已经被占用')); + } + $this->success(); + } + + /** + * 检测手机 + * + * @param string $mobile 手机号 + * @param string $id 会员ID + */ + public function check_mobile_available() + { + $email = $this->request->request('mobile'); + $id = (int) $this->request->request('id'); + $count = User::where('mobile', '=', $email)->where('id', '<>', $id)->count(); + if ($count > 0) + { + $this->error(__('已经使用该手机号注册')); + } + $this->success(); + } + + /** + * 检测手机 + * + * @param string $mobile 手机号 + */ + public function check_mobile_exist() + { + $email = $this->request->request('mobile'); + $count = User::where('mobile', '=', $email)->count(); + if (!$count) + { + $this->error(__('手机号不存在')); + } + $this->success(); + } + + /** + * 检测验证码 + * + * @param string $mobile 手机号 + * @param string $captcha 验证码 + * @param string $event 事件 + */ + public function check_sms_correct() + { + $mobile = $this->request->request('mobile'); + $captcha = $this->request->request('captcha'); + $event = $this->request->request('event'); + if (!\app\common\library\Sms::check($mobile, $captcha, $event)) + { + $this->error(__('验证码不正确')); + } + $this->success(); + } + +} diff --git a/application/api/lang/zh-cn/common.php b/application/api/lang/zh-cn/common.php new file mode 100644 index 00000000..fb78e7c1 --- /dev/null +++ b/application/api/lang/zh-cn/common.php @@ -0,0 +1,7 @@ + '未上传文件或超出服务器上传限制', + 'Uploaded file format is limited' => '上传文件格式受限制', + 'Upload successful' => '上传成功', +]; diff --git a/application/api/lang/zh-cn/user.php b/application/api/lang/zh-cn/user.php new file mode 100644 index 00000000..db7fd11b --- /dev/null +++ b/application/api/lang/zh-cn/user.php @@ -0,0 +1,40 @@ + '会员中心', + 'Register' => '注册', + 'Login' => '登录', + 'Sign up successful' => '注册成功', + 'Username can not be empty' => '用户名不能为空', + 'Username must be 6 to 30 characters' => '用户名必须6-30个字符', + 'Password can not be empty' => '密码不能为空', + 'Password must be 6 to 30 characters' => '密码必须6-30个字符', + 'Email is incorrect' => '邮箱格式不正确', + 'Mobile is incorrect' => '手机格式不正确', + 'Username already exist' => '用户名已经存在', + 'Email already exist' => '邮箱已经存在', + 'Mobile already exist' => '手机号已经存在', + 'Username is incorrect' => '用户名不正确', + 'Email is incorrect' => '邮箱不正确', + 'Account is locked' => '账户已经被锁定', + 'Password is incorrect' => '密码不正确', + 'Account is incorrect' => '账户不正确', + 'Account not exist' => '账户不存在', + 'Account can not be empty' => '账户不能为空', + 'Username or password is incorrect' => '用户名或密码不正确', + 'You are not logged in' => '你当前还未登录', + 'You\'ve logged in, do not login again' => '你已经存在,请不要重复登录', + 'Profile' => '个人资料', + 'Verify email' => '邮箱验证', + 'Change password' => '修改密码', + 'Change password successful' => '修改密码成功', + 'Captcha is incorrect' => '验证码不正确', + 'Sign up successful' => '注册成功', + 'Logged in successful' => '登录成功', + 'Logout successful' => '注销成功', + 'Operation failed' => '操作失败', + 'Invalid parameters' => '参数不正确', + 'Change password failure' => '修改密码失败', + 'Change password successful' => '修改密码成功', + 'Reset password successful' => '重置密码成功', +]; diff --git a/application/common/controller/Api.php b/application/common/controller/Api.php index b2591417..b6dde880 100644 --- a/application/common/controller/Api.php +++ b/application/common/controller/Api.php @@ -47,7 +47,7 @@ class Api * @var array */ protected $noNeedRight = []; - + /** * 权限Auth * @var Auth @@ -89,9 +89,9 @@ class Api $modulename = $this->request->module(); $controllername = strtolower($this->request->controller()); $actionname = strtolower($this->request->action()); - + // token - $token = $this->request->request('token'); + $token = $this->request->request('token') ?: $this->request->cookie('token'); $path = str_replace('.', '/', $controllername) . '/' . $actionname; // 设置当前请求的URI diff --git a/application/common/library/Auth.php b/application/common/library/Auth.php index f102ade7..b1259252 100644 --- a/application/common/library/Auth.php +++ b/application/common/library/Auth.php @@ -320,6 +320,8 @@ class Auth $this->_token = Random::uuid(); Token::set($this->_token, $user->id); + + $this->_logined = TRUE; //登录成功的事件 Hook::listen("user_login_successed", $this->_user); diff --git a/application/common/library/Email.php b/application/common/library/Email.php index 39bd3023..0881e26e 100644 --- a/application/common/library/Email.php +++ b/application/common/library/Email.php @@ -127,12 +127,17 @@ class Email /** * 获取最后产生的错误 + * @return string */ public function getError() { return $this->_error; } + /** + * 设置错误 + * @param string $error 信息信息 + */ protected function setError($error) { $this->_error = $error; diff --git a/application/common/library/Sms.php b/application/common/library/Sms.php index ace4e25a..aa1e2c9f 100644 --- a/application/common/library/Sms.php +++ b/application/common/library/Sms.php @@ -2,9 +2,11 @@ namespace app\common\library; -use addons\alisms\library\Alisms; -use app\common\model\MobileCode; +use think\Hook; +/** + * 验证码类 + */ class Sms { @@ -23,94 +25,89 @@ class Sms /** * 获取最后一次手机发送的数据 * - * @param int $mobile 手机号 - * @param string $type 类型 - * @return array + * @param int $mobile 手机号 + * @param string $event 事件 + * @return Sms */ - public static function get($mobile, $type = 'default') + public static function get($mobile, $event = 'default') { - return MobileCode:: - where(['mobile' => $mobile, 'type' => $type]) - ->order('id', 'DESC') - ->find(); + $sms = \app\common\model\Sms:: + where(['mobile' => $mobile, 'event' => $event]) + ->order('id', 'DESC') + ->find(); + $result = Hook::listen('sms_get', $sms); + return $result ? $result : NULL; } /** * 发送验证码 * - * @param int $mobile 手机号 - * @param int $code 验证码 - * @param string $type 类型 - * @return array + * @param int $mobile 手机号 + * @param int $code 验证码,为空时将自动生成4位数字 + * @param string $event 事件 + * @return boolean */ - public static function send($mobile, $code = '', $type = 'default') + public static function send($mobile, $code = NULL, $event = 'default') { - $config = get_addon_config('alisms'); - $code = !$code ? mt_rand(1000, 9999) : $code; - $alisms = new Alisms(); - $ret = $alisms->mobile($mobile) - ->template($config['template'][$type]) - ->param(['code' => $code]) - ->send(); - if ($ret) - { - $time = time(); - MobileCode::create(['type' => $type, 'mobile' => $mobile, 'code' => $code, 'createtime' => $time]); - return TRUE; - } - else + $code = is_null($code) ? mt_rand(1000, 9999) : $code; + $time = time(); + $sms = \app\common\model\Sms::create(['event' => $event, 'mobile' => $mobile, 'code' => $code, 'createtime' => $time]); + $result = Hook::listen('sms_send', $sms); + if (!$result) { + $sms->delete(); return FALSE; } + return TRUE; } /** * 发送通知 - * @param int $mobile 手机号 - * @param string $template 模板ID + * * @param array $params 参数 * @return boolean */ - public static function notice($mobile, $template, $params = []) + public static function notice($params = []) { - $alisms = Alisms::instance(); - $ret = $alisms->mobile($mobile) - ->template($template) - ->param($params) - ->send(); - return $ret ? TRUE : FALSE; + $result = Hook::listen('sms_notice', $params); + return $result ? TRUE : FALSE; } /** * 校验验证码 * - * @param int $mobile 手机号 - * @param int $code 验证码 - * @param string $type 类型 - * @return boolean + * @param int $mobile 手机号 + * @param int $code 验证码 + * @param string $event 事件 + * @return boolean */ - public static function check($mobile, $code, $type = 'default') + public static function check($mobile, $code, $event = 'default') { $time = time() - self::$expire; - $obj = MobileCode::where(['mobile' => $mobile, 'type' => $type]) + $sms = \app\common\model\Sms::where(['mobile' => $mobile, 'event' => $event]) ->order('id', 'DESC') ->find(); - if ($obj) + if ($sms) { - if ($obj['createtime'] > $time && $obj['times'] <= self::$maxCheckNums) + if ($sms['createtime'] > $time && $sms['times'] <= self::$maxCheckNums) { - $correct = $code == $obj['code']; + $correct = $code == $sms['code']; if (!$correct) { - $obj->times = $obj->times + 1; - $obj->save(); + $sms->times = $sms->times + 1; + $sms->save(); + return FALSE; + } + else + { + $result = Hook::listen('sms_check', $sms); + return $result; } - return $correct; } else { // 过期则清空该手机验证码 - self::flush($mobile, $type); + self::flush($mobile, $event); return FALSE; } } @@ -123,15 +120,16 @@ class Sms /** * 清空指定手机号验证码 * - * @param int $mobile 手机号 - * @param string $type 类型 - * @return boolean + * @param int $mobile 手机号 + * @param string $event 事件 + * @return boolean */ - public static function flush($mobile, $type = 'default') + public static function flush($mobile, $event = 'default') { - MobileCode:: - where(['mobile' => $mobile, 'type' => $type]) + \app\common\model\Sms:: + where(['mobile' => $mobile, 'event' => $event]) ->delete(); + Hook::listen('sms_flush'); return TRUE; } diff --git a/application/common/library/Token.php b/application/common/library/Token.php index 42b3f11b..d42679cf 100644 --- a/application/common/library/Token.php +++ b/application/common/library/Token.php @@ -10,20 +10,21 @@ class Token /** * 存储Token - * @param string $token Token - * @param int $user_id 会员ID - * @param int $expire 过期时长,0表示无限,单位秒 + * @param string $token Token + * @param int $user_id 会员ID + * @param int $expire 过期时长,0表示无限,单位秒 */ public static function set($token, $user_id, $expire = 0) { $expiretime = $expire ? time() + $expire : 0; \app\common\model\Token::create(['token' => $token, 'user_id' => $user_id, 'expiretime' => $expiretime]); + return TRUE; } /** * 获取Token内的信息 - * @param string $token - * @return array + * @param string $token + * @return array */ public static function get($token) { @@ -44,9 +45,9 @@ class Token /** * 判断Token是否可用 - * @param string $token Token - * @param int $user_id 会员ID - * @return boolean + * @param string $token Token + * @param int $user_id 会员ID + * @return boolean */ public static function check($token, $user_id) { @@ -56,8 +57,8 @@ class Token /** * 删除Token - * @param string $token - * @return boolean + * @param string $token + * @return boolean */ public static function delete($token) { @@ -72,8 +73,8 @@ class Token /** * 删除指定用户的所有Token - * @param int $user_id - * @return boolean + * @param int $user_id + * @return boolean */ public static function clear($user_id) { diff --git a/application/common/model/ScoreLog.php b/application/common/model/ScoreLog.php new file mode 100644 index 00000000..63b5e864 --- /dev/null +++ b/application/common/model/ScoreLog.php @@ -0,0 +1,23 @@ + 0, 'mobile' => 0], $value); + return (object) $value; + } + + /** + * 设置验证字段 + * @param mixed $value + * @return string + */ + public function setVerificationAttr($value) + { + $value = is_object($value) || is_array($value) ? json_encode($value) : $value; + return $value; + } + + /** + * 变更会员积分 + * @param int $score 积分 + * @param int $user_id 会员ID + * @param string $memo 备注 + */ + public static function score($score, $user_id, $memo) + { + $user = self::get($user_id); + if ($user) + { + $before = $user->score; + $after = $user->score + $score; + $level = self::nextlevel($after); + //更新会员信息 + $user->save(['score' => $after, 'level' => $level]); + //写入日志 + ScoreLog::create(['user_id' => $user_id, 'score' => $score, 'before' => $before, 'after' => $after, 'memo' => $memo]); + } + } + + /** + * 根据积分获取等级 + * @param int $score 积分 + * @return int + */ + public static function nextlevel($score = 0) + { + $lv = array(1 => 0, 2 => 30, 3 => 100, 4 => 500, 5 => 1000, 6 => 2000, 7 => 3000, 8 => 5000, 9 => 8000, 10 => 10000); + $level = 1; + foreach ($lv as $key => $value) + { + if ($score >= $value) + { + $level = $key; + } + } + return $level; + } + +} diff --git a/application/common/model/UserGroup.php b/application/common/model/UserGroup.php new file mode 100644 index 00000000..4646845d --- /dev/null +++ b/application/common/model/UserGroup.php @@ -0,0 +1,21 @@ + false, //登录页默认背景图 'login_background' => "/assets/img/loginbg.jpg", + //自动检测更新 + 'checkupdate' => false, //版本号 - 'version' => '1.0.0.20180117_beta', - 'api_url' => 'http://api.fastadmin.net', + 'version' => '1.0.0.20180119_beta', + 'api_url' => '//api.fastadmin.net', ], ]; diff --git a/application/index/controller/Ajax.php b/application/index/controller/Ajax.php index 3c68fca4..c4e2e865 100644 --- a/application/index/controller/Ajax.php +++ b/application/index/controller/Ajax.php @@ -3,8 +3,6 @@ namespace app\index\controller; use app\common\controller\Frontend; -use fast\Random; -use think\Config; use think\Lang; /** @@ -31,94 +29,13 @@ class Ajax extends Frontend $result = 'define(' . json_encode(Lang::get(), JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE) . ');'; return $result; } - + /** * 上传文件 */ public function upload() { - Config::set('default_return_type', 'json'); - $file = $this->request->file('file'); - if (empty($file)) - { - $this->error(__('No file upload or server upload limit exceeded')); - } - - //判断是否已经存在附件 - $sha1 = $file->hash(); - - $upload = Config::get('upload'); - - preg_match('/(\d+)(\w+)/', $upload['maxsize'], $matches); - $type = strtolower($matches[2]); - $typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3]; - $size = (int) $upload['maxsize'] * pow(1024, isset($typeDict[$type]) ? $typeDict[$type] : 0); - $fileInfo = $file->getInfo(); - $suffix = strtolower(pathinfo($fileInfo['name'], PATHINFO_EXTENSION)); - $suffix = $suffix ? $suffix : 'file'; - - $mimetypeArr = explode(',', $upload['mimetype']); - $typeArr = explode('/', $fileInfo['type']); - //验证文件后缀 - if ($upload['mimetype'] !== '*' && !in_array($suffix, $mimetypeArr) && !in_array($fileInfo['type'], $mimetypeArr) && !in_array($typeArr[0] . '/*', $mimetypeArr)) - { - $this->error(__('Uploaded file format is limited')); - } - $replaceArr = [ - '{year}' => date("Y"), - '{mon}' => date("m"), - '{day}' => date("d"), - '{hour}' => date("H"), - '{min}' => date("i"), - '{sec}' => date("s"), - '{random}' => Random::alnum(16), - '{random32}' => Random::alnum(32), - '{filename}' => $suffix ? substr($fileInfo['name'], 0, strripos($fileInfo['name'], '.')) : $fileInfo['name'], - '{suffix}' => $suffix, - '{.suffix}' => $suffix ? '.' . $suffix : '', - '{filemd5}' => md5_file($fileInfo['tmp_name']), - ]; - $savekey = $upload['savekey']; - $savekey = str_replace(array_keys($replaceArr), array_values($replaceArr), $savekey); - - $uploadDir = substr($savekey, 0, strripos($savekey, '/') + 1); - $fileName = substr($savekey, strripos($savekey, '/') + 1); - // - $splInfo = $file->validate(['size' => $size])->move(ROOT_PATH . '/public' . $uploadDir, $fileName); - if ($splInfo) - { - $imagewidth = $imageheight = 0; - if (in_array($suffix, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf'])) - { - $imgInfo = getimagesize($splInfo->getPathname()); - $imagewidth = isset($imgInfo[0]) ? $imgInfo[0] : $imagewidth; - $imageheight = isset($imgInfo[1]) ? $imgInfo[1] : $imageheight; - } - $params = array( - 'filesize' => $fileInfo['size'], - 'imagewidth' => $imagewidth, - 'imageheight' => $imageheight, - 'imagetype' => $suffix, - 'imageframes' => 0, - 'mimetype' => $fileInfo['type'], - 'url' => $uploadDir . $splInfo->getSaveName(), - 'uploadtime' => time(), - 'storage' => 'local', - 'sha1' => $sha1, - ); - $attachment = model("attachment"); - $attachment->data(array_filter($params)); - $attachment->save(); - \think\Hook::listen("upload_after", $attachment); - $this->success(__('Upload successful'), null, [ - 'url' => $uploadDir . $splInfo->getSaveName() - ]); - } - else - { - // 上传失败获取错误信息 - $this->error($file->getError()); - } + return action('api/common/upload'); } } diff --git a/application/index/controller/User.php b/application/index/controller/User.php new file mode 100644 index 00000000..6b5d49ce --- /dev/null +++ b/application/index/controller/User.php @@ -0,0 +1,353 @@ +auth; + + //监听注册登录注销的事件 + Hook::add('user_login_successed', function($user) use($auth) { + Cookie::set('uid', $user->id); + Cookie::set('token', $auth->getToken()); + }); + Hook::add('user_register_successed', function($user) use($auth) { + Cookie::set('uid', $user->id); + Cookie::set('token', $auth->getToken()); + }); + Hook::add('user_delete_successed', function($user) use($auth) { + Cookie::delete('uid'); + Cookie::delete('token'); + }); + Hook::add('user_logout_successed', function($user) use($auth) { + Cookie::delete('uid'); + Cookie::delete('token'); + }); + } + + /** + * 会员中心 + */ + public function index() + { + $this->view->assign('title', __('User center')); + return $this->view->fetch(); + } + + /** + * 注册会员 + */ + public function register() + { + $url = $this->request->request('url', url('user/index')); + if ($this->auth->id) + $this->success(__('You\'ve logged in, do not login again'), $url); + if ($this->request->isPost()) + { + $username = $this->request->post('username'); + $password = $this->request->post('password'); + $email = $this->request->post('email'); + $mobile = $this->request->post('mobile', ''); + $captcha = $this->request->post('captcha'); + $token = $this->request->post('__token__'); + $rule = [ + 'username' => 'require|length:3,30', + 'password' => 'require|length:6,30', + 'email' => 'require|email', + 'mobile' => 'regex:/^1\d{10}$/', + 'captcha' => 'require|captcha', + '__token__' => 'token', + ]; + + $msg = [ + 'username.require' => 'Username can not be empty', + 'username.length' => 'Username must be 3 to 30 characters', + 'password.require' => 'Password can not be empty', + 'password.length' => 'Password must be 6 to 30 characters', + 'captcha.require' => 'Captcha can not be empty', + 'captcha.captcha' => 'Captcha is incorrect', + 'email' => 'Email is incorrect', + 'mobile' => 'Mobile is incorrect', + ]; + $data = [ + 'username' => $username, + 'password' => $password, + 'email' => $email, + 'mobile' => $mobile, + 'captcha' => $captcha, + '__token__' => $token, + ]; + $validate = new Validate($rule, $msg); + $result = $validate->check($data); + if (!$result) + { + $this->error(__($validate->getError())); + } + if ($this->auth->register($username, $password, $email, $mobile)) + { + $synchtml = ''; + ////////////////同步到Ucenter//////////////// + if (defined('UC_STATUS') && UC_STATUS) + { + $uc = new \addons\ucenter\library\client\Client(); + $synchtml = $uc->uc_user_synregister($this->auth->id, $password); + } + $referer = Cookie::get('referer_url'); + $this->success(__('Sign up successful') . $synchtml, $referer); + } + else + { + $this->error($this->auth->getError()); + } + } + Session::set('redirect_url', $url); + $this->view->assign('title', __('Register')); + return $this->view->fetch(); + } + + /** + * 会员登录 + */ + public function login() + { + $url = $this->request->request('url', url('user/index')); + if ($this->auth->id) + $this->success(__('You\'ve logged in, do not login again'), $url); + if ($this->request->isPost()) + { + $account = $this->request->post('account'); + $password = $this->request->post('password'); + $keeptime = (int) $this->request->post('keeptime'); + $token = $this->request->post('__token__'); + $rule = [ + 'account' => 'require|length:3,50', + 'password' => 'require|length:6,30', + '__token__' => 'token', + ]; + + $msg = [ + 'account.require' => 'Account can not be empty', + 'account.length' => 'Account must be 3 to 50 characters', + 'password.require' => 'Password can not be empty', + 'password.length' => 'Password must be 6 to 30 characters', + ]; + $data = [ + 'account' => $account, + 'password' => $password, + '__token__' => $token, + ]; + $validate = new Validate($rule, $msg); + $result = $validate->check($data); + if (!$result) + { + $this->error(__($validate->getError())); + return FALSE; + } + if ($this->auth->login($account, $password, $keeptime)) + { + $synchtml = ''; + ////////////////同步到Ucenter//////////////// + if (defined('UC_STATUS') && UC_STATUS) + { + $uc = new \addons\ucenter\library\client\Client(); + $synchtml = $uc->uc_user_synlogin($this->auth->id); + } + $this->success(__('Logged in successful') . $synchtml, $url); + } + else + { + $this->error($this->auth->getError()); + } + } + $this->view->assign('title', __('Login')); + return $this->view->fetch(); + } + + /** + * 注销登录 + */ + function logout() + { + //注销本站 + $this->auth->logout(); + $synchtml = ''; + ////////////////同步到Ucenter//////////////// + if (defined('UC_STATUS') && UC_STATUS) + { + $uc = new \addons\ucenter\library\client\Client(); + $synchtml = $uc->uc_user_synlogout(); + } + $this->success(__('Logout successful') . $synchtml, url('user/index')); + } + + /** + * 第三方登录跳转和回调处理 + */ + public function third() + { + $url = url('user/index'); + $action = $this->request->param('action'); + $platform = $this->request->param('platform'); + $config = get_addon_config('third'); + if (!$config || !isset($config[$platform])) + { + $this->error(__('Invalid parameters')); + } + foreach ($config as $k => &$v) + { + $v['callback'] = url('user/third', ['action' => 'callback', 'platform' => $k], false, true); + } + unset($v); + $app = new \addons\third\library\Application($config); + if ($action == 'redirect') + { + // 跳转到登录授权页面 + $this->redirect($app->{$platform}->getAuthorizeUrl()); + } + else if ($action == 'callback') + { + // 授权成功后的回调 + $result = $app->{$platform}->getUserInfo(); + if ($result) + { + $loginret = \addons\third\library\Service::connect($platform, $result); + if ($loginret) + { + $synchtml = ''; + ////////////////同步到Ucenter//////////////// + if (defined('UC_STATUS') && UC_STATUS) + { + $uc = new \addons\ucenter\library\client\Client(); + $synchtml = $uc->uc_user_synlogin($this->auth->id); + } + $this->success(__('Logged in successful') . $synchtml, $url); + } + } + $this->error(__('Operation failed'), $url); + } + else + { + $this->error(__('Invalid parameters')); + } + } + + /** + * 个人信息 + */ + public function profile() + { + $this->view->assign('title', __('Profile')); + return $this->view->fetch(); + } + + /** + * 激活邮箱 + */ + public function activeemail() + { + $code = $this->request->request('code'); + $code = base64_decode($code); + parse_str($code, $params); + if (!isset($params['id']) || !isset($params['time']) || !isset($params['key'])) + { + $this->error(__('Invalid parameters')); + } + $user = \app\common\model\User::get($params['id']); + if (!$user) + { + $this->error(__('User not found')); + } + if ($user->verification->email) + { + $this->error(__('Email already activation')); + } + if ($key !== md5(md5($user->id . $user->email . $time) . $user->salt) || time() - $params['time'] > 1800) + { + $this->error(__('Secrity code already invalid')); + } + $verification = $user->verification; + $verification->email = 1; + $user->verification = $verification; + $user->save(); + $this->success(__('Active email successful'), url('user/index')); + return; + } + + /** + * 修改密码 + */ + public function changepwd() + { + if ($this->request->isPost()) + { + $oldpassword = $this->request->post("oldpassword"); + $newpassword = $this->request->post("newpassword"); + $renewpassword = $this->request->post("renewpassword"); + $token = $this->request->post('__token__'); + $rule = [ + 'oldpassword' => 'require|length:6,30', + 'newpassword' => 'require|length:6,30', + 'renewpassword' => 'require|length:6,30|confirm:newpassword', + '__token__' => 'token', + ]; + + $msg = [ + ]; + $data = [ + 'oldpassword' => $oldpassword, + 'newpassword' => $newpassword, + 'renewpassword' => $renewpassword, + '__token__' => $token, + ]; + $field = [ + 'oldpassword' => __('Old password'), + 'newpassword' => __('New password'), + 'renewpassword' => __('Renew password') + ]; + $validate = new Validate($rule, $msg, $field); + $result = $validate->check($data); + if (!$result) + { + $this->error(__($validate->getError())); + return FALSE; + } + + $ret = $this->auth->changepwd($newpassword, $oldpassword); + if ($ret) + { + $synchtml = ''; + ////////////////同步到Ucenter//////////////// + if (defined('UC_STATUS') && UC_STATUS) + { + $uc = new \addons\ucenter\library\client\Client(); + $synchtml = $uc->uc_user_synlogout(); + } + $this->success(__('Reset password successful') . $synchtml, url('user/login')); + } + else + { + $this->error($this->auth->getError()); + } + } + $this->view->assign('title', __('Change password')); + return $this->view->fetch(); + } + +} diff --git a/application/index/lang/zh-cn.php b/application/index/lang/zh-cn.php index 69ff6cf1..fd4cb9ec 100644 --- a/application/index/lang/zh-cn.php +++ b/application/index/lang/zh-cn.php @@ -1,90 +1,112 @@ '保持会话', - 'Sign in' => '登入', - 'Username' => '用户名', - 'User id' => '会员ID', - 'Username' => '用户名', - 'Nickname' => '昵称', - 'Password' => '密码', - 'Sign up' => '注 册', - 'Sign in' => '登 录', - 'Sign out' => '注 销', - 'Keep login' => '保持会话', - 'Guest' => '游客', - 'Welcome' => '%s,你好!', - 'Add' => '添加', - 'Edit' => '编辑', - 'Delete' => '删除', - 'Move' => '移动', - 'Name' => '名称', - 'Status' => '状态', - 'Weigh' => '权重', - 'Operate' => '操作', - 'Warning' => '温馨提示', - 'Default' => '默认', - 'Article' => '文章', - 'Page' => '单页', - 'OK' => '确定', - 'Cancel' => '取消', - 'Loading' => '加载中', - 'More' => '更多', - 'Normal' => '正常', - 'Hidden' => '隐藏', - 'Submit' => '提交', - 'Reset' => '重置', - 'Execute' => '执行', - 'Close' => '关闭', - 'Search' => '搜索', - 'Refresh' => '刷新', - 'First' => '首页', - 'Previous' => '上一页', - 'Next' => '下一页', - 'Last' => '末页', - 'None' => '无', - 'Home' => '主页', - 'Online' => '在线', - 'Logout' => '注销', - 'Profile' => '个人资料', - 'Index' => '首页', - 'Hot' => '热门', - 'Recommend' => '推荐', - 'Dashboard' => '控制台', - 'Code' => '编号', - 'Message' => '内容', - 'Line' => '行号', - 'File' => '文件', - 'Menu' => '菜单', - 'Name' => '名称', - 'Weigh' => '权重', - 'Type' => '类型', - 'Title' => '标题', - 'Content' => '内容', - 'Status' => '状态', - 'Operate' => '操作', - 'Append' => '追加', - 'Memo' => '备注', - 'Parent' => '父级', - 'Params' => '参数', - 'Permission' => '权限', - 'Advance search' => '高级搜索', - 'Check all' => '选中全部', - 'Expand all' => '展开全部', - 'Begin time' => '开始时间', - 'End time' => '结束时间', - 'Create time' => '创建时间', - 'Flag' => '标志', - 'Redirect now' => '立即跳转', - 'Operation completed' => '操作成功!', - 'Operation failed' => '操作失败!', - 'Unknown data format' => '未知的数据格式!', - 'Network error' => '网络错误!', - 'Advanced search' => '高级搜索', - 'Invalid parameters' => '未知参数', - 'No results were found' => '记录未找到', - 'Parameter %s can not be empty' => '参数%s不能为空', - 'You have no permission' => '你没有权限访问', - 'An unexpected error occurred' => '发生了一个意外错误,程序猿正在紧急处理中', - 'This page will be re-directed in %s seconds' => '页面将在 %s 秒后自动跳转', + 'Keep login' => '保持会话', + 'Forgot password' => '忘记密码?', + 'Sign in' => '登入', + 'Username' => '用户名', + 'User id' => '会员ID', + 'Username' => '用户名', + 'Nickname' => '昵称', + 'Password' => '密码', + 'Sign up' => '注 册', + 'Sign in' => '登 录', + 'Sign out' => '注 销', + 'Guest' => '游客', + 'Welcome' => '%s,你好!', + 'Add' => '添加', + 'Edit' => '编辑', + 'Delete' => '删除', + 'Move' => '移动', + 'Name' => '名称', + 'Status' => '状态', + 'Weigh' => '权重', + 'Operate' => '操作', + 'Warning' => '温馨提示', + 'Default' => '默认', + 'Article' => '文章', + 'Page' => '单页', + 'OK' => '确定', + 'Cancel' => '取消', + 'Loading' => '加载中', + 'More' => '更多', + 'Normal' => '正常', + 'Hidden' => '隐藏', + 'Submit' => '提交', + 'Reset' => '重置', + 'Execute' => '执行', + 'Close' => '关闭', + 'Search' => '搜索', + 'Refresh' => '刷新', + 'First' => '首页', + 'Previous' => '上一页', + 'Next' => '下一页', + 'Last' => '末页', + 'None' => '无', + 'Home' => '主页', + 'Online' => '在线', + 'Logout' => '注销', + 'Profile' => '个人资料', + 'Index' => '首页', + 'Hot' => '热门', + 'Recommend' => '推荐', + 'Dashboard' => '控制台', + 'Code' => '编号', + 'Message' => '内容', + 'Line' => '行号', + 'File' => '文件', + 'Menu' => '菜单', + 'Name' => '名称', + 'Weigh' => '权重', + 'Type' => '类型', + 'Title' => '标题', + 'Content' => '内容', + 'Status' => '状态', + 'Operate' => '操作', + 'Append' => '追加', + 'Memo' => '备注', + 'Parent' => '父级', + 'Params' => '参数', + 'Permission' => '权限', + 'Advance search' => '高级搜索', + 'Check all' => '选中全部', + 'Expand all' => '展开全部', + 'Begin time' => '开始时间', + 'End time' => '结束时间', + 'Create time' => '创建时间', + 'Flag' => '标志', + 'Gitee' => '码云', + 'Github' => 'Github', + 'QQ group' => 'QQ群', + 'Go to Dashboard' => '登录后台', + 'Contribution' => '为FastAdmin贡献代码!', + 'Copyrights' => '版权所有', + 'Responsive' => '响应式开发', + 'Languages' => '多语言', + 'Module' => '模块化开发', + 'Extension' => '自由可扩展', + 'Auth' => '权限管理', + 'The fastest framework based on ThinkPHP5 and Bootstrap' => '基于ThinkPHP5和Bootstrap的极速后台开发框架', + 'Features' => '功能特性', + 'Home' => '首页', + 'Store' => '插件市场', + 'Services' => '服务', + 'Download' => '下载', + 'Demo' => '演示', + 'Donation' => '捐赠', + 'Forum' => '社区', + 'Docs' => '文档', + 'Send verification code' => '发磅验证码', + 'Redirect now' => '立即跳转', + 'Operation completed' => '操作成功!', + 'Operation failed' => '操作失败!', + 'Unknown data format' => '未知的数据格式!', + 'Network error' => '网络错误!', + 'Advanced search' => '高级搜索', + 'Invalid parameters' => '未知参数', + 'No results were found' => '记录未找到', + 'Parameter %s can not be empty' => '参数%s不能为空', + 'You have no permission' => '你没有权限访问', + 'An unexpected error occurred' => '发生了一个意外错误,程序猿正在紧急处理中', + 'This page will be re-directed in %s seconds' => '页面将在 %s 秒后自动跳转', ]; diff --git a/application/index/lang/zh-cn/user.php b/application/index/lang/zh-cn/user.php new file mode 100644 index 00000000..cda5d167 --- /dev/null +++ b/application/index/lang/zh-cn/user.php @@ -0,0 +1,59 @@ + '会员中心', + 'Register' => '注册', + 'Login' => '登录', + 'Account' => '账号', + 'Mobile' => '手机号', + 'Email' => '邮箱', + 'Captcha' => '验证码', + 'Lv' => 'Lv', + 'Score' => '积分', + 'Day' => '天', + 'Intro' => '个人介绍', + 'Successions' => '连续登录', + 'Maxsuccessions' => '最长连续登录', + 'Logintime' => '登录时间', + 'Prevtime' => '最后登录', + 'Change' => '修改', + 'Click to edit' => '点击编辑', + 'Email/Mobile/Username' => '邮箱/手机/用户名', + 'Sign up successful' => '注册成功', + 'Email active successful' => '邮箱激活成功', + 'Username can not be empty' => '用户名不能为空', + 'Username must be 6 to 30 characters' => '用户名必须6-30个字符', + 'Password can not be empty' => '密码不能为空', + 'Password must be 6 to 30 characters' => '密码必须6-30个字符', + 'Email is incorrect' => '邮箱格式不正确', + 'Mobile is incorrect' => '手机格式不正确', + 'Username already exist' => '用户名已经存在', + 'Email already exist' => '邮箱已经存在', + 'Mobile already exist' => '手机号已经存在', + 'Username is incorrect' => '用户名不正确', + 'Email is incorrect' => '邮箱不正确', + 'Account is locked' => '账户已经被锁定', + 'Password is incorrect' => '密码不正确', + 'Account is incorrect' => '账户不正确', + 'Account not exist' => '账户不存在', + 'Account can not be empty' => '账户不能为空', + 'Username or password is incorrect' => '用户名或密码不正确', + 'You are not logged in' => '你当前还未登录', + 'You\'ve logged in, do not login again' => '你已经存在,请不要重复登录', + 'Profile' => '个人资料', + 'Old password' => '旧密码', + 'New password' => '新密码', + 'Renew password' => '确认新密码', + 'Change password' => '修改密码', + 'New email' => '新邮箱', + 'New mobile' => '新手机号', + 'Change password successful' => '修改密码成功', + 'Captcha is incorrect' => '验证码不正确', + 'Sign up successful' => '注册成功', + 'Logged in successful' => '登录成功', + 'Logout successful' => '注销成功', + 'Operation failed' => '操作失败', + 'Invalid parameters' => '参数不正确', + 'Change password failure' => '修改密码失败', + 'Change password successful' => '修改密码成功', +]; diff --git a/application/index/view/common/meta.html b/application/index/view/common/meta.html new file mode 100644 index 00000000..d75406ff --- /dev/null +++ b/application/index/view/common/meta.html @@ -0,0 +1,27 @@ + +{$title|default=''} – {:__('The fastest framework based on ThinkPHP5 and Bootstrap')} + + + +{if isset($keywords)} + +{/if} +{if isset($description)} + +{/if} + + + + + + + + + \ No newline at end of file diff --git a/application/index/view/common/script.html b/application/index/view/common/script.html new file mode 100644 index 00000000..060e0a40 --- /dev/null +++ b/application/index/view/common/script.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/application/index/view/common/sidenav.html b/application/index/view/common/sidenav.html new file mode 100644 index 00000000..d258d351 --- /dev/null +++ b/application/index/view/common/sidenav.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/application/index/view/index/index.html b/application/index/view/index/index.html index e75a11ee..a75f8527 100644 --- a/application/index/view/index/index.html +++ b/application/index/view/index/index.html @@ -9,7 +9,7 @@ - FastAdmin - 基于ThinkPHP5和Bootstrap的极速后台开发框架 + FastAdmin - {:__('The fastest framework based on ThinkPHP5 and Bootstrap')} @@ -39,14 +39,14 @@ @@ -61,8 +61,8 @@

    FastAdmin

    -

    基于ThinkPHP5和Bootstrap的极速后台开发框架

    - 登录后台 +

    {:__('The fastest framework based on ThinkPHP5 and Bootstrap')}

    + {:__('Go to Dashboard')}
    @@ -75,7 +75,7 @@
    -

    功能特性

    +

    {:__('Features')}

    @@ -86,42 +86,42 @@
    -

    权限管理

    +

    {:__('Auth')}

    基于完善的Auth权限控制管理、无限父子级权限分组、可自由分配子级权限、一个管理员可同时属于多个组别

    -

    响应式开发

    +

    {:__('Responsive')}

    基于Bootstrap和AdminLTE进行二次开发,手机、平板、PC均自动适配,无需要担心兼容性问题

    -

    多语言

    +

    {:__('Languages')}

    不仅仅后台开发支持多语言,同时视图部分和JS部分仍然共享同一个语言包,语法相同且自动加载

    -

    模块化开发

    +

    {:__('Module')}

    控制器、模型、视图、JS一一对应,使用RequireJS进行JS模块化管理,采用Bower进行前端包组件管理

    -

    CRUD

    +

    {:__('CRUD')}

    控制台进行一键生成控制器、模型、视图和JS文件,同时可一键生成后台权限节点和菜单栏

    -

    自由可扩展

    +

    {:__('Extension')}

    FastAdmin提供强大的扩展中心,可直接在线安装和卸载插件,同时支持命令行一键操作

    @@ -136,7 +136,7 @@

    不要犹豫
    开始行动

    - 为FastAdmin贡献代码! + {:__('Contribution')}
    @@ -147,13 +147,13 @@

    © 2017 FastAdmin. All Rights Reserved.

    diff --git a/application/index/view/layout/common.html b/application/index/view/layout/common.html index 7daa9deb..f833a835 100644 --- a/application/index/view/layout/common.html +++ b/application/index/view/layout/common.html @@ -9,7 +9,7 @@ - FastAdmin - 基于ThinkPHP5和Bootstrap的极速后台开发框架 + FastAdmin - {:__('The fastest framework based on ThinkPHP5 and Bootstrap')} @@ -50,14 +50,14 @@
    @@ -73,13 +73,13 @@

    © 2017 FastAdmin. All Rights Reserved.

    diff --git a/application/index/view/layout/default.html b/application/index/view/layout/default.html new file mode 100644 index 00000000..5030e678 --- /dev/null +++ b/application/index/view/layout/default.html @@ -0,0 +1,68 @@ + + + + {include file="common/meta" /} + + + + + + + + {__CONTENT__} + + + + {include file="common/script" /} + + + + \ No newline at end of file diff --git a/application/index/view/user/changepwd.html b/application/index/view/user/changepwd.html new file mode 100644 index 00000000..a9c35233 --- /dev/null +++ b/application/index/view/user/changepwd.html @@ -0,0 +1,43 @@ +
    +
    +
    + {include file="common/sidenav" /} +
    +
    +
    +
    + +
    + {:token()} +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    + + +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/application/index/view/user/index.html b/application/index/view/user/index.html new file mode 100644 index 00000000..ea9f71e2 --- /dev/null +++ b/application/index/view/user/index.html @@ -0,0 +1,56 @@ +
    +
    +
    + {include file="common/sidenav" /} +
    +
    +
    +
    + +
    +
    + + + +
    +
    + +
    + +

    {$user.username}

    + +

    {$user.bio|default='这个人很懒,啥也没写'}

    + +
    + + + + + + + + + + + + + + + + + + + +
    {:__('Lv')}{$user.level}{:__('Score')}{$user.score}
    {:__('Successions')}{$user.successions} {:__('Day')}{:__('Maxsuccessions')}{$user.maxsuccessions} {:__('Day')}
    {:__('Logintime')}{$user.logintime|date="Y-m-d H:i:s",###}{:__('Prevtime')}{$user.prevtime|date="Y-m-d H:i:s",###}
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/application/index/view/user/login.html b/application/index/view/user/login.html new file mode 100644 index 00000000..f3476db3 --- /dev/null +++ b/application/index/view/user/login.html @@ -0,0 +1,72 @@ +
    +
    + + +
    +
    + \ No newline at end of file diff --git a/application/index/view/user/profile.html b/application/index/view/user/profile.html new file mode 100644 index 00000000..ff8678a5 --- /dev/null +++ b/application/index/view/user/profile.html @@ -0,0 +1,164 @@ + +
    +
    +
    + {include file="common/sidenav" /} +
    +
    +
    +
    + +
    + {:token()} + +
    + +
    +
    + +
    {:__('Click to edit')}
    + +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    +
    + + + {:__('Change')} + +
    + +
    +
    +
    + +
    +
    + + + {:__('Change')} + +
    + +
    +
    + +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/application/index/view/user/register.html b/application/index/view/user/register.html new file mode 100644 index 00000000..5a7b33d0 --- /dev/null +++ b/application/index/view/user/register.html @@ -0,0 +1,54 @@ +
    +
    + + +
    +
    \ No newline at end of file diff --git a/public/assets/css/user.css b/public/assets/css/user.css new file mode 100644 index 00000000..7f5839fe --- /dev/null +++ b/public/assets/css/user.css @@ -0,0 +1,17 @@ +h2.page-header { + margin:10px 0 25px 0; + padding-bottom:15px; +} +.user-baseinfo { + margin-bottom:25px; +} +.user-baseinfo table tr td {color:#999;} +@media (min-width: 767px) { + .user-center .avatar-text,.user-center .avatar-img { + height:150px;width:150px;border-radius: 150px;line-height:150px;font-size:70px; + } + .user-center .avatar-img {font-size:0;} + .user-center .avatar-img img { + height:150px;width:150px;border-radius:150px; + } +} \ No newline at end of file diff --git a/public/assets/js/backend/addon.js b/public/assets/js/backend/addon.js index 454f858a..b4999043 100644 --- a/public/assets/js/backend/addon.js +++ b/public/assets/js/backend/addon.js @@ -59,6 +59,42 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function // 为表格绑定事件 Table.api.bindevent(table); + table.on('click', '.btn-addoninfo', function (event) { + var index = parseInt($(this).data("index")); + var data = table.bootstrapTable("getData"); + var item = data[index]; + var addon = typeof Config.addons[item.name] != 'undefined' ? Config.addons[item.name] : null; + console.log(item, addon); + Layer.alert(Template("addoninfotpl", {item: item, addon: addon}), { + btn: [__('OK'), __('Donate'), __('Feedback'), __('Document')], + title: __('Detail'), + area: ['450px', '490px'], + btn2: function () { + //打赏 + Layer.open({ + content: Template("paytpl", {payimg: item.donateimage}), + shade: 0.8, + area: ['800px', '600px'], + skin: 'layui-layer-msg layui-layer-pay', + title: false, + closeBtn: true, + btn: false, + resize: false, + }); + }, + btn3: function () { + return false; + }, + btn4: function () { + return false; + }, + success: function (layero, index) { + $(".layui-layer-btn2", layero).attr("href", "http://forum.fastadmin.net/t/bug?ref=addon&name=" + item.name).attr("target", "_blank"); + $(".layui-layer-btn3", layero).attr("href", "http://www.fastadmin.net/store/" + item.name + ".html?ref=addon").attr("target", "_blank"); + } + }); + }); + // 如果是https则启用提示 if (location.protocol === "https:") { $("#warmtips").removeClass("hide"); @@ -73,6 +109,15 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function }); }); + //查看插件首页 + $(document).on("click", ".btn-addonindex", function () { + if ($(this).attr("href") == 'javascript:;') { + Layer.msg(__('Not installed tips'), {icon: 7}); + } else if ($(this).parent().find("a.btn-enable").size() > 0) { + Layer.msg(__('Not enabled tips'), {icon: 7}); + return false; + } + }); //切换URL $(document).on("click", ".btn-switch", function () { $(".btn-switch").removeClass("active"); diff --git a/public/assets/js/backend/index.js b/public/assets/js/backend/index.js index 8836a7f6..3e80037d 100755 --- a/public/assets/js/backend/index.js +++ b/public/assets/js/backend/index.js @@ -118,7 +118,7 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi //读取版本检测信息 var ignoreversion = localStorage.getItem("ignoreversion"); - if (ignoreversion !== "*") { + if (Config.fastadmin.checkupdate && ignoreversion !== "*") { checkupdate(ignoreversion, false); } //手动检测版本信息 diff --git a/public/assets/js/backend/user/group.js b/public/assets/js/backend/user/group.js new file mode 100644 index 00000000..600e6da4 --- /dev/null +++ b/public/assets/js/backend/user/group.js @@ -0,0 +1,114 @@ +define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'jstree'], function ($, undefined, Backend, Table, Form, undefined) { + //读取选中的条目 + $.jstree.core.prototype.get_all_checked = function (full) { + var obj = this.get_selected(), i, j; + for (i = 0, j = obj.length; i < j; i++) { + obj = obj.concat(this.get_node(obj[i]).parents); + } + obj = $.grep(obj, function (v, i, a) { + return v != '#'; + }); + obj = obj.filter(function (itm, i, a) { + return i == a.indexOf(itm); + }); + return full ? $.map(obj, $.proxy(function (i) { + return this.get_node(i); + }, this)) : obj; + }; + var Controller = { + index: function () { + // 初始化表格参数配置 + Table.api.init({ + extend: { + index_url: 'user/group/index', + add_url: 'user/group/add', + edit_url: 'user/group/edit', + del_url: 'user/group/del', + multi_url: 'user/group/multi', + table: 'user_group', + } + }); + + var table = $("#table"); + + // 初始化表格 + table.bootstrapTable({ + url: $.fn.bootstrapTable.defaults.extend.index_url, + pk: 'id', + sortName: 'id', + columns: [ + [ + {checkbox: true}, + {field: 'id', title: __('Id')}, + {field: 'name', title: __('Name')}, + {field: 'createtime', title: __('Createtime'), formatter: Table.api.formatter.datetime}, + {field: 'updatetime', title: __('Updatetime'), formatter: Table.api.formatter.datetime}, + {field: 'status', title: __('Status'), formatter: Table.api.formatter.status}, + {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate} + ] + ] + }); + + // 为表格绑定事件 + Table.api.bindevent(table); + }, + add: function () { + Controller.api.bindevent(); + }, + edit: function () { + Controller.api.bindevent(); + }, + api: { + bindevent: function () { + Form.api.bindevent($("form[role=form]"), null, null, function () { + if ($("#treeview").size() > 0) { + var r = $("#treeview").jstree("get_all_checked"); + $("input[name='row[rules]']").val(r.join(',')); + } + return true; + }); + //渲染权限节点树 + //销毁已有的节点树 + $("#treeview").jstree("destroy"); + Controller.api.rendertree(nodeData); + //全选和展开 + $(document).on("click", "#checkall", function () { + $("#treeview").jstree($(this).prop("checked") ? "check_all" : "uncheck_all"); + }); + $(document).on("click", "#expandall", function () { + $("#treeview").jstree($(this).prop("checked") ? "open_all" : "close_all"); + }); + $("select[name='row[pid]']").trigger("change"); + }, + rendertree: function (content) { + $("#treeview") + .on('redraw.jstree', function (e) { + $(".layer-footer").attr("domrefresh", Math.random()); + }) + .jstree({ + "themes": {"stripes": true}, + "checkbox": { + "keep_selected_style": false, + }, + "types": { + "root": { + "icon": "fa fa-folder-open", + }, + "menu": { + "icon": "fa fa-folder-open", + }, + "file": { + "icon": "fa fa-file-o", + } + }, + "plugins": ["checkbox", "types"], + "core": { + 'check_callback': true, + "data": content + } + }); + } + } + }; + return Controller; +}); \ No newline at end of file diff --git a/public/assets/js/backend/user/rule.js b/public/assets/js/backend/user/rule.js new file mode 100644 index 00000000..4a9848a8 --- /dev/null +++ b/public/assets/js/backend/user/rule.js @@ -0,0 +1,70 @@ +define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) { + + var Controller = { + index: function () { + // 初始化表格参数配置 + Table.api.init({ + extend: { + index_url: 'user/rule/index', + add_url: 'user/rule/add', + edit_url: 'user/rule/edit', + del_url: 'user/rule/del', + multi_url: 'user/rule/multi', + table: 'user_rule', + } + }); + + var table = $("#table"); + + // 初始化表格 + table.bootstrapTable({ + url: $.fn.bootstrapTable.defaults.extend.index_url, + pk: 'id', + sortName: 'weigh', + escape: false, + columns: [ + [ + {checkbox: true}, + {field: 'id', title: __('Id')}, + {field: 'pid', title: __('Pid'), visible: false}, + {field: 'title', title: __('Title'), align: 'left'}, + {field: 'name', title: __('Name')}, + {field: 'remark', title: __('Remark')}, + {field: 'ismenu', title: __('Ismenu'), formatter: Controller.api.formatter.toggle}, + {field: 'createtime', title: __('Createtime'), formatter: Table.api.formatter.datetime, visible: false}, + {field: 'updatetime', title: __('Updatetime'), formatter: Table.api.formatter.datetime, visible: false}, + {field: 'weigh', title: __('Weigh')}, + {field: 'status', title: __('Status'), formatter: Table.api.formatter.status}, + {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate} + ] + ] + }); + + // 为表格绑定事件 + Table.api.bindevent(table); + }, + add: function () { + Controller.api.bindevent(); + }, + edit: function () { + Controller.api.bindevent(); + }, + api: { + bindevent: function () { + $(document).on('click', "input[name='row[ismenu]']", function () { + var name = $("input[name='row[name]']"); + name.prop("placeholder", $(this).val() == 1 ? name.data("placeholder-menu") : name.data("placeholder-node")); + }); + $("input[name='row[ismenu]']:checked").trigger("click"); + Form.api.bindevent($("form[role=form]")); + }, + formatter: { + toggle: function (value, row, index) { + //添加上btn-change可以自定义请求的URL进行数据处理 + return ''; + }, + } + } + }; + return Controller; +}); \ No newline at end of file diff --git a/public/assets/js/backend/user/user.js b/public/assets/js/backend/user/user.js new file mode 100644 index 00000000..aacab6c1 --- /dev/null +++ b/public/assets/js/backend/user/user.js @@ -0,0 +1,65 @@ +define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) { + + var Controller = { + index: function () { + // 初始化表格参数配置 + Table.api.init({ + extend: { + index_url: 'user/user/index', + add_url: 'user/user/add', + edit_url: 'user/user/edit', + del_url: 'user/user/del', + multi_url: 'user/user/multi', + table: 'user', + } + }); + + var table = $("#table"); + + // 初始化表格 + table.bootstrapTable({ + url: $.fn.bootstrapTable.defaults.extend.index_url, + pk: 'id', + sortName: 'user.id', + columns: [ + [ + {checkbox: true}, + {field: 'id', title: __('Id'), sortable: true}, + {field: 'group.name', title: __('Group')}, + {field: 'username', title: __('Username'), operate: 'LIKE'}, + {field: 'nickname', title: __('Nickname'), operate: 'LIKE'}, + {field: 'email', title: __('Email'), operate: 'LIKE'}, + {field: 'mobile', title: __('Mobile'), operate: 'LIKE'}, + {field: 'avatar', title: __('Avatar'), formatter: Table.api.formatter.image, operate: false}, + {field: 'level', title: __('Level'), operate: 'BETWEEN', sortable: true}, + {field: 'gender', title: __('Gender'), visible: false, searchList: {1: __('Male'), 0: __('Female')}}, + {field: 'score', title: __('Score'), operate: 'BETWEEN', sortable: true}, + {field: 'successions', title: __('Successions'), visible: false, operate: 'BETWEEN', sortable: true}, + {field: 'maxsuccessions', title: __('Maxsuccessions'), visible: false, operate: 'BETWEEN', sortable: true}, + {field: 'logintime', title: __('Logintime'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true}, + {field: 'loginip', title: __('Loginip'), formatter: Table.api.formatter.search}, + {field: 'jointime', title: __('Jointime'), formatter: Table.api.formatter.datetime, operate: 'RANGE', addclass: 'datetimerange', sortable: true}, + {field: 'joinip', title: __('Joinip'), formatter: Table.api.formatter.search}, + {field: 'status', title: __('Status'), formatter: Table.api.formatter.status, searchList: {normal: __('Normal'), hidden: __('Hidden')}}, + {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate} + ] + ] + }); + + // 为表格绑定事件 + Table.api.bindevent(table); + }, + add: function () { + Controller.api.bindevent(); + }, + edit: function () { + Controller.api.bindevent(); + }, + api: { + bindevent: function () { + Form.api.bindevent($("form[role=form]")); + } + } + }; + return Controller; +}); \ No newline at end of file diff --git a/public/assets/js/frontend.js b/public/assets/js/frontend.js index 27dba578..2e8ddb1c 100644 --- a/public/assets/js/frontend.js +++ b/public/assets/js/frontend.js @@ -2,26 +2,6 @@ define(['fast'], function (Fast) { var Frontend = { api: Fast.api, init: function () { - //发送邮箱验证码 - $(document).on("click", ".btn-email", function (e) { - var email = $(this).closest("form").find("#email"); - if (email.val() == "") { - Layer.alert("邮箱不能为空!"); - return false; - } - var that = this; - email.isValid(function (v) { - if (v) { - Frontend.api.ajax({url: "sms/sendemail", data: {type: $(that).data("type"), email: email.val()}}, function () { - $(that).val("已发送"); - }); - } else { - Layer.alert("请确认已经输入了正解的邮箱!"); - } - }); - - return false; - }); //发送手机验证码 $(document).on("click", ".btn-captcha", function (e) { var mobile = $(this).closest("form").find("#mobile"); @@ -37,8 +17,7 @@ define(['fast'], function (Fast) { if (v) { $(that).addClass("disabled", true).text("获取中..."); var si; - Frontend.api.ajax({url: "sms/send", data: {type: $(that).data("type"), mobile: mobile.val()}}, function () { - Layer.msg("验证码已发送"); + Frontend.api.ajax({url: $(that).data("url"), data: {event: $(that).data("event"), mobile: mobile.val()}}, function () { clearInterval(si); var seconds = 60; si = setInterval(function () { diff --git a/public/assets/js/frontend/user.js b/public/assets/js/frontend/user.js new file mode 100644 index 00000000..870cfc92 --- /dev/null +++ b/public/assets/js/frontend/user.js @@ -0,0 +1,106 @@ +define(['jquery', 'bootstrap', 'frontend', 'form', 'template'], function ($, undefined, Frontend, Form, Template) { + var Controller = { + login: function () { + //本地验证未通过时提示 + $("#login-form").data("validator-options", { + invalid: function (form, errors) { + $.each(errors, function (i, j) { + Layer.alert(j); + }); + }, + }); + + //为表单绑定事件 + Form.api.bindevent($("#login-form"), function (data, ret) { + setTimeout(function () { + location.href = ret.url ? ret.url : "/"; + }, 1000); + }); + + Form.api.bindevent($("#resetpwd-form"), function (data) { + Layer.closeAll(); + }); + + $(document).on("click", ".btn-forgot", function () { + var id = "resetpwdtpl"; + var content = Template(id, {}); + Layer.open({ + type: 1, + title: "修改", + area: ["450px", "auto"], + content: content, + success: function (layero) { + Form.api.bindevent($("#resetpwd-form", layero), function (data) { + Layer.closeAll(); + }); + } + }); + }); + }, + register: function () { + //本地验证未通过时提示 + $("#register-form").data("validator-options", { + invalid: function (form, errors) { + $.each(errors, function (i, j) { + Layer.alert(j); + }); + }, + }); + + //为表单绑定事件 + Form.api.bindevent($("#register-form"), function (data, ret) { + setTimeout(function () { + location.href = ret.url ? ret.url : "/"; + }, 1000); + }); + }, + changepwd: function () { + //本地验证未通过时提示 + $("#resetpwd-form").data("validator-options", { + invalid: function (form, errors) { + $.each(errors, function (i, j) { + Layer.alert(j); + }); + }, + }); + + //为表单绑定事件 + Form.api.bindevent($("#changepwd-form"), function (data, ret) { + setTimeout(function () { + location.href = ret.url ? ret.url : "/"; + }, 1000); + }); + }, + profile: function () { + // 给上传按钮添加上传成功事件 + $("#plupload-avatar").data("upload-success", function (data) { + var url = Fast.api.cdnurl(data.url); + $(".profile-user-img").prop("src", url); + Toastr.success("上传成功!"); + }); + + //为表单绑定事件 + Form.api.bindevent($("#profile-form"), function (data) { + }); + Form.api.bindevent($("#email-form"), function (data) { + Layer.closeAll(); + $("#basic-form #email").val($("#email").val()); + }); + Form.api.bindevent($("#mobile-form"), function (data) { + Layer.closeAll(); + $("#basic-form #mobile").val($("#mobile").val()); + }); + $(document).on("click", ".btn-change", function () { + var id = $(this).data("type") + "tpl"; + var content = Template(id, {}); + Layer.open({ + type: 1, + title: "修改", + area: ["450px", "auto"], + content: content, + }); + }); + } + }; + return Controller; +}); \ No newline at end of file diff --git a/public/assets/js/require-backend.min.js b/public/assets/js/require-backend.min.js index 7c61b99a..66f0a272 100644 --- a/public/assets/js/require-backend.min.js +++ b/public/assets/js/require-backend.min.js @@ -9702,7 +9702,10 @@ define('table',['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstr //Bootstrap操作区 var toolbar = $(options.toolbar, parenttable); //当刷新表格时 - table.on('load-error.bs.table', function (status, res) { + table.on('load-error.bs.table', function (status, res, e) { + if (e.status === 0) { + return; + } Toastr.error(__('Unknown data format')); }); //当刷新表格时 @@ -10017,9 +10020,16 @@ define('table',['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstr var options = table ? table.bootstrapTable('getOptions') : {}; // 默认按钮组 var buttons = $.extend([], this.buttons || []); - buttons.push({name: 'dragsort', icon: 'fa fa-arrows', classname: 'btn btn-xs btn-primary btn-dragsort'}); - buttons.push({name: 'edit', icon: 'fa fa-pencil', classname: 'btn btn-xs btn-success btn-editone', url: options.extend.edit_url}); - buttons.push({name: 'del', icon: 'fa fa-trash', classname: 'btn btn-xs btn-danger btn-delone'}); + + if (options.extend.dragsort_url !== '') { + buttons.push({name: 'dragsort', icon: 'fa fa-arrows', classname: 'btn btn-xs btn-primary btn-dragsort'}); + } + if (options.extend.edit_url !== '') { + buttons.push({name: 'edit', icon: 'fa fa-pencil', classname: 'btn btn-xs btn-success btn-editone', url: options.extend.edit_url}); + } + if (options.extend.del_url !== '') { + buttons.push({name: 'del', icon: 'fa fa-trash', classname: 'btn btn-xs btn-danger btn-delone'}); + } return Table.api.buttonlink(this, buttons, value, row, index, 'operate'); }, buttons: function (value, row, index) { @@ -12744,6 +12754,13 @@ define('form',['jquery', 'bootstrap', 'upload', 'validator'], function ($, undef display: function (elem) { return $(elem).closest('.form-group').find(".control-label").text().replace(/\:/, ''); }, + dataFilter: function (data) { + if (data.code === 1) { + return ""; + } else { + return data.msg; + } + }, target: function (input) { var $formitem = $(input).closest('.form-group'), $msgbox = $formitem.find('span.msg-box'); diff --git a/public/assets/js/require-form.js b/public/assets/js/require-form.js index 365e48bb..b49f36fe 100755 --- a/public/assets/js/require-form.js +++ b/public/assets/js/require-form.js @@ -17,6 +17,13 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U display: function (elem) { return $(elem).closest('.form-group').find(".control-label").text().replace(/\:/, ''); }, + dataFilter: function (data) { + if (data.code === 1) { + return ""; + } else { + return data.msg; + } + }, target: function (input) { var $formitem = $(input).closest('.form-group'), $msgbox = $formitem.find('span.msg-box'); diff --git a/public/assets/js/require-frontend.min.js b/public/assets/js/require-frontend.min.js index ff8e0ce1..750e1ace 100644 --- a/public/assets/js/require-frontend.min.js +++ b/public/assets/js/require-frontend.min.js @@ -2282,26 +2282,6 @@ define('frontend',['fast'], function (Fast) { var Frontend = { api: Fast.api, init: function () { - //发送邮箱验证码 - $(document).on("click", ".btn-email", function (e) { - var email = $(this).closest("form").find("#email"); - if (email.val() == "") { - Layer.alert("邮箱不能为空!"); - return false; - } - var that = this; - email.isValid(function (v) { - if (v) { - Frontend.api.ajax({url: "sms/sendemail", data: {type: $(that).data("type"), email: email.val()}}, function () { - $(that).val("已发送"); - }); - } else { - Layer.alert("请确认已经输入了正解的邮箱!"); - } - }); - - return false; - }); //发送手机验证码 $(document).on("click", ".btn-captcha", function (e) { var mobile = $(this).closest("form").find("#mobile"); @@ -2317,8 +2297,7 @@ define('frontend',['fast'], function (Fast) { if (v) { $(that).addClass("disabled", true).text("获取中..."); var si; - Frontend.api.ajax({url: "sms/send", data: {type: $(that).data("type"), mobile: mobile.val()}}, function () { - Layer.msg("验证码已发送"); + Frontend.api.ajax({url: $(that).data("url"), data: {event: $(that).data("event"), mobile: mobile.val()}}, function () { clearInterval(si); var seconds = 60; si = setInterval(function () { diff --git a/public/assets/js/require-table.js b/public/assets/js/require-table.js index 251f7444..bdb265f7 100644 --- a/public/assets/js/require-table.js +++ b/public/assets/js/require-table.js @@ -101,7 +101,10 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table //Bootstrap操作区 var toolbar = $(options.toolbar, parenttable); //当刷新表格时 - table.on('load-error.bs.table', function (status, res) { + table.on('load-error.bs.table', function (status, res, e) { + if (e.status === 0) { + return; + } Toastr.error(__('Unknown data format')); }); //当刷新表格时 @@ -416,9 +419,16 @@ define(['jquery', 'bootstrap', 'moment', 'moment/locale/zh-cn', 'bootstrap-table var options = table ? table.bootstrapTable('getOptions') : {}; // 默认按钮组 var buttons = $.extend([], this.buttons || []); - buttons.push({name: 'dragsort', icon: 'fa fa-arrows', classname: 'btn btn-xs btn-primary btn-dragsort'}); - buttons.push({name: 'edit', icon: 'fa fa-pencil', classname: 'btn btn-xs btn-success btn-editone', url: options.extend.edit_url}); - buttons.push({name: 'del', icon: 'fa fa-trash', classname: 'btn btn-xs btn-danger btn-delone'}); + + if (options.extend.dragsort_url !== '') { + buttons.push({name: 'dragsort', icon: 'fa fa-arrows', classname: 'btn btn-xs btn-primary btn-dragsort'}); + } + if (options.extend.edit_url !== '') { + buttons.push({name: 'edit', icon: 'fa fa-pencil', classname: 'btn btn-xs btn-success btn-editone', url: options.extend.edit_url}); + } + if (options.extend.del_url !== '') { + buttons.push({name: 'del', icon: 'fa fa-trash', classname: 'btn btn-xs btn-danger btn-delone'}); + } return Table.api.buttonlink(this, buttons, value, row, index, 'operate'); }, buttons: function (value, row, index) {