├── .bowerrc
├── .gitattributes
├── .gitignore
├── LICENSE.md
├── README.md
├── actions
└── GalleryAction.php
├── assets
├── AppAsset.php
├── BaseAsset.php
├── ClockPickerAsset.php
├── CountDownAsset.php
├── GalleryAsset.php
├── MetisMenuAsset.php
├── MultiSelectAsset.php
└── ToasterAsset.php
├── commands
└── HelloController.php
├── components
├── QiNiu.php
└── Ucpaas.php
├── composer.json
├── composer.lock
├── config
├── console.php
├── db.php
├── params.php
└── web.php
├── controllers
├── ActivityController.php
├── BusinessController.php
├── CategoryController.php
├── OrderController.php
├── ProductController.php
├── SiteController.php
└── UserController.php
├── lazy_waimai.sql
├── mail
└── layouts
│ └── html.php
├── models
├── AccountLoginForm.php
├── Activity.php
├── Admin.php
├── Business.php
├── BusinessActivity.php
├── BusinessScene.php
├── Category.php
├── CategorySearch.php
├── ChangePhoneForm.php
├── LoginSendSmsForm.php
├── Order.php
├── OrderSearch.php
├── PhoneLoginForm.php
├── Product.php
├── ProductSearch.php
├── SendChangePhoneSmsForm.php
├── UpdatePasswordForm.php
├── User.php
└── VerifyPasswordForm.php
├── requirements.php
├── runtime
└── .gitignore
├── tests
├── README.md
├── codeception.yml
└── codeception
│ ├── .gitignore
│ ├── _bootstrap.php
│ ├── _output
│ └── .gitignore
│ ├── _pages
│ ├── AboutPage.php
│ ├── ContactPage.php
│ └── LoginPage.php
│ ├── acceptance.suite.yml
│ ├── acceptance
│ ├── AboutCept.php
│ ├── ContactCept.php
│ ├── HomeCept.php
│ ├── LoginCept.php
│ └── _bootstrap.php
│ ├── bin
│ ├── _bootstrap.php
│ ├── yii
│ └── yii.bat
│ ├── config
│ ├── acceptance.php
│ ├── config.php
│ ├── functional.php
│ └── unit.php
│ ├── fixtures
│ └── .gitignore
│ ├── functional.suite.yml
│ ├── functional
│ ├── AboutCept.php
│ ├── ContactCept.php
│ ├── HomeCept.php
│ ├── LoginCept.php
│ └── _bootstrap.php
│ ├── templates
│ └── .gitignore
│ ├── unit.suite.yml
│ └── unit
│ ├── _bootstrap.php
│ ├── fixtures
│ ├── .gitkeep
│ └── data
│ │ └── .gitkeep
│ ├── models
│ ├── ContactFormTest.php
│ ├── LoginFormTest.php
│ └── UserTest.php
│ └── templates
│ └── fixtures
│ └── .gitkeep
├── upload
└── images
│ └── .gitignore
├── views
├── activity
│ └── index.php
├── business
│ ├── profile.php
│ ├── scene.php
│ └── setup.php
├── category
│ ├── form.php
│ └── index.php
├── gallery.php
├── layouts
│ ├── base.php
│ └── main.php
├── order
│ └── index.php
├── product
│ ├── form.php
│ └── index.php
├── site
│ ├── error.php
│ └── index.php
└── user
│ ├── login.php
│ ├── profile.php
│ ├── update-password.php
│ └── update-phone.php
├── web
├── .htaccess
├── assets
│ └── .gitignore
├── css
│ ├── gallery.css
│ ├── lightbox.css
│ ├── login.css
│ └── site.css
├── favicon.ico
├── images
│ ├── close.png
│ ├── loading.gif
│ ├── login_bgx.gif
│ ├── login_border_bg.png
│ ├── logo.png
│ ├── next.png
│ └── prev.png
├── index-test.php
├── index.php
├── js
│ ├── jquery.gallery.js
│ ├── jquery.iframe-transport.js
│ ├── lightbox.js
│ ├── login.js
│ ├── setup.js
│ ├── site.js
│ └── update-phone.js
├── raw
│ └── booking_times.json
└── robots.txt
├── widgets
├── Alert.php
└── Gallery.php
├── yii
└── yii.bat
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory" : "vendor/bower"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.js linguist-language=php
2 | *.css linguist-language=php
3 | *.html linguist-language=php
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | /vendor
3 | .DS_Store
4 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The Yii framework is free software. It is released under the terms of
2 | the following BSD License.
3 |
4 | Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions
9 | are met:
10 |
11 | * Redistributions of source code must retain the above copyright
12 | notice, this list of conditions and the following disclaimer.
13 | * Redistributions in binary form must reproduce the above copyright
14 | notice, this list of conditions and the following disclaimer in
15 | the documentation and/or other materials provided with the
16 | distribution.
17 | * Neither the name of Yii Software LLC nor the names of its
18 | contributors may be used to endorse or promote products derived
19 | from this software without specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | POSSIBILITY OF SUCH DAMAGE.
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | LazyWaimai-Web
2 | ==========
3 | 此项目是懒人外卖(本人用来练手的项目,类似于百度外卖,美团外卖和饿了么的系统)的商家端,为[Android客户端](https://github.com/cheikh-wang/LazyWaimai-Android)提供商铺管理服务,基于 [Yii2](https://github.com/yiisoft/yii2) 框架实现的。
4 |
5 | 环境条件
6 | -------
7 | + PHP版本必须大于或等于php5.4
8 |
9 | 安装
10 | -------
11 | #### 1.clone到本地
12 | ```
13 | git clone -b develop https://github.com/cheikh-wang/LazyWaimai-Web.git
14 | ```
15 | #### 2.配置数据库
16 | 1. 将sql文件导入到数据库中
17 |
18 | 2. 配置数据库
19 | ```
20 | cd LazyWaimai-Web
21 | vi config/web.php // 将数据库密码修改成你本机的数据库密码
22 | ```
23 | #### 3.安装依赖
24 | 本项目使用composer管理依赖,所以需要先安装composer(已安装请跳过)
25 | ```
26 | curl -sS https://getcomposer.org/installer | php
27 | mv composer.phar /usr/local/bin/composer
28 | ```
29 | 还需要安装composer-asset-plugin(已安装请跳过)
30 | ```
31 | composer global require "fxp/composer-asset-plugin:^1.3.1"
32 | ```
33 |
34 | 安装项目所需依赖(开始之前请确保composer和composer-asset-plugin已成功安装)
35 | ```
36 | composer install
37 | ```
38 | #### 5.配置服务器
39 | ```
40 | 配置nginx/apache的webroot指向LazyWaimai-Web/web
41 | ```
42 | #### 6.完毕
43 |
44 | 安装完毕,打开你的服务器访问地址:http://localhost:端口号, 默认的管理员账号,用户名:admin,密码:123456
45 |
46 | 其他配置
47 | -------
48 | #### 1.短信服务的配置
49 | ###### 本项目的短信服务是使用的[云之讯](http://www.ucpaas.com),请自行注册账户并按如下方式配置:
50 |
51 | 编辑config/web.php
52 |
53 | ```
54 | 'ucpass' => [
55 | 'class' => 'app\components\Ucpaas',
56 | 'accountSid' => '修改为你的云之讯Account Sid',
57 | 'token' => '修改为你的云之讯Auth Token',
58 | 'appId' => '修改为你的云之讯应用ID',
59 | 'templateId' => '修改为你的云之讯短信模板ID',
60 | ],
61 | ```
62 | #### 2.七牛云的配置
63 | ###### 本项目的图片上传服务是使用的[七牛](http://www.qiniu.com),请自行注册账户并按如下方式配置:
64 | ```
65 | 'qiniu' => [
66 | 'class' => 'app\components\QiNiu',
67 | 'accessKey' => '修改为你的AccessKey',
68 | 'secretKey' => '修改为你的SecretKey',
69 | 'bucket' => '修改为你的空间名',
70 | 'domain' => '修改为你的域名',
71 | ],
72 | ```
73 |
--------------------------------------------------------------------------------
/actions/GalleryAction.php:
--------------------------------------------------------------------------------
1 | actionDelete(Yii::$app->request->post('id'));
22 | break;
23 | case 'ajaxUpload':
24 | return $this->actionAjaxUpload(Yii::$app->request->get('type'));
25 | break;
26 | case 'order':
27 | return $this->actionOrder(Yii::$app->request->post('order'));
28 | break;
29 | default:
30 | throw new HttpException(400, 'Action do not exists');
31 | break;
32 | }
33 | }
34 |
35 | /**
36 | * Removes image with ids specified in post request.
37 | * On success returns 'OK'
38 | *
39 | * @param $id
40 | * @return string
41 | * @throws Exception
42 | */
43 | protected function actionDelete($id) {
44 | try {
45 | /** @var $scene BusinessScene */
46 | $scene = BusinessScene::findOne($id);
47 | if (!$scene) {
48 | throw new Exception('未找到该资源!');
49 | }
50 | // 删除数据库文件记录
51 | if (!$scene->delete()) {
52 | throw new Exception('图片删除失败');
53 | }
54 | // 删除原图
55 | Yii::$app->qiniu->delete($scene->original_name);
56 | // 删除缩略图
57 | Yii::$app->qiniu->delete($scene->thumb_name);
58 |
59 | return Json::encode([
60 | 'status' => 'ok'
61 | ]);
62 | } catch (Exception $e) {
63 | return Json::encode([
64 | 'status' => 'err',
65 | 'message' => $e->getMessage()
66 | ]);
67 | }
68 | }
69 |
70 | /**
71 | * Method to handle file upload thought XHR2
72 | * On success returns JSON object with image info.
73 | *
74 | * @param $type
75 | * @return string
76 | * @throws Exception
77 | */
78 | public function actionAjaxUpload($type) {
79 | try {
80 | $imageFile = UploadedFile::getInstanceByName('image');
81 |
82 | // 生成原图名称和缩略图名称
83 | $randomStr = Yii::$app->security->generateRandomString(20);
84 | $originalName = $randomStr . '.' . $imageFile->extension;
85 | $thumbName = $randomStr . '_145x145.' . $imageFile->extension;
86 |
87 | $originalImage = Image::getImagine()->open($imageFile->tempName);
88 | $basePath = Yii::getAlias(Yii::$app->params['imageUploadPath']);
89 |
90 | // 临时保存原图
91 | $originalPath = $basePath . DIRECTORY_SEPARATOR . $originalName;
92 | if (!$originalImage->save($originalPath)) {
93 | throw new Exception('保存图片失败!');
94 | }
95 |
96 | // 临时保存缩略图
97 | $thumbPath = $basePath . DIRECTORY_SEPARATOR . $thumbName;
98 | if (!$originalImage->copy()->thumbnail(new Box(145, 145))->save($thumbPath)) {
99 | throw new Exception('保存图片失败!');
100 | }
101 |
102 | // 上传原图和缩略图到七牛
103 | $originalUrl = Yii::$app->qiniu->uploadFile($originalPath, $originalName);
104 | $thumbUrl = Yii::$app->qiniu->uploadFile($thumbPath, $thumbName);
105 |
106 | // 删除临时的图片文件
107 | if (file_exists($originalPath)) {
108 | @unlink($originalPath);
109 | }
110 | if (file_exists($thumbPath)) {
111 | @unlink($thumbPath);
112 | }
113 |
114 | /* @var $admin Admin */
115 | $admin = Admin::findOne(Yii::$app->user->id);
116 |
117 | // 保存信息到数据库
118 | $model = new BusinessScene();
119 | $model->business_id = $admin->business_id;
120 | $model->type = $type;
121 | $model->rank = 1;
122 | $model->original_name = $originalName;
123 | $model->original_url = $originalUrl;
124 | $model->thumb_name = $thumbName;
125 | $model->thumb_url = $thumbUrl;
126 | if (!$model->save(false)) {
127 | throw new Exception('保存图片记录失败!');
128 | }
129 |
130 | Yii::$app->response->headers->set('Content-Type', 'text/html');
131 |
132 | return Json::encode([
133 | 'status' => 'ok',
134 | 'data' => [
135 | 'id' => $model->id,
136 | 'original_url' => $originalUrl,
137 | 'thumb_url' => $thumbUrl,
138 | 'rank' => $model->rank
139 | ]
140 | ]);
141 | } catch (Exception $e) {
142 | return Json::encode([
143 | 'status' => 'err',
144 | 'message' => $e->getMessage()
145 | ]);
146 | }
147 | }
148 |
149 | /**
150 | * Saves images order according to request.
151 | *
152 | * @param array $order new arrange of image ids, to be saved
153 | *
154 | * @return string
155 | * @throws HttpException
156 | */
157 | public function actionOrder($order) {
158 | if (count($order) == 0) {
159 | throw new HttpException(400, 'No data, to save');
160 | }
161 | $orders = [];
162 | $i = 0;
163 | foreach ($order as $k => $v) {
164 | if (!$v) {
165 | $order[$k] = $k;
166 | }
167 | $orders[] = $order[$k];
168 | $i++;
169 | }
170 | sort($orders);
171 | $i = 0;
172 | $res = [];
173 | foreach ($order as $k => $v) {
174 | $res[$k] = $orders[$i];
175 | // TODO 更新 rank
176 | // \Yii::$app->db->createCommand()
177 | // ->update(
178 | // $this->tableName,
179 | // ['rank' => $orders[$i]],
180 | // ['id' => $k]
181 | // )->execute();
182 |
183 | $i++;
184 | }
185 |
186 | return Json::encode($order);
187 | }
188 | }
--------------------------------------------------------------------------------
/assets/AppAsset.php:
--------------------------------------------------------------------------------
1 | registerJsFile($jsfile, [BaseAsset::className(), 'depends' => 'app\assets\BaseAsset']);
28 | }
29 |
30 | /**
31 | * 定义按需加载css方法,注意加载顺序在最后
32 | * @param $view View
33 | * @param $cssfile string
34 | */
35 | public static function addCss($view, $cssfile) {
36 | $view->registerCssFile($cssfile, [BaseAsset::className(), 'depends' => 'app\assets\BaseAsset']);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/assets/ClockPickerAsset.php:
--------------------------------------------------------------------------------
1 |
18 | * @since 2.0
19 | */
20 | class HelloController extends Controller
21 | {
22 | /**
23 | * This command echoes what you have entered as the message.
24 | * @param string $message the message to be echoed.
25 | */
26 | public function actionIndex($message = 'hello world')
27 | {
28 | echo $message . "\n";
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "LazyWaimai-Web",
3 | "description": "懒人外卖的商家端",
4 | "keywords": ["懒人外卖", "商家端", "LazyWaimai"],
5 | "homepage": "https://github.com/cheikh-wang/LazyWaimai-Web",
6 | "type": "project",
7 | "license": "BSD-3-Clause",
8 | "support": {
9 | "issues": "https://github.com/cheikh-wang/LazyWaimai-Web/issues?state=open",
10 | "source": "https://github.com/cheikh-wang/LazyWaimai-Web"
11 | },
12 | "minimum-stability": "stable",
13 | "require": {
14 | "php": ">=5.4.0",
15 | "yiisoft/yii2": "~2.0.5",
16 | "yiisoft/yii2-bootstrap": "~2.0.0",
17 | "yiisoft/yii2-swiftmailer": "~2.0.0",
18 | "yiisoft/yii2-imagine": "*",
19 | "yiisoft/yii2-jui": "*",
20 |
21 | "kartik-v/yii2-widgets": "*",
22 | "kartik-v/yii2-field-range": "*",
23 | "2amigos/yii2-chartjs-widget": "~2.0",
24 | "rmrevin/yii2-fontawesome": "*",
25 |
26 | "bower-asset/jquery.countdown": "*",
27 | "bower-asset/jquery.toaster": "*",
28 | "bower-asset/bootstrap-multiselect": "*",
29 | "bower-asset/metismenu": "*",
30 | "bower-asset/clockpicker": "*"
31 | },
32 | "require-dev": {
33 | "yiisoft/yii2-debug": "~2.0.0",
34 | "yiisoft/yii2-gii": "~2.0.0",
35 | "yiisoft/yii2-faker": "~2.0.0",
36 |
37 | "codeception/base": "^2.2.3",
38 | "codeception/verify": "~0.3.1",
39 | "codeception/specify": "~0.4.3"
40 | },
41 | "config": {
42 | "process-timeout": 1800,
43 | "fxp-asset":{
44 | "installer-paths": {
45 | "npm-asset-library": "vendor/npm",
46 | "bower-asset-library": "vendor/bower"
47 | }
48 | }
49 | },
50 | "scripts": {
51 | "post-install-cmd": [
52 | "yii\\composer\\Installer::postInstall"
53 | ],
54 | "post-create-project-cmd": [
55 | "yii\\composer\\Installer::postCreateProject",
56 | "yii\\composer\\Installer::postInstall"
57 | ]
58 | },
59 | "extra": {
60 | "yii\\composer\\Installer::postCreateProject": {
61 | "generateCookieValidationKey": [
62 | "config/web.php"
63 | ]
64 | },
65 | "yii\\composer\\Installer::postInstall": {
66 | "setPermission": [
67 | {
68 | "runtime": "0777",
69 | "web/assets": "0777",
70 | "yii": "0755",
71 | "upload/images": "0777"
72 | }
73 | ]
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/config/console.php:
--------------------------------------------------------------------------------
1 | 'basic-console',
10 | 'basePath' => dirname(__DIR__),
11 | 'bootstrap' => ['log', 'gii'],
12 | 'controllerNamespace' => 'app\commands',
13 | 'modules' => [
14 | 'gii' => 'yii\gii\Module',
15 | ],
16 | 'components' => [
17 | 'cache' => [
18 | 'class' => 'yii\caching\FileCache',
19 | ],
20 | 'log' => [
21 | 'targets' => [
22 | [
23 | 'class' => 'yii\log\FileTarget',
24 | 'levels' => ['error', 'warning'],
25 | ],
26 | ],
27 | ],
28 | 'db' => $db,
29 | ],
30 | 'params' => $params,
31 | ];
32 |
--------------------------------------------------------------------------------
/config/db.php:
--------------------------------------------------------------------------------
1 | 'yii\db\Connection',
5 | 'dsn' => 'mysql:host=localhost;dbname=lazy_waimai',
6 | 'username' => 'root',
7 | 'password' => '123456',
8 | 'charset' => 'utf8',
9 | ];
10 |
--------------------------------------------------------------------------------
/config/params.php:
--------------------------------------------------------------------------------
1 | 'admin@example.com',
5 |
6 | // 图片配置
7 | 'imageBaseUrl' => 'http://localhost/LazyWaimai-Web',
8 |
9 | 'imageUploadPath' => '@upload/images'
10 | ];
11 |
--------------------------------------------------------------------------------
/config/web.php:
--------------------------------------------------------------------------------
1 | 'basic',
7 | 'name' => '懒人外卖管理平台',
8 | 'basePath' => dirname(__DIR__),
9 | 'bootstrap' => ['log'],
10 | 'aliases' => [
11 | '@upload' => '@app/upload',
12 | ],
13 | 'components' => [
14 | 'request' => [
15 | // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
16 | 'cookieValidationKey' => 'MrwoEsBNtoOdrBTUNbVufuYhN6P6OvzM',
17 | ],
18 | 'cache' => [
19 | 'class' => 'yii\caching\FileCache',
20 | ],
21 | 'user' => [
22 | 'identityClass' => 'app\models\Admin',
23 | 'enableAutoLogin' => true,
24 | 'loginUrl' => ['user/login']
25 | ],
26 | 'urlManager' => [
27 | 'enablePrettyUrl' => true,
28 | 'showScriptName' => false
29 | ],
30 | 'ucpass' => [
31 | 'class' => 'app\components\Ucpaas',
32 | 'accountSid' => '3550344c4a362cffbb14ca55b4683772',
33 | 'token' => '2e3fcf93c16a77209145f74e3c234532',
34 | 'appId' => 'a8ef8cf06c1b4cef81ed57d7de7ceead',
35 | 'templateId' => '12084',
36 | ],
37 | 'qiniu' => [
38 | 'class' => 'app\components\QiNiu',
39 | 'accessKey' => 'jqg0WMfuswsDOXwEywf_imIMl0iWGPPM5gRU7zfv',
40 | 'secretKey' => 'Q0iJyLo1NcwTf5RVx-qQtqvAXPcfDGq38uJRXNX3',
41 | 'bucket' => 'lazywaimai',
42 | 'domain' => 'ocd605cmo.bkt.clouddn.com',
43 | ],
44 | 'errorHandler' => [
45 | 'errorAction' => 'site/error',
46 | ],
47 | 'mailer' => [
48 | 'class' => 'yii\swiftmailer\Mailer',
49 | // send all mails to a file by default. You have to set
50 | // 'useFileTransport' to false and configure a transport
51 | // for the mailer to send real emails.
52 | 'useFileTransport' => true,
53 | ],
54 | 'log' => [
55 | 'traceLevel' => YII_DEBUG ? 3 : 0,
56 | 'targets' => [
57 | [
58 | 'class' => 'yii\log\FileTarget',
59 | 'levels' => ['error', 'warning'],
60 | ],
61 | ],
62 | ],
63 | 'db' => require(__DIR__ . '/db.php'),
64 | ],
65 | 'params' => $params,
66 | ];
67 |
68 | if (YII_ENV_DEV) {
69 | // configuration adjustments for 'dev' environment
70 | $config['bootstrap'][] = 'debug';
71 | $config['modules']['debug'] = 'yii\debug\Module';
72 |
73 | $config['bootstrap'][] = 'gii';
74 | $config['modules']['gii'] = 'yii\gii\Module';
75 | }
76 |
77 | return $config;
78 |
--------------------------------------------------------------------------------
/controllers/ActivityController.php:
--------------------------------------------------------------------------------
1 | [
27 | 'class' => AccessControl::className(),
28 | 'rules' => [
29 | [
30 | 'allow' => true,
31 | 'roles' => ['@'],
32 | ],
33 | ],
34 | ]
35 | ];
36 | }
37 |
38 | /**
39 | * 浏览商品列表的操作
40 | *
41 | * @return string
42 | */
43 | public function actionIndex() {
44 | $model = new Activity();
45 | $dataProvider = new ActiveDataProvider([
46 | 'query' => BusinessActivity::activities(2),
47 | // 'sort' => [
48 | // 'defaultOrder' => ['priority' => SORT_ASC]
49 | // ],
50 | 'pagination' => [
51 | 'pageSize' => 20
52 | ]
53 | ]);
54 |
55 | return $this->render('index', [
56 | 'dataProvider' => $dataProvider,
57 | 'model' => $model
58 | ]);
59 | }
60 |
61 | /**
62 | * 添加商品的操作
63 | * @return string|\yii\web\Response
64 | * @throws Exception
65 | */
66 | public function actionAdd() {
67 | $model = new Product();
68 | $model->setScenario('insert');
69 |
70 | if ($model->load(Yii::$app->request->post())) {
71 |
72 | $model->image = UploadedFile::getInstance($model, 'image');
73 | $model->business_id = Yii::$app->user->id;
74 |
75 | if ($model->validate()) {
76 | // 通过事务来保存数据
77 | $transaction = Yii::$app->db->beginTransaction();
78 | try {
79 | // 生成一个随机的图片名并保存到数据库
80 | $model->image_path = Yii::$app->security->generateRandomString(10).'.'.$model->image->extension;
81 | if (!$model->save(false)) {
82 | throw new Exception('商品添加失败!');
83 | }
84 |
85 | // 保存上传图片到服务器的指定目录
86 | $filename = Yii::getAlias(Yii::$app->params['product.imagePath']).DIRECTORY_SEPARATOR.$model->image_path;
87 | if (!$model->image->saveAs($filename)) {
88 | throw new Exception('商品图片添加失败!');
89 | }
90 |
91 | $transaction->commit();
92 | Yii::$app->session->setFlash('success', '成功添加商品“'.$model->name.'”。');
93 |
94 | return $this->refresh();
95 | } catch (Exception $e) {
96 | $transaction->rollBack();
97 | Yii::$app->session->setFlash('danger', $e->getMessage());
98 | }
99 | }
100 | }
101 |
102 | return $this->render('form', [
103 | 'model' => $model
104 | ]);
105 | }
106 |
107 | /**
108 | * 更新商品的操作
109 | * @param $id
110 | * @return string|\yii\web\Response
111 | * @throws Exception
112 | * @throws NotFoundHttpException
113 | * @throws \yii\db\Exception
114 | */
115 | public function actionUpdate($id) {
116 | /* @var $model Product */
117 | $model = Product::findOne($id);
118 |
119 | if (!$model) {
120 | throw new NotFoundHttpException('未找到该商品。');
121 | }
122 |
123 | if ($model->load(Yii::$app->request->post())) {
124 |
125 | $model->image = UploadedFile::getInstance($model, 'image');
126 |
127 | if ($model->validate()) {
128 | if ($model->image !== null) {
129 | $model->image_path = Yii::$app->security->generateRandomString(10).'.'.$model->image->extension;
130 | }
131 |
132 | $transaction = Yii::$app->db->beginTransaction();
133 | try {
134 | if (!$model->save(false)) {
135 | throw new Exception('商品更新失败!');
136 | }
137 |
138 | if ($model->image !== null) {
139 | $filename = Yii::getAlias(Yii::$app->params['product.imagePath']).DIRECTORY_SEPARATOR.$model->image_path;
140 | if (!$model->image->saveAs($filename)) {
141 | throw new Exception('商品图片添加失败!');
142 | }
143 | }
144 |
145 | $transaction->commit();
146 | Yii::$app->session->setFlash('success', '成功更新商品“'.$model->name.'”。');
147 |
148 | return $this->refresh();
149 | } catch (Exception $e) {
150 | $transaction->rollBack();
151 | Yii::$app->session->setFlash('danger', $e->getMessage());
152 | }
153 | }
154 | }
155 |
156 | return $this->render('form', [
157 | 'model' => $model
158 | ]);
159 | }
160 |
161 | /**
162 | * 删除商品的操作
163 | * @param $id
164 | * @return \yii\web\Response
165 | * @throws Exception
166 | * @throws NotFoundHttpException
167 | */
168 | public function actionDelete($id) {
169 | /* @var $model Product */
170 | $model = Product::findOne($id);
171 |
172 | if (!$model) {
173 | throw new NotFoundHttpException('未找到该商品。');
174 | }
175 |
176 | // 删除数据库数据
177 | if ($model->delete()) {
178 | Yii::$app->session->setFlash('success', '成功删除商品“'.$model->name.'”。');
179 | } else {
180 | Yii::$app->session->setFlash('danger', '删除商品失败。');
181 | }
182 |
183 | return $this->redirect(['activity/index']);
184 | }
185 | }
--------------------------------------------------------------------------------
/controllers/BusinessController.php:
--------------------------------------------------------------------------------
1 | [
26 | 'class' => AccessControl::className(),
27 | 'rules' => [
28 | [
29 | 'allow' => true,
30 | 'roles' => ['@'],
31 | ],
32 | ],
33 | ],
34 | ];
35 | }
36 |
37 | /**
38 | * @inheritdoc
39 | */
40 | public function actions() {
41 | return [
42 | 'gallery' => [
43 | 'class' => GalleryAction::className(),
44 | ],
45 | ];
46 | }
47 |
48 | /**
49 | * 店铺设置的操作
50 | */
51 | public function actionSetup() {
52 | /* @var $admin Admin */
53 | $admin = Admin::findOne(Yii::$app->user->id);
54 | /* @var $model Business */
55 | $model = Business::findOne($admin->business_id);
56 |
57 | $json = file_get_contents(Yii::getAlias('@webroot').'/raw/booking_times.json');
58 | $bookingTimes = Json::decode($json);
59 |
60 | if ($model->load(Yii::$app->request->post()) && $model->validate()) {
61 | if ($model->save()) {
62 | Yii::$app->session->setFlash('success', '店铺设置成功。');
63 | } else {
64 | Yii::$app->session->setFlash('error', '系统异常,店铺设置失败。');
65 | }
66 | }
67 |
68 | return $this->render('setup', [
69 | 'model' => $model,
70 | 'bookingTimes' => $bookingTimes
71 | ]);
72 | }
73 |
74 | /**
75 | * 基本资料的操作
76 | */
77 | public function actionProfile() {
78 | /* @var $admin Admin */
79 | $admin = Admin::findOne(Yii::$app->user->id);
80 | /* @var $model Business */
81 | $model = Business::findOne($admin->business_id);
82 |
83 | if ($model->load(Yii::$app->request->post())) {
84 |
85 | $model->image = UploadedFile::getInstance($model, 'image');
86 |
87 | if ($model->validate()) {
88 | if ($model->image != null) {
89 | // 随机生成图片名
90 | $filename = Yii::$app->security->generateRandomString(10).'.'.$model->image->extension;
91 | // 使用七牛上传图片
92 | $model->pic_url = Yii::$app->qiniu->uploadFile($model->image->tempName, $filename);
93 | }
94 |
95 | if (!$model->save(false)) {
96 | Yii::$app->session->setFlash('danger', '资料修改失败');
97 | }
98 |
99 | Yii::$app->session->setFlash('success', '资料修改成功。');
100 |
101 | return $this->refresh();
102 | }
103 | }
104 |
105 | return $this->render('profile', [
106 | 'model' => $model
107 | ]);
108 | }
109 |
110 | /**
111 | * 店面实景的操作
112 | */
113 | public function actionScene() {
114 | /* @var $admin Admin */
115 | $admin = Admin::findOne(Yii::$app->user->id);
116 |
117 | // 门面
118 | $frontImages = BusinessScene::find()
119 | ->where([
120 | 'business_id' => $admin->business_id,
121 | 'type' => BusinessScene::TYPE_FRONT
122 | ])
123 | ->orderBy(['rank' => 'asc'])
124 | ->all();
125 | // 大厅
126 | $foyerImages = BusinessScene::find()
127 | ->where([
128 | 'business_id' => $admin->business_id,
129 | 'type' => BusinessScene::TYPE_FOYER
130 | ])
131 | ->orderBy(['rank' => 'asc'])
132 | ->all();
133 | // 后厨
134 | $kitchenImages = BusinessScene::find()
135 | ->where([
136 | 'business_id' => $admin->business_id,
137 | 'type' => BusinessScene::TYPE_KITCHEN
138 | ])
139 | ->orderBy(['rank' => 'asc'])
140 | ->all();
141 |
142 | return $this->render('scene', [
143 | 'frontImages' => $frontImages,
144 | 'foyerImages' => $foyerImages,
145 | 'kitchenImages' => $kitchenImages
146 | ]);
147 | }
148 | }
--------------------------------------------------------------------------------
/controllers/CategoryController.php:
--------------------------------------------------------------------------------
1 | [
27 | 'class' => AccessControl::className(),
28 | 'rules' => [
29 | [
30 | 'allow' => true,
31 | 'roles' => ['@'],
32 | ],
33 | ],
34 | ],
35 | ];
36 | }
37 |
38 | /**
39 | * 浏览商品分类的操作
40 | */
41 | public function actionIndex() {
42 | $searchModel = new CategorySearch();
43 | $dataProvider = $searchModel->search(Yii::$app->request->get());
44 |
45 | return $this->render('index', [
46 | 'dataProvider' => $dataProvider,
47 | 'searchModel' => $searchModel
48 | ]);
49 | }
50 |
51 | /**
52 | * 添加商品分类的操作
53 | * @return string|Response
54 | */
55 | public function actionAdd() {
56 | $model = new Category();
57 |
58 | if ($model->load(Yii::$app->request->post())) {
59 | /* @var $admin Admin */
60 | $admin = Admin::findOne(Yii::$app->user->id);
61 | $model->business_id = $admin->business_id;
62 |
63 | if ($model->validate()) {
64 | if ($model->save()) {
65 | Yii::$app->session->setFlash('success', '成功添加分类“'.$model->name.'”。');
66 | return $this->redirect(['index']);
67 | } else {
68 | Yii::$app->session->setFlash('danger', '分类添加失败。');
69 | }
70 | }
71 | }
72 |
73 | return $this->render('form', [
74 | 'model' => $model
75 | ]);
76 | }
77 |
78 | /**
79 | * 修改商品分类的操作
80 | * @param $id
81 | * @return string|Response
82 | * @throws NotFoundHttpException
83 | */
84 | public function actionUpdate($id) {
85 | /** @var $model Category */
86 | $model = Category::findOne($id);
87 |
88 | if (!$model) {
89 | throw new NotFoundHttpException('未找到该分类。');
90 | }
91 |
92 | if ($model->load(Yii::$app->request->post())) {
93 | if ($model->validate()) {
94 | if ($model->save()) {
95 | Yii::$app->session->setFlash('success', '成功更新分类“'.$model->name.'”。');
96 | return $this->refresh();
97 | } else {
98 | Yii::$app->session->setFlash('danger', '分类更新失败。');
99 | }
100 | }
101 | }
102 |
103 | return $this->render('form', [
104 | 'model' => $model
105 | ]);
106 | }
107 |
108 | /**
109 | * 删除商品分类的操作
110 | * @param $id
111 | * @return \yii\web\Response
112 | * @throws Exception
113 | * @throws NotFoundHttpException
114 | */
115 | public function actionDelete($id) {
116 | /* @var $model Category */
117 | $model = Category::findOne($id);
118 |
119 | if (!$model) {
120 | throw new NotFoundHttpException('未找到该商品分类。');
121 | }
122 |
123 | $transaction = Yii::$app->db->beginTransaction();
124 | try {
125 | if (!$model->delete()) {
126 | throw new Exception('删除商品分类失败!');
127 | }
128 |
129 | if (!Product::deleteAll(['category_id' => $model->id])) {
130 | throw new Exception('删除商品分类下的商品失败!');
131 | }
132 |
133 | $transaction->commit();
134 | Yii::$app->session->setFlash('success', '删除商品分类成功“'.$model->name.'”。');
135 |
136 | return $this->refresh();
137 | } catch (Exception $e) {
138 | $transaction->rollBack();
139 | Yii::$app->session->setFlash('danger', $e->getMessage());
140 | }
141 |
142 | return $this->redirect(['category/index']);
143 | }
144 | }
--------------------------------------------------------------------------------
/controllers/OrderController.php:
--------------------------------------------------------------------------------
1 | [
23 | 'class' => AccessControl::className(),
24 | 'rules' => [
25 | [
26 | 'allow' => true,
27 | 'roles' => ['@'],
28 | ],
29 | ],
30 | ],
31 | ];
32 | }
33 |
34 | /**
35 | * 店铺设置的操作
36 | * @param int $status
37 | * @return string
38 | */
39 | public function actionIndex($status = Order::STATUS_WAIT_ACCEPT) {
40 | $searchModel = new OrderSearch();
41 | $dataProvider = $searchModel->search(Yii::$app->request->get());
42 |
43 | return $this->render('index', [
44 | 'status' => $status,
45 | 'dataProvider' => $dataProvider,
46 | 'searchModel' => $searchModel
47 | ]);
48 | }
49 |
50 | public function actionStatus($id) {
51 | $id = Yii::$app->request->get('id');
52 | $currentStatus = Yii::$app->request->get('current');
53 | $targetStatus = Yii::$app->request->get('target');
54 |
55 | /* @var $model Order */
56 | $model = Order::findOne($id);
57 | if (!$model) {
58 | throw new NotFoundHttpException('未找到该订单。');
59 | }
60 | $model->status = $targetStatus;
61 | if ($model->save(false)) {
62 | Yii::$app->session->setFlash('success', '操作成功.');
63 | } else {
64 | Yii::$app->session->setFlash('success', '操作失败.');
65 | }
66 |
67 | return $this->redirect(['order/index', 'status' => $currentStatus]);
68 | }
69 | }
--------------------------------------------------------------------------------
/controllers/ProductController.php:
--------------------------------------------------------------------------------
1 | [
26 | 'class' => AccessControl::className(),
27 | 'rules' => [
28 | [
29 | 'allow' => true,
30 | 'roles' => ['@'],
31 | ],
32 | ],
33 | ],
34 | ];
35 | }
36 |
37 | /**
38 | * 浏览商品列表的操作
39 | *
40 | * @return string
41 | */
42 | public function actionIndex() {
43 | $searchModel = new ProductSearch();
44 | $dataProvider = $searchModel->search(Yii::$app->request->get());
45 |
46 | return $this->render('index', [
47 | 'model' => new Product(),
48 | 'dataProvider' => $dataProvider,
49 | 'searchModel' => $searchModel
50 | ]);
51 | }
52 |
53 | /**
54 | * 添加商品的操作
55 | * @return string|\yii\web\Response
56 | * @throws Exception
57 | */
58 | public function actionAdd() {
59 | $model = new Product();
60 | $model->setScenario('insert');
61 |
62 | if ($model->load(Yii::$app->request->post())) {
63 |
64 | $model->image = UploadedFile::getInstance($model, 'image');
65 |
66 | /* @var $admin Admin */
67 | $admin = Admin::findOne(Yii::$app->user->id);
68 | $model->business_id = $admin->business_id;
69 |
70 | if ($model->validate()) {
71 | // 随机生成图片名
72 | $filename = Yii::$app->security->generateRandomString(10).'.'.$model->image->extension;
73 | // 使用七牛上传图片并保存到数据库
74 | $model->image_path = Yii::$app->qiniu->uploadFile($model->image->tempName, $filename);
75 | if (!$model->save(false)) {
76 | Yii::$app->session->setFlash('danger', '商品添加失败');
77 | }
78 |
79 | Yii::$app->session->setFlash('success', '成功添加商品“'.$model->name.'”。');
80 |
81 | return $this->redirect(['index']);
82 | }
83 | }
84 |
85 | return $this->render('form', [
86 | 'model' => $model
87 | ]);
88 | }
89 |
90 | /**
91 | * 更新商品的操作
92 | * @param $id
93 | * @return string|\yii\web\Response
94 | * @throws Exception
95 | * @throws NotFoundHttpException
96 | * @throws \yii\db\Exception
97 | */
98 | public function actionUpdate($id) {
99 | /* @var $model Product */
100 | $model = Product::findOne($id);
101 |
102 | if (!$model) {
103 | throw new NotFoundHttpException('未找到该商品。');
104 | }
105 |
106 | if ($model->load(Yii::$app->request->post())) {
107 |
108 | $model->image = UploadedFile::getInstance($model, 'image');
109 |
110 | if ($model->validate()) {
111 | // 如果有更新图片
112 | if ($model->image !== null) {
113 | // 随机生成图片名
114 | $filename = Yii::$app->security->generateRandomString(10).'.'.$model->image->extension;
115 | // 使用七牛上传图片
116 | $model->image_path = Yii::$app->qiniu->uploadFile($model->image->tempName, $filename);
117 | }
118 |
119 | if (!$model->save(false)) {
120 | Yii::$app->session->setFlash('danger', '商品更新失败');
121 | }
122 |
123 | Yii::$app->session->setFlash('success', '成功更新商品“'.$model->name.'”。');
124 |
125 | return $this->refresh();
126 | }
127 | }
128 |
129 | return $this->render('form', [
130 | 'model' => $model
131 | ]);
132 | }
133 |
134 | /**
135 | * 删除商品的操作
136 | * @param $id
137 | * @return \yii\web\Response
138 | * @throws Exception
139 | * @throws NotFoundHttpException
140 | */
141 | public function actionDelete($id) {
142 | /* @var $model Product */
143 | $model = Product::findOne($id);
144 |
145 | if (!$model) {
146 | throw new NotFoundHttpException('未找到该商品。');
147 | }
148 |
149 | // 删除数据库数据
150 | if ($model->delete()) {
151 | Yii::$app->session->setFlash('success', '成功删除商品“'.$model->name.'”。');
152 | } else {
153 | Yii::$app->session->setFlash('danger', '删除商品失败。');
154 | }
155 |
156 | return $this->redirect(['product/index']);
157 | }
158 | }
--------------------------------------------------------------------------------
/controllers/SiteController.php:
--------------------------------------------------------------------------------
1 | [
24 | 'class' => AccessControl::className(),
25 | 'only' => ['index', 'error'],
26 | 'rules' => [
27 | [
28 | 'allow' => true,
29 | 'actions' => ['error'],
30 | 'roles' => ['?'],
31 | ],
32 | [
33 | 'allow' => true,
34 | 'actions' => ['index'],
35 | 'roles' => ['@'],
36 | ],
37 | ],
38 | ],
39 | 'verbs' => [
40 | 'class' => VerbFilter::className(),
41 | 'actions' => [
42 | 'index' => ['get', 'post'],
43 | 'error' => ['get', 'post'],
44 | ],
45 | ],
46 | ];
47 | }
48 |
49 | /**
50 | * 主页仪表盘的操作
51 | * @return string
52 | */
53 | public function actionIndex() {
54 | $last15days = [];
55 | $last6Month = [];
56 | $numDataOrder = []; // 订单生成数据
57 | $numDataUser = []; // 用户注册数据
58 | $numDataVolume = []; // 营业额数据
59 | $numDataCompleted = []; // 订单完成数据
60 | $numDataVolumeMonth = []; // 每月营业额
61 |
62 | $today = strtotime("00:00:00");
63 | $todayEnd = strtotime("23:59:59");
64 | for ($i = 0; $i < 15; $i++) {
65 | $timestrap = strtotime('-' . $i . ' days', $today);
66 | $timestrapEnd = strtotime('-' . $i . ' days', $todayEnd);
67 | $where = [
68 | 'and',
69 | ['>=', 'created_at', $timestrap],
70 | ['<=', 'created_at', $timestrapEnd]
71 | ];
72 | array_unshift($last15days, date('m/d', $timestrap));
73 | array_unshift($numDataOrder, Order::find()->where($where)->count());
74 | array_unshift($numDataUser, User::find()->where($where)->count());
75 |
76 | $data = Order::find()->select(['sum(total_price) AS volume', 'count(*) AS count'])
77 | ->where($where)
78 | ->asArray()
79 | ->one();
80 | array_unshift($numDataVolume, $data['volume']);
81 | array_unshift($numDataCompleted, $data['count']);
82 | }
83 |
84 | for ($i = 0; $i < 6; $i ++) {
85 | $timestrap = strtotime("first day of -{$i} month", $today);
86 | $timestrapEnd = strtotime("last day of -{$i} month", $todayEnd);
87 | $where = [
88 | 'and',
89 | ['>=', 'created_at', $timestrap],
90 | ['<=', 'created_at', $timestrapEnd]
91 | ];
92 | array_unshift($last6Month, date('Y/m', $timestrap));
93 | array_unshift($numDataVolumeMonth, Order::find()->where($where)->sum('total_price'));
94 | }
95 |
96 | $data2 = Order::find()->select(['sum(total_price) AS volume', 'count(*) AS count'])
97 | ->asArray()
98 | ->one();
99 |
100 | return $this->render('index', [
101 | 'last15days' => $last15days,
102 | 'last6Month' => $last6Month,
103 | 'numDataOrder' => $numDataOrder,
104 | 'numDataUser' => $numDataUser,
105 | 'numDataVolume' => $numDataVolume,
106 | 'numDataCompleted' => $numDataCompleted,
107 | 'numDataVolumeMonth' => $numDataVolumeMonth,
108 | 'countOrder' => Order::find()->count(),
109 | 'countCompleted' => $data2['count'],
110 | 'sumVolume' => $data2['volume'] ?: '0.00',
111 | 'countUser' => User::find()->count(),
112 | ]);
113 | }
114 |
115 | /**
116 | * 显示系统报错的操作
117 | * @return string
118 | */
119 | public function actionError() {
120 | if (Yii::$app->user->isGuest) {
121 | $this->layout = 'base';
122 | }
123 |
124 | if (($exception = Yii::$app->getErrorHandler()->exception) === null) {
125 | return '';
126 | }
127 |
128 | if ($exception instanceof HttpException) {
129 | $code = $exception->statusCode;
130 | } else {
131 | $code = $exception->getCode();
132 | }
133 | if ($exception instanceof Exception) {
134 | $name = $exception->getName();
135 | } else {
136 | $name = '错误';
137 | }
138 | if ($code) {
139 | $name .= " (#$code)";
140 | }
141 |
142 | if ($exception instanceof UserException) {
143 | $message = $exception->getMessage();
144 | } else {
145 | $message = '服务器错误!';
146 | }
147 |
148 | if (Yii::$app->getRequest()->getIsAjax()) {
149 | return "$name: $message";
150 | } else {
151 | return $this->render('error', [
152 | 'name' => $name,
153 | 'message' => $message,
154 | 'exception' => $exception,
155 | ]);
156 | }
157 | }
158 | }
--------------------------------------------------------------------------------
/controllers/UserController.php:
--------------------------------------------------------------------------------
1 | [
33 | 'class' => AccessControl::className(),
34 | 'only' => ['login', 'logout', 'send-sms'],
35 | 'rules' => [
36 | [
37 | 'allow' => true,
38 | 'actions' => ['login', 'send-sms'],
39 | 'roles' => ['?'],
40 | ],
41 | [
42 | 'allow' => true,
43 | 'actions' => ['logout'],
44 | 'roles' => ['@'],
45 | ],
46 | ],
47 | ],
48 | 'verbs' => [
49 | 'class' => VerbFilter::className(),
50 | 'actions' => [
51 | 'login' => ['get', 'post'],
52 | 'logout' => ['get'],
53 | 'send-sms' => ['post'],
54 | ],
55 | ],
56 | ];
57 | }
58 |
59 | /**
60 | * 个人资料的操作
61 | */
62 | public function actionProfile() {
63 | /* @var $model Admin */
64 | $model = Admin::findOne(Yii::$app->user->id);
65 |
66 | if (!$model) {
67 | throw new NotFoundHttpException('未找到该管理员。');
68 | }
69 |
70 | if ($model->load(Yii::$app->request->post()) && $model->validate()) {
71 | if ($model->save()) {
72 | Yii::$app->session->setFlash('success', '个人资料修改成功.');
73 | } else {
74 | Yii::$app->session->setFlash('success', '个人资料修改失败.');
75 | }
76 | }
77 |
78 | return $this->render('profile', [
79 | 'model' => $model
80 | ]);
81 | }
82 |
83 | /**
84 | * 用户登录的操作
85 | */
86 | public function actionLogin() {
87 | $this->layout = 'base';
88 |
89 | return $this->render('login');
90 | }
91 |
92 | /**
93 | * 用户注销登录的操作
94 | */
95 | public function actionLogout() {
96 | if (Yii::$app->user->logout()) {
97 | return $this->redirect(['user/login']);
98 | } else {
99 | return $this->goBack();
100 | }
101 | }
102 |
103 | /**
104 | * 通过ajax发送登录的验证码的操作
105 | * @return array
106 | */
107 | public function actionSendLoginSms() {
108 | $model = new LoginSendSmsForm();
109 | $model->phone = Yii::$app->request->post('phone');
110 |
111 | if ($model->sendSms()) {
112 | return Json::encode(['status' => 'ok']);
113 | } else {
114 | $message = $model->getFirstError('phone');
115 | return Json::encode(['status' => 'err', 'message' => $message]);
116 | }
117 | }
118 |
119 | /**
120 | * 通过ajax进行手机号登录的操作
121 | * @return array
122 | */
123 | public function actionPhoneLogin() {
124 | $model = new PhoneLoginForm();
125 |
126 | if ($model->load(Yii::$app->request->post(), '') && $model->login()) {
127 | return $this->redirect(['site/index']);
128 | } else {
129 | $message = $model->getFirstError('code');
130 | return Json::encode(['status' => 'err', 'message' => $message]);
131 | }
132 | }
133 |
134 | /**
135 | * 通过ajax进行账户登录的操作
136 | * @return array
137 | */
138 | public function actionAccountLogin() {
139 | $model = new AccountLoginForm();
140 |
141 | if ($model->load(Yii::$app->request->post(), '') && $model->login()) {
142 | return $this->redirect(['site/index']);
143 | } else {
144 | $message = $model->getFirstError('password');
145 | return Json::encode(['status' => 'err', 'message' => $message]);
146 | }
147 | }
148 |
149 | /**
150 | * 修改密码的操作
151 | * @return array
152 | */
153 | public function actionUpdatePassword() {
154 | $model = new UpdatePasswordForm();
155 |
156 | if ($model->load(Yii::$app->request->post()) && $model->updatePassword()) {
157 | Yii::$app->session->setFlash('success', '密码修改成功。');
158 | return $this->refresh();
159 | }
160 |
161 | return $this->render('update-password', [
162 | 'model' => $model
163 | ]);
164 | }
165 |
166 | /**
167 | * 通过ajax发送修改手机号的验证码的操作
168 | * @return array
169 | */
170 | public function actionSendUpdatePhoneSms() {
171 | $model = new SendChangePhoneSmsForm();
172 | $model->phone = Yii::$app->request->post('phone');
173 |
174 | if ($model->sendSms()) {
175 | return Json::encode(['status' => 'ok']);
176 | } else {
177 | $message = $model->getFirstError('phone');
178 | return Json::encode(['status' => 'err', 'message' => $message]);
179 | }
180 | }
181 |
182 | /**
183 | * 修改手机号的操作
184 | * @param string $step
185 | * @return array
186 | */
187 | public function actionUpdatePhone($step = '1') {
188 | $params = ['step' => $step];
189 |
190 | if ($step === '1') {
191 | $verifyPasswordForm = new VerifyPasswordForm();
192 |
193 | if ($verifyPasswordForm->load(Yii::$app->request->post()) && $verifyPasswordForm->validate()) {
194 | Yii::$app->session['passwordVerified'] = true;
195 |
196 | return $this->redirect(['user/update-phone', 'step' => '2']);
197 | }
198 |
199 | /** @var $admin Admin */
200 | $admin = Yii::$app->user->identity;
201 |
202 | $params['phone'] = $admin->phone;
203 | $params['verifyPasswordForm'] = $verifyPasswordForm;
204 | } elseif ($step === '2' && Yii::$app->session->has('passwordVerified') && Yii::$app->session['passwordVerified']) {
205 | $changeMobileForm = new ChangePhoneForm();
206 |
207 | if ($changeMobileForm->load(Yii::$app->request->post()) && $changeMobileForm->change()) {
208 | Yii::$app->session->setFlash('success', '手机更换成功!');
209 | return $this->redirect(['user/update-phone']);
210 | }
211 |
212 | $params['changeMobileForm'] = $changeMobileForm;
213 | } else {
214 | return $this->redirect(['user/update-phone']);
215 | }
216 |
217 | return $this->render('update-phone', $params);
218 | }
219 | }
--------------------------------------------------------------------------------
/mail/layouts/html.php:
--------------------------------------------------------------------------------
1 |
8 | beginPage() ?>
9 |
10 |
11 |
12 |
13 | = Html::encode($this->title) ?>
14 | head() ?>
15 |
16 |
17 | beginBody() ?>
18 | = $content ?>
19 | endBody() ?>
20 |
21 |
22 | endPage() ?>
23 |
--------------------------------------------------------------------------------
/models/AccountLoginForm.php:
--------------------------------------------------------------------------------
1 | '用户名',
36 | 'password' => '密码',
37 | 'remember' => '记住我1周',
38 | ];
39 | }
40 |
41 | /**
42 | * Validates the password.
43 | * This method serves as the inline validation for password.
44 | *
45 | * @param string $attribute the attribute currently being validated
46 | * @param array $params the additional name-value pairs given in the rule
47 | */
48 | public function validatePassword($attribute, $params) {
49 | if (!$this->hasErrors()) {
50 | $user = $this->getUser();
51 | if (!$user) {
52 | $this->addError($attribute, '不存在该用户名.');
53 | } else if (!$user->validatePassword($this->password)) {
54 | $this->addError($attribute, ' 密码输入错误.');
55 | }
56 | }
57 | }
58 |
59 | /**
60 | * Logs in a admin using the provided username and password.
61 | *
62 | * @return boolean whether the user is logged in successfully
63 | */
64 | public function login() {
65 | if ($this->validate()) {
66 | return Yii::$app->user->login($this->getUser(), $this->remember ? 3600 * 24 * 7 : 0);
67 | } else {
68 | return false;
69 | }
70 | }
71 |
72 | /**
73 | * Finds user by [[username]]
74 | *
75 | * @return Admin|null
76 | */
77 | public function getUser() {
78 | if ($this->_user === false) {
79 | $this->_user = Admin::findByUsername($this->username);
80 | }
81 |
82 | return $this->_user;
83 | }
84 | }
--------------------------------------------------------------------------------
/models/Activity.php:
--------------------------------------------------------------------------------
1 | 50],
40 | [['description'], 'string', 'max' => 100],
41 | [['icon_name', 'icon_color'], 'string', 'max' => 10],
42 | ];
43 | }
44 |
45 | /**
46 | * @inheritdoc
47 | */
48 | public function attributeLabels()
49 | {
50 | return [
51 | 'id' => '主键ID',
52 | 'name' => '活动的名称',
53 | 'description' => '活动的描述',
54 | 'icon_name' => '活动图标的文字',
55 | 'icon_color' => '活动图标的颜色',
56 | 'code' => '逻辑code',
57 | 'is_share' => '是否和其他活动共享',
58 | 'priority' => '活动的优先级',
59 | 'created_at' => '创建时间',
60 | 'updated_at' => 'Updated At',
61 | ];
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/models/Admin.php:
--------------------------------------------------------------------------------
1 | 'trim'],
68 | ['user_name', 'string', 'min' => 4, 'max' => 20],
69 | ['user_name', 'match', 'pattern' => '/^[A-Za-z_-][A-Za-z0-9_-]+$/'],
70 | ['user_name', 'unique', 'message' => '该用户名已被使用'],
71 |
72 | ['business_id', 'required'],
73 |
74 | ['real_name', 'filter', 'filter' => 'trim'],
75 | ['real_name', 'required'],
76 | ['real_name', 'string', 'min' => 2, 'max' => 20],
77 |
78 | ['identity_num', 'required'],
79 | ['identity_num', 'string', 'max' => 20],
80 |
81 | ['email', 'filter', 'filter' => 'trim'],
82 | ['email', 'required'],
83 | ['email', 'email'],
84 |
85 | [['password'], 'required', 'on' => 'insert'],
86 | [['password'], 'string', 'min' => 6, 'max' => 24],
87 | [['password'], 'match', 'pattern' => '/^\S+$/'],
88 |
89 | ['gender', 'default', 'value' => self::GENDER_MALE],
90 | ['gender', 'in', 'range' => [self::GENDER_MALE, self::GENDER_WOMAN, self::GENDER_OTHER]],
91 |
92 | ['phone', 'required'],
93 | ['phone', 'match', 'pattern' => '/^1[3|4|5|7|8][0-9]{9}$/'],
94 | ];
95 | }
96 |
97 | /**
98 | * @inheritdoc
99 | */
100 | public function attributeLabels() {
101 | return [
102 | 'id' => 'ID',
103 | 'business_id' => '管理的商铺ID',
104 | 'user_name' => '用户名',
105 | 'real_name' => '真实姓名',
106 | 'identity_num' => '身份证号',
107 | 'password' => '密码',
108 | 'gender' => '性别',
109 | 'email' => '邮箱',
110 | 'phone' => '手机',
111 | 'status' => '状态',
112 | 'created_at' => '创建时间',
113 | 'updated_at' => '更新时间',
114 | ];
115 | }
116 |
117 | /**
118 | * @inheritdoc
119 | */
120 | public static function findIdentity($id) {
121 | return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);
122 | }
123 |
124 | /**
125 | * @inheritdoc
126 | */
127 | public static function findIdentityByAccessToken($token, $type = null) {
128 | throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
129 | }
130 |
131 | /**
132 | * Finds user by username
133 | *
134 | * @param string $username
135 | * @return Admin|null
136 | */
137 | public static function findByUsername($username) {
138 | return static::findOne(['user_name' => $username, 'status' => self::STATUS_ACTIVE]);
139 | }
140 |
141 | /**
142 | * @inheritdoc
143 | */
144 | public function getId() {
145 | return $this->getPrimaryKey();
146 | }
147 |
148 | /**
149 | * @inheritdoc
150 | */
151 | public function getAuthKey() {
152 | return $this->auth_key;
153 | }
154 |
155 | /**
156 | * @inheritdoc
157 | */
158 | public function validateAuthKey($authKey) {
159 | return $this->getAuthKey() === $authKey;
160 | }
161 |
162 | /**
163 | * Validates password
164 | *
165 | * @param string $password password to validate
166 | * @return boolean if password provided is valid for current user
167 | */
168 | public function validatePassword($password) {
169 | return Yii::$app->security->validatePassword($password, $this->password_hash);
170 | }
171 |
172 | /**
173 | * Generates password hash from password and sets it to the model
174 | *
175 | * @param string $password
176 | */
177 | public function setPassword($password) {
178 | $this->password_hash = Yii::$app->security->generatePasswordHash($password);
179 | }
180 |
181 | /**
182 | * Generates "remember me" authentication key
183 | */
184 | public function generateAuthKey() {
185 | $this->auth_key = Yii::$app->security->generateRandomString();
186 | }
187 |
188 | public static function getGenderList() {
189 | if (self::$_genderList === null) {
190 | self::$_genderList = [
191 | self::GENDER_MALE => '男',
192 | self::GENDER_WOMAN => '女',
193 | self::GENDER_OTHER => '保密'
194 | ];
195 | }
196 |
197 | return self::$_genderList;
198 | }
199 |
200 | public function getGenderMsg() {
201 | $list = self::getGenderList();
202 |
203 | return isset($list[$this->gender]) ? $list[$this->gender] : null;
204 | }
205 |
206 | public static function getStatusList() {
207 | if (self::$_statusList === null) {
208 | self::$_statusList = [
209 | self::STATUS_ACTIVE => '正常',
210 | self::STATUS_BLOCKED => '禁用'
211 | ];
212 | }
213 |
214 | return self::$_statusList;
215 | }
216 |
217 | public function getStatusMsg() {
218 | $list = self::getStatusList();
219 |
220 | return isset($list[$this->status]) ? $list[$this->status] : null;
221 | }
222 | }
--------------------------------------------------------------------------------
/models/Business.php:
--------------------------------------------------------------------------------
1 | 50],
63 | [['phone', 'shipping_time'], 'string', 'max' => 20],
64 | [['address'], 'string', 'max' => 100],
65 | [['pic_url'], 'string', 'max' => 200],
66 | [['bulletin'], 'string', 'max' => 300],
67 | ];
68 | }
69 |
70 | /**
71 | * @inheritdoc
72 | */
73 | public function attributeLabels() {
74 | return [
75 | 'id' => 'ID',
76 | 'name' => '店名',
77 | 'phone' => '联系电话',
78 | 'address' => '地址',
79 | 'status' => '营业状态',
80 | 'opening_time' => '营业时间',
81 | 'pic_url' => 'Logo地址',
82 | 'image' => '店铺LOGO',
83 | 'shipping_fee' => '配送费',
84 | 'package_fee' => '打包费',
85 | 'min_price' => '起送价',
86 | 'shipping_time' => '平均配送时间',
87 | 'month_sales' => '月销量',
88 | 'bulletin' => '公告',
89 | 'category' => '分类',
90 | 'booking_times' => '可预订的时间段',
91 | 'updated_at' => '最近修改时间',
92 | 'created_at' => '创建时间',
93 | ];
94 | }
95 | }
--------------------------------------------------------------------------------
/models/BusinessActivity.php:
--------------------------------------------------------------------------------
1 | 100],
38 | ];
39 | }
40 |
41 | /**
42 | * @inheritdoc
43 | */
44 | public function attributeLabels() {
45 | return [
46 | 'id' => '主键ID',
47 | 'business_id' => '商户ID',
48 | 'activity_id' => '活动ID',
49 | 'attribute' => '解析description所用的json数据',
50 | 'created_at' => '创建时间',
51 | 'updated_at' => 'Updated At',
52 | ];
53 | }
54 |
55 | public static function activities($businessId) {
56 | $query = new Query();
57 | return $query->select([
58 | 'a.id',
59 | 'b.name',
60 | 'b.description',
61 | 'a.attribute',
62 | 'b.icon_name',
63 | 'b.icon_color',
64 | 'b.code',
65 | 'b.is_share',
66 | 'b.priority'
67 | ])
68 | ->from('business_activity a')
69 | ->innerJoin('activity b', 'a.activity_id=b.id')
70 | ->where(['a.business_id' => $businessId]);
71 | }
72 | }
--------------------------------------------------------------------------------
/models/BusinessScene.php:
--------------------------------------------------------------------------------
1 | 100],
57 | ];
58 | }
59 |
60 | /**
61 | * @inheritdoc
62 | */
63 | public function attributeLabels()
64 | {
65 | return [
66 | 'id' => 'ID',
67 | 'business_id' => 'Business ID',
68 | 'type' => '实景类别,1=门面、2=大堂、3=后厨',
69 | 'original_name' => 'Original Name',
70 | 'original_url' => 'Original Url',
71 | 'thumb_name' => 'Thumb Name',
72 | 'thumb_url' => 'Thumb Url',
73 | 'rank' => 'Rank',
74 | 'created_at' => 'Created At',
75 | 'updated_at' => 'Updated At',
76 | ];
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/models/Category.php:
--------------------------------------------------------------------------------
1 | 30],
46 | [['description'], 'string', 'max' => 50],
47 | [['icon_url'], 'string', 'max' => 200],
48 | ];
49 | }
50 |
51 | /**
52 | * @inheritdoc
53 | */
54 | public function attributeLabels() {
55 | return [
56 | 'id' => 'ID',
57 | 'business_id' => 'Business ID',
58 | 'name' => '分类名',
59 | 'description' => '描述',
60 | 'icon_url' => '图标url',
61 | 'created_at' => '创建时间',
62 | 'updated_at' => '最近更新时间',
63 | ];
64 | }
65 |
66 | /**
67 | * 获取指定商家的所有商品分类(id name)键值对
68 | * @return array
69 | */
70 | public static function getKeyValuePairs() {
71 | /* @var $admin Admin */
72 | $admin = Admin::findOne(Yii::$app->user->id);
73 |
74 | $sql = "SELECT `id`, `name` FROM ".self::tableName()." WHERE `business_id`=".$admin->business_id." ORDER BY `id` ASC";
75 |
76 | return Yii::$app->db->createCommand($sql)->queryAll(\PDO::FETCH_KEY_PAIR);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/models/CategorySearch.php:
--------------------------------------------------------------------------------
1 | user->id);
26 |
27 | $query = Category::find();
28 | $dataProvider = new ActiveDataProvider([
29 | 'query' => $query->where(['business_id' => $admin->business_id]),
30 | 'sort' => [
31 | 'defaultOrder' => ['id' => SORT_ASC]
32 | ],
33 | 'pagination' => [
34 | 'pageSize' => 20
35 | ]
36 | ]);
37 |
38 | if (!($this->load($params) && $this->validate())) {
39 | return $dataProvider;
40 | }
41 |
42 | $dateBegin = strtotime($this->date);
43 | $dateEnd = $dateBegin + 86400;
44 |
45 | $query->andFilterWhere(['like', 'name', $this->name])
46 | ->andFilterWhere(['like', 'description', $this->description])
47 | ->andFilterWhere(['>=', 'created_at', $this->date ? $dateBegin : null])
48 | ->andFilterWhere(['<', 'created_at', $this->date ? $dateEnd : null]);
49 |
50 | return $dataProvider;
51 | }
52 | }
--------------------------------------------------------------------------------
/models/ChangePhoneForm.php:
--------------------------------------------------------------------------------
1 | '/^1[3|5|7|8|][0-9]{9}$/'],
25 | [['phone'], 'unique', 'targetClass' => '\backend\models\Admin', 'message' => '该手机号已被注册!'],
26 | [['phone'], function ($attribute, $params) {
27 | $session = Yii::$app->session;
28 | if ($session->has('changePhoneSendPhone') && $session['changePhoneSendPhone'] !== $this->phone) {
29 | $this->addError($attribute, '该手机号与上次不匹配!');
30 | }
31 | }],
32 |
33 | [['verifyCode'], 'string', 'length' => 6],
34 | [['verifyCode'], function ($attribute, $params) {
35 | $session = Yii::$app->session;
36 | if (!$session->has('changePhoneSendPhone') || !$session->has('changePhoneVerifyCode')) {
37 | $this->addError($attribute, '请您发送验证码!');
38 | return;
39 | }
40 | if ($session['changePhoneVerifyCode'] !== $this->verifyCode) {
41 | $this->addError($attribute, '验证码不匹配!');
42 | }
43 | }],
44 | ];
45 | }
46 |
47 | /**
48 | * @inheritdoc
49 | */
50 | public function attributeLabels() {
51 | return [
52 | 'phone' => '新手机号',
53 | 'verifyCode' => '验证码',
54 | ];
55 | }
56 |
57 | /**
58 | * 执行修改手机号的错作
59 | * @param bool $runValidation
60 | * @return bool
61 | */
62 | public function change($runValidation = true) {
63 | if ($runValidation && !$this->validate()) {
64 | return false;
65 | }
66 |
67 | /** @var $admin Admin */
68 | $admin = Yii::$app->user->identity;
69 | $admin->phone = $this->phone;
70 |
71 | return $admin->save(false);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/models/LoginSendSmsForm.php:
--------------------------------------------------------------------------------
1 | '/^1[3|5|7|8|][0-9]{9}$/'],
21 | // 验证手机号是否存在
22 | ['phone', function ($attribute, $params) {
23 | $admin = Admin::findOne(['phone' => $this->phone]);
24 | if (!$admin) {
25 | $this->addError($attribute, '不存在该手机号!');
26 | }
27 | }],
28 | // 验证获取验证码的频率
29 | ['phone', function ($attribute, $params) {
30 | $session = Yii::$app->session;
31 | if ($session->has('loginNextSendTime') && $session['loginNextSendTime'] > time()) {
32 | $this->addError($attribute, '发送验证码过于频繁。');
33 | }
34 | }]
35 | ];
36 | }
37 |
38 | /**
39 | * @inheritdoc
40 | */
41 | public function attributeLabels() {
42 | return [
43 | 'phone' => '手机号',
44 | ];
45 | }
46 |
47 | /**
48 | * 发送短信验证码
49 | * @param bool $runValidation
50 | * @return bool
51 | */
52 | public function sendSms($runValidation = true) {
53 |
54 | if ($runValidation && !$this->validate()) {
55 | return false;
56 | }
57 |
58 | $verifyCode = (string) mt_rand(100000, 999999);
59 | $validMinutes = 30;
60 |
61 | // 调用云之讯组件发送模板短信
62 | /** @var $ucpass Ucpaas */
63 | $ucpass = Yii::$app->ucpass;
64 | $ucpass->templateSMS($this->phone, $verifyCode.','.$validMinutes);
65 |
66 | if ($ucpass->state == Ucpaas::STATUS_SUCCESS) {
67 | $session = Yii::$app->session;
68 | $session['loginNextSendTime'] = time() + 60;
69 | $session['loginSendPhone'] = $this->phone;
70 | $session['loginVerifyCode'] = $verifyCode;
71 |
72 | return true;
73 | } else {
74 | $this->addError('phone', $ucpass->message);
75 |
76 | return false;
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/models/Order.php:
--------------------------------------------------------------------------------
1 | 50],
72 | [['consignee', 'phone', 'booked_at'], 'string', 'max' => 20],
73 | [['address'], 'string', 'max' => 100],
74 | [['remark'], 'string', 'max' => 200],
75 | ];
76 | }
77 |
78 | /**
79 | * @inheritdoc
80 | */
81 | public function attributeLabels() {
82 | return [
83 | 'id' => '订单ID',
84 | 'cart_id' => '购物车ID',
85 | 'business_id' => '商铺ID',
86 | 'user_id' => '用户ID',
87 | 'order_num' => '订单编号',
88 | 'status' => '订单状态',
89 | 'origin_price' => '商品原价',
90 | 'discount_price' => '优惠价格',
91 | 'total_price' => '合计价格',
92 | 'consignee' => '联系人',
93 | 'phone' => '联系电话',
94 | 'address' => '收货地址',
95 | 'pay_method' => '支付方式',
96 | 'remark' => '备注',
97 | 'booked_at' => '预订时间',
98 | 'created_at' => '下单时间',
99 | 'updated_at' => 'Updated At',
100 | ];
101 | }
102 |
103 | public static function getPayMethodList() {
104 | if (self::$_payMethodtList === null) {
105 | self::$_payMethodtList = [
106 | self::PAYMENT_ONLINE => '在线支付',
107 | self::PAYMENT_OFFLINE => '货到付款'
108 | ];
109 | }
110 |
111 | return self::$_payMethodtList;
112 | }
113 |
114 | public static function getStatusList() {
115 | if (self::$_statusList === null) {
116 | self::$_statusList = [
117 | self::STATUS_WAIT_ACCEPT => '待接单',
118 | self::STATUS_WAIT_SEND => '待配送',
119 | self::STATUS_FINISHED => '已完成',
120 | ];
121 | }
122 |
123 | return self::$_statusList;
124 | }
125 | }
--------------------------------------------------------------------------------
/models/OrderSearch.php:
--------------------------------------------------------------------------------
1 | user->id);
28 |
29 | $query = Order::find();
30 | $dataProvider = new ActiveDataProvider([
31 | 'query' => $query->where([
32 | 'business_id' => $admin->business_id,
33 | 'status' => $params['status']
34 | ]),
35 | 'sort' => [
36 | 'defaultOrder' => ['created_at' => SORT_DESC]
37 | ],
38 | 'pagination' => [
39 | 'pageSize' => 20
40 | ]
41 | ]);
42 |
43 | if (!($this->load($params) && $this->validate())) {
44 | return $dataProvider;
45 | }
46 |
47 | $this->_addDigitalFilter($query, 'order_num');
48 | $this->_addDigitalFilter($query, 'phone');
49 | $this->_addDigitalFilter($query, 'origin_price');
50 | $this->_addDigitalFilter($query, 'discount_price');
51 | $this->_addDigitalFilter($query, 'total_price');
52 |
53 | $dateBegin = strtotime($this->date);
54 | $dateEnd = $dateBegin + 86400;
55 |
56 | // adjust the query by adding the filters
57 | $query->andFilterWhere(['id' => $this->id])
58 | ->andFilterWhere(['like', 'consignee', $this->consignee])
59 | ->andFilterWhere(['like', 'address', $this->address])
60 | ->andFilterWhere(['like', 'remark', $this->remark])
61 | ->andFilterWhere(['pay_method' => $this->pay_method])
62 | ->andFilterWhere(['>=', 'created_at', $this->date ? $dateBegin : null])
63 | ->andFilterWhere(['<', 'created_at', $this->date ? $dateEnd : null]);
64 |
65 | return $dataProvider;
66 | }
67 |
68 | /**
69 | * 附加数字过滤器
70 | * @param $query ActiveQuery
71 | * @param $attribute string
72 | */
73 | protected function _addDigitalFilter($query, $attribute) {
74 | $pattern = '/^(>|>=|<|<=|=)(\d*\.?\d+)$/';
75 | if (preg_match($pattern, $this->{$attribute}, $matches) === 1) {
76 | $query->andFilterWhere([$matches[1], $attribute, $matches[2]]);
77 | } else {
78 | $query->andFilterWhere(['like', $attribute, $this->{$attribute}]);
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/models/PhoneLoginForm.php:
--------------------------------------------------------------------------------
1 | '手机号',
36 | 'code' => '验证码',
37 | 'remember' => '记住我1周',
38 | ];
39 | }
40 |
41 | /**
42 | * Validates the code.
43 | * This method serves as the inline validation for password.
44 | *
45 | * @param string $attribute the attribute currently being validated
46 | * @param array $params the additional name-value pairs given in the rule
47 | */
48 | public function validateCode($attribute, $params) {
49 | if (!$this->hasErrors()) {
50 | $session = Yii::$app->session;
51 | $user = $this->getUser();
52 | if (!$user) {
53 | $this->addError($attribute, '不存在该手机号!');
54 | } else if ($session['loginVerifyCode'] != $this->code) {
55 | $this->addError($attribute, '验证码不正确!');
56 | }
57 | }
58 | }
59 |
60 | /**
61 | * Logs in a admin using the provided username and password.
62 | *
63 | * @return boolean whether the user is logged in successfully
64 | */
65 | public function login() {
66 | if ($this->validate()) {
67 | return Yii::$app->user->login($this->getUser(), $this->remember ? 3600 * 24 * 7 : 0);
68 | } else {
69 | return false;
70 | }
71 | }
72 |
73 | /**
74 | * Finds user by [[username]]
75 | *
76 | * @return Admin|null
77 | */
78 | public function getUser() {
79 | if ($this->_user === false) {
80 | $this->_user = Admin::findOne(['phone' => $this->phone]);
81 | }
82 |
83 | return $this->_user;
84 | }
85 | }
--------------------------------------------------------------------------------
/models/Product.php:
--------------------------------------------------------------------------------
1 | 'insert'],
58 | [
59 | 'image',
60 | 'image',
61 | 'extensions' => 'jpg, png, jpeg, gif',
62 | 'mimeTypes' => 'image/jpeg, image/png, image/gif',
63 | 'checkExtensionByMimeType' => false,
64 | 'minSize' => 100,
65 | 'maxSize' => 204800,
66 | 'tooBig' => '{attribute}最大不能超过200KB',
67 | 'tooSmall' => '{attribute}最小不能小于0.1KB',
68 | 'notImage' => '{file} 不是图片文件'
69 | ],
70 |
71 | [['business_id', 'category_id', 'left_num'], 'integer'],
72 |
73 | [['category_id'], 'exist', 'targetClass' => Category::className(), 'targetAttribute' => 'id'],
74 |
75 | ['price', 'number', 'min' => 0],
76 |
77 | [['name'], 'string', 'max' => 100],
78 | [['description'], 'string', 'max' => 200],
79 | [['image_path'], 'string', 'max' => 200],
80 | ];
81 | }
82 |
83 | /**
84 | * @inheritdoc
85 | */
86 | public function attributeLabels() {
87 | return [
88 | 'id' => '编号',
89 | 'business_id' => '商家',
90 | 'category_id' => '分类',
91 | 'name' => '商品名',
92 | 'description' => '商品描述',
93 | 'image' => '商品图片',
94 | 'price' => '价格',
95 | 'image_path' => '商品图片',
96 | 'month_sales' => '月销量',
97 | 'rate' => '评价',
98 | 'left_num' => '库存',
99 | 'created_at' => '添加时间',
100 | ];
101 | }
102 |
103 | /**
104 | * @return ActiveQuery
105 | */
106 | public function getCategory() {
107 | return $this->hasOne(Category::className(), ['id' => 'category_id']);
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/models/ProductSearch.php:
--------------------------------------------------------------------------------
1 | user->id);
27 |
28 | $query = Product::find();
29 | $dataProvider = new ActiveDataProvider([
30 | 'query' => $query->where(['business_id' => $admin->business_id]),
31 | 'sort' => [
32 | 'defaultOrder' => ['id' => SORT_ASC]
33 | ],
34 | 'pagination' => [
35 | 'pageSize' => 20
36 | ]
37 | ]);
38 |
39 | if (!($this->load($params) && $this->validate())) {
40 | return $dataProvider;
41 | }
42 |
43 | $this->_addDigitalFilter($query, 'price');
44 | $this->_addDigitalFilter($query, 'month_sales');
45 | $this->_addDigitalFilter($query, 'left_num');
46 |
47 | $dateBegin = strtotime($this->date);
48 | $dateEnd = $dateBegin + 86400;
49 |
50 | // adjust the query by adding the filters
51 | $query->andFilterWhere(['like', 'name', $this->name])
52 | ->andFilterWhere(['like', 'description', $this->description])
53 | ->andFilterWhere(['category_id' => $this->category_id])
54 | ->andFilterWhere(['>=', 'created_at', $this->date ? $dateBegin : null])
55 | ->andFilterWhere(['<', 'created_at', $this->date ? $dateEnd : null]);
56 |
57 | return $dataProvider;
58 | }
59 |
60 | /**
61 | * 附加数字过滤器
62 | * @param $query ActiveQuery
63 | * @param $attribute string
64 | */
65 | protected function _addDigitalFilter($query, $attribute) {
66 | $pattern = '/^(>|>=|<|<=|=)(\d*\.?\d+)$/';
67 | if (preg_match($pattern, $this->{$attribute}, $matches) === 1) {
68 | $query->andFilterWhere([$matches[1], $attribute, $matches[2]]);
69 | } else {
70 | $query->andFilterWhere(['like', $attribute, $this->{$attribute}]);
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/models/SendChangePhoneSmsForm.php:
--------------------------------------------------------------------------------
1 | '/^1[3|5|7|8|][0-9]{9}$/'],
21 | // 验证手机号是否存在
22 | ['phone', function ($attribute, $params) {
23 | $admin = Admin::findOne(['phone' => $this->phone]);
24 | if ($admin) {
25 | $this->addError($attribute, '该手机号已被注册!');
26 | }
27 | }],
28 | // 验证获取验证码的频率
29 | ['phone', function ($attribute, $params) {
30 | $session = Yii::$app->session;
31 | if ($session->has('changePhoneNextSendTime') && $session['changePhoneNextSendTime'] > time()) {
32 | $this->addError($attribute, '发送验证码过于频繁。');
33 | }
34 | }]
35 | ];
36 | }
37 |
38 | /**
39 | * @inheritdoc
40 | */
41 | public function attributeLabels() {
42 | return [
43 | 'phone' => '手机号',
44 | ];
45 | }
46 |
47 | /**
48 | * 发送短信验证码
49 | * @param bool $runValidation
50 | * @return bool
51 | */
52 | public function sendSms($runValidation = true) {
53 |
54 | if ($runValidation && !$this->validate()) {
55 | return false;
56 | }
57 |
58 | $verifyCode = (string) mt_rand(100000, 999999);
59 | $validMinutes = 30;
60 |
61 | // 调用云之讯组件发送模板短信
62 | /** @var $ucpass Ucpaas */
63 | $ucpass = Yii::$app->ucpass;
64 | $ucpass->templateSMS($this->phone, $verifyCode.','.$validMinutes);
65 |
66 | if ($ucpass->state == Ucpaas::STATUS_SUCCESS) {
67 | $session = Yii::$app->session;
68 | $session['changePhoneNextSendTime'] = time() + 60;
69 | $session['changePhoneSendPhone'] = $this->phone;
70 | $session['changePhoneVerifyCode'] = $verifyCode;
71 |
72 | return true;
73 | } else {
74 | $this->addError('phone', $ucpass->message);
75 |
76 | return false;
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/models/UpdatePasswordForm.php:
--------------------------------------------------------------------------------
1 | '原始密码不能为空'],
23 | ['new_password', 'required', 'message' => '新的密码不能为空'],
24 | ['repeat_password', 'required', 'message' => '确认密码不能为空'],
25 | ['repeat_password', 'compare', 'compareAttribute' => 'new_password', 'message' => '两次密码输入不一致'],
26 | ['old_password', 'validateOldPassword'],
27 | ];
28 | }
29 |
30 | /**
31 | * @inheritdoc
32 | */
33 | public function attributeLabels() {
34 | return [
35 | 'old_password' => '原始密码',
36 | 'new_password' => '新的密码',
37 | 'repeat_password' => '确认密码',
38 | ];
39 | }
40 |
41 | /**
42 | * Validates the old password.
43 | * This method serves as the inline validation for password.
44 | *
45 | * @param string $attribute the attribute currently being validated
46 | * @param array $params the additional name-value pairs given in the rule
47 | */
48 | public function validateOldPassword($attribute, $params) {
49 | if (!$this->hasErrors()) {
50 | /** @var $admin Admin */
51 | $admin = Admin::findOne(Yii::$app->user->id);
52 | if (!$admin->validatePassword($this->old_password)) {
53 | $this->addError($attribute, '原始密码输入错误');
54 | }
55 | }
56 | }
57 |
58 | /**
59 | * 修改新密码
60 | * @return bool
61 | */
62 | public function updatePassword() {
63 | if ($this->validate()) {
64 | /* @var $admin Admin */
65 | $admin = Admin::findOne(Yii::$app->user->id);
66 | $admin->setPassword($this->new_password);
67 | return $admin->save();
68 | } else {
69 | return false;
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/models/User.php:
--------------------------------------------------------------------------------
1 | 20],
44 | [['access_token', 'password_hash', 'password_reset_token'], 'string', 'max' => 255],
45 | [['email'], 'string', 'max' => 50],
46 | [['avatar_url'], 'string', 'max' => 200],
47 | ];
48 | }
49 |
50 | /**
51 | * @inheritdoc
52 | */
53 | public function attributeLabels()
54 | {
55 | return [
56 | 'id' => '主键ID',
57 | 'username' => '用户名',
58 | 'access_token' => '身份标识',
59 | 'password_hash' => '密码hash 值',
60 | 'password_reset_token' => '重置密码的标识',
61 | 'mobile' => '手机号',
62 | 'email' => '邮箱',
63 | 'avatar_url' => '头像URL',
64 | 'last_address_id' => '最近一次使用的地址ID',
65 | 'last_ip' => '最近一次登录的IP',
66 | 'last_device_type' => '最近一次登录的设备类型',
67 | 'last_device_id' => '最近一次登录的设备ID',
68 | 'created_at' => '创建时间',
69 | 'updated_at' => '更新时间',
70 | ];
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/models/VerifyPasswordForm.php:
--------------------------------------------------------------------------------
1 | 6, 'max' => 24],
22 | ['password', 'match', 'pattern' => '/^\S+$/'],
23 | ['password', 'validatePassword']
24 | ];
25 | }
26 |
27 | /**
28 | * @inheritdoc
29 | */
30 | public function attributeLabels() {
31 | return [
32 | 'password' => '登录密码'
33 | ];
34 | }
35 |
36 | /**
37 | * Validates the password.
38 | * This method serves as the inline validation for password.
39 | *
40 | * @param string $attribute the attribute currently being validated
41 | * @param array $params the additional name-value pairs given in the rule
42 | */
43 | public function validatePassword($attribute, $params) {
44 | if (!$this->hasErrors()) {
45 | /** @var $admin Admin */
46 | $admin = Yii::$app->user->identity;
47 | if (!$admin->validatePassword($this->password)) {
48 | $this->addError($attribute, '密码验证错误。');
49 | }
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/requirements.php:
--------------------------------------------------------------------------------
1 | Error';
18 | echo 'The path to yii framework seems to be incorrect.
';
19 | echo 'You need to install Yii framework via composer or adjust the framework path in file ' . basename(__FILE__) . '.
';
20 | echo 'Please refer to the README on how to install Yii.
';
21 | }
22 |
23 | require_once($frameworkPath . '/requirements/YiiRequirementChecker.php');
24 | $requirementsChecker = new YiiRequirementChecker();
25 |
26 | /**
27 | * Adjust requirements according to your application specifics.
28 | */
29 | $requirements = array(
30 | // Database :
31 | array(
32 | 'name' => 'PDO extension',
33 | 'mandatory' => true,
34 | 'condition' => extension_loaded('pdo'),
35 | 'by' => 'All DB-related classes',
36 | ),
37 | array(
38 | 'name' => 'PDO SQLite extension',
39 | 'mandatory' => false,
40 | 'condition' => extension_loaded('pdo_sqlite'),
41 | 'by' => 'All DB-related classes',
42 | 'memo' => 'Required for SQLite database.',
43 | ),
44 | array(
45 | 'name' => 'PDO MySQL extension',
46 | 'mandatory' => false,
47 | 'condition' => extension_loaded('pdo_mysql'),
48 | 'by' => 'All DB-related classes',
49 | 'memo' => 'Required for MySQL database.',
50 | ),
51 | array(
52 | 'name' => 'PDO PostgreSQL extension',
53 | 'mandatory' => false,
54 | 'condition' => extension_loaded('pdo_pgsql'),
55 | 'by' => 'All DB-related classes',
56 | 'memo' => 'Required for PostgreSQL database.',
57 | ),
58 | // Cache :
59 | array(
60 | 'name' => 'Memcache extension',
61 | 'mandatory' => false,
62 | 'condition' => extension_loaded('memcache') || extension_loaded('memcached'),
63 | 'by' => 'MemCache',
64 | 'memo' => extension_loaded('memcached') ? 'To use memcached set MemCache::useMemcached to true
.' : ''
65 | ),
66 | array(
67 | 'name' => 'APC extension',
68 | 'mandatory' => false,
69 | 'condition' => extension_loaded('apc'),
70 | 'by' => 'ApcCache',
71 | ),
72 | // PHP ini :
73 | 'phpSafeMode' => array(
74 | 'name' => 'PHP safe mode',
75 | 'mandatory' => false,
76 | 'condition' => $requirementsChecker->checkPhpIniOff("safe_mode"),
77 | 'by' => 'File uploading and console command execution',
78 | 'memo' => '"safe_mode" should be disabled at php.ini',
79 | ),
80 | 'phpExposePhp' => array(
81 | 'name' => 'Expose PHP',
82 | 'mandatory' => false,
83 | 'condition' => $requirementsChecker->checkPhpIniOff("expose_php"),
84 | 'by' => 'Security reasons',
85 | 'memo' => '"expose_php" should be disabled at php.ini',
86 | ),
87 | 'phpAllowUrlInclude' => array(
88 | 'name' => 'PHP allow url include',
89 | 'mandatory' => false,
90 | 'condition' => $requirementsChecker->checkPhpIniOff("allow_url_include"),
91 | 'by' => 'Security reasons',
92 | 'memo' => '"allow_url_include" should be disabled at php.ini',
93 | ),
94 | 'phpSmtp' => array(
95 | 'name' => 'PHP mail SMTP',
96 | 'mandatory' => false,
97 | 'condition' => strlen(ini_get('SMTP'))>0,
98 | 'by' => 'Email sending',
99 | 'memo' => 'PHP mail SMTP server required',
100 | ),
101 | );
102 | $requirementsChecker->checkYii()->check($requirements)->render();
103 |
--------------------------------------------------------------------------------
/runtime/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !debug
3 | !logs
4 | !.gitignore
--------------------------------------------------------------------------------
/tests/README.md:
--------------------------------------------------------------------------------
1 | This directory contains various tests for the basic application.
2 |
3 | Tests in `codeception` directory are developed with [Codeception PHP Testing Framework](http://codeception.com/).
4 |
5 | After creating the basic application, follow these steps to prepare for the tests:
6 |
7 | 1. Install Codeception if it's not yet installed:
8 |
9 | ```
10 | composer global require "codeception/codeception=2.0.*"
11 | composer global require "codeception/specify=*"
12 | composer global require "codeception/verify=*"
13 | ```
14 |
15 | If you've never used Composer for global packages run `composer global status`. It should output:
16 |
17 | ```
18 | Changed current directory to
19 | ```
20 |
21 | Then add `/vendor/bin` to you `PATH` environment variable. Now we're able to use `codecept` from command
22 | line globally.
23 |
24 | 2. Install faker extension by running the following from template root directory where `composer.json` is:
25 |
26 | ```
27 | composer require --dev yiisoft/yii2-faker:*
28 | ```
29 |
30 | 3. Create `yii2_basic_tests` database and update it by applying migrations:
31 |
32 | ```
33 | codeception/bin/yii migrate
34 | ```
35 |
36 | 4. Build the test suites:
37 |
38 | ```
39 | codecept build
40 | ```
41 |
42 | 5. In order to be able to run acceptance tests you need to start a webserver. The simplest way is to use PHP built in
43 | webserver. In the `web` directory execute the following:
44 |
45 | ```
46 | php -S localhost:8080
47 | ```
48 |
49 | 6. Now you can run the tests with the following commands:
50 |
51 | ```
52 | # run all available tests
53 | codecept run
54 | # run acceptance tests
55 | codecept run acceptance
56 | # run functional tests
57 | codecept run functional
58 | # run unit tests
59 | codecept run unit
60 | ```
61 |
62 | Please refer to [Codeception tutorial](http://codeception.com/docs/01-Introduction) for
63 | more details about writing and running acceptance, functional and unit tests.
64 |
--------------------------------------------------------------------------------
/tests/codeception.yml:
--------------------------------------------------------------------------------
1 | actor: Tester
2 | paths:
3 | tests: codeception
4 | log: codeception/_output
5 | data: codeception/_data
6 | helpers: codeception/_support
7 | settings:
8 | bootstrap: _bootstrap.php
9 | suite_class: \PHPUnit_Framework_TestSuite
10 | memory_limit: 1024M
11 | log: true
12 | colors: true
13 | config:
14 | # the entry script URL (without host info) for functional and acceptance tests
15 | # PLEASE ADJUST IT TO THE ACTUAL ENTRY SCRIPT URL
16 | test_entry_url: /index-test.php
--------------------------------------------------------------------------------
/tests/codeception/.gitignore:
--------------------------------------------------------------------------------
1 | # these files are auto generated by codeception build
2 | /unit/UnitTester.php
3 | /functional/FunctionalTester.php
4 | /acceptance/AcceptanceTester.php
5 |
--------------------------------------------------------------------------------
/tests/codeception/_bootstrap.php:
--------------------------------------------------------------------------------
1 | $value) {
21 | $inputType = $field === 'body' ? 'textarea' : 'input';
22 | $this->actor->fillField($inputType . '[name="ContactForm[' . $field . ']"]', $value);
23 | }
24 | $this->actor->click('contact-button');
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tests/codeception/_pages/LoginPage.php:
--------------------------------------------------------------------------------
1 | actor->fillField('input[name="LoginForm[username]"]', $username);
22 | $this->actor->fillField('input[name="LoginForm[password]"]', $password);
23 | $this->actor->click('login-button');
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/codeception/acceptance.suite.yml:
--------------------------------------------------------------------------------
1 | # Codeception Test Suite Configuration
2 |
3 | # suite for acceptance tests.
4 | # perform tests in browser using the Selenium-like tools.
5 | # powered by Mink (http://mink.behat.org).
6 | # (tip: that's what your customer will see).
7 | # (tip: test your ajax and javascript by one of Mink drivers).
8 |
9 | # RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
10 |
11 | class_name: AcceptanceTester
12 | modules:
13 | enabled:
14 | - PhpBrowser
15 | # you can use WebDriver instead of PhpBrowser to test javascript and ajax.
16 | # This will require you to install selenium. See http://codeception.com/docs/04-AcceptanceTests#Selenium
17 | # "restart" option is used by the WebDriver to start each time per test-file new session and cookies,
18 | # it is useful if you want to login in your app in each test.
19 | # - WebDriver
20 | config:
21 | PhpBrowser:
22 | url: 'http://localhost:8080'
23 | # WebDriver:
24 | # url: 'http://localhost'
25 | # browser: firefox
26 | # restart: true
27 |
--------------------------------------------------------------------------------
/tests/codeception/acceptance/AboutCept.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that about works');
7 | AboutPage::openBy($I);
8 | $I->see('About', 'h1');
9 |
--------------------------------------------------------------------------------
/tests/codeception/acceptance/ContactCept.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that contact works');
7 |
8 | $contactPage = ContactPage::openBy($I);
9 |
10 | $I->see('Contact', 'h1');
11 |
12 | $I->amGoingTo('submit contact form with no data');
13 | $contactPage->submit([]);
14 | $I->expectTo('see validations errors');
15 | $I->see('Contact', 'h1');
16 | $I->see('Name cannot be blank');
17 | $I->see('Email cannot be blank');
18 | $I->see('Subject cannot be blank');
19 | $I->see('Body cannot be blank');
20 | $I->see('The verification code is incorrect');
21 |
22 | $I->amGoingTo('submit contact form with not correct email');
23 | $contactPage->submit([
24 | 'name' => 'tester',
25 | 'email' => 'tester.email',
26 | 'subject' => 'test subject',
27 | 'body' => 'test content',
28 | 'verifyCode' => 'testme',
29 | ]);
30 | $I->expectTo('see that email adress is wrong');
31 | $I->dontSee('Name cannot be blank', '.help-inline');
32 | $I->see('Email is not a valid email address.');
33 | $I->dontSee('Subject cannot be blank', '.help-inline');
34 | $I->dontSee('Body cannot be blank', '.help-inline');
35 | $I->dontSee('The verification code is incorrect', '.help-inline');
36 |
37 | $I->amGoingTo('submit contact form with correct data');
38 | $contactPage->submit([
39 | 'name' => 'tester',
40 | 'email' => 'tester@example.com',
41 | 'subject' => 'test subject',
42 | 'body' => 'test content',
43 | 'verifyCode' => 'testme',
44 | ]);
45 | if (method_exists($I, 'wait')) {
46 | $I->wait(3); // only for selenium
47 | }
48 | $I->dontSeeElement('#contact-form');
49 | $I->see('Thank you for contacting us. We will respond to you as soon as possible.');
50 |
--------------------------------------------------------------------------------
/tests/codeception/acceptance/HomeCept.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that home page works');
5 | $I->amOnPage(Yii::$app->homeUrl);
6 | $I->see('My Company');
7 | $I->seeLink('About');
8 | $I->click('About');
9 | $I->see('This is the About page.');
10 |
--------------------------------------------------------------------------------
/tests/codeception/acceptance/LoginCept.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that login works');
7 |
8 | $loginPage = LoginPage::openBy($I);
9 |
10 | $I->see('Login', 'h1');
11 |
12 | $I->amGoingTo('try to login with empty credentials');
13 | $loginPage->login('', '');
14 | $I->expectTo('see validations errors');
15 | $I->see('Username cannot be blank.');
16 | $I->see('Password cannot be blank.');
17 |
18 | $I->amGoingTo('try to login with wrong credentials');
19 | $loginPage->login('admin', 'wrong');
20 | if (method_exists($I, 'wait')) {
21 | $I->wait(3); // only for selenium
22 | }
23 | $I->expectTo('see validations errors');
24 | $I->see('Incorrect username or password.');
25 |
26 | $I->amGoingTo('try to login with correct credentials');
27 | $loginPage->login('admin', 'admin');
28 | if (method_exists($I, 'wait')) {
29 | $I->wait(3); // only for selenium
30 | }
31 | $I->expectTo('see user info');
32 | $I->see('Logout (admin)');
33 |
--------------------------------------------------------------------------------
/tests/codeception/acceptance/_bootstrap.php:
--------------------------------------------------------------------------------
1 | [
18 | 'fixture' => [
19 | 'class' => 'yii\faker\FixtureController',
20 | 'fixtureDataPath' => '@tests/codeception/fixtures',
21 | 'templatePath' => '@tests/codeception/templates'
22 | ],
23 | ],
24 | ]
25 | );
26 |
27 | $application = new yii\console\Application($config);
28 | $exitCode = $application->run();
29 | exit($exitCode);
30 |
--------------------------------------------------------------------------------
/tests/codeception/bin/yii.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | rem -------------------------------------------------------------
4 | rem Yii command line bootstrap script for Windows.
5 | rem
6 | rem @author Qiang Xue
7 | rem @link http://www.yiiframework.com/
8 | rem @copyright Copyright (c) 2008 Yii Software LLC
9 | rem @license http://www.yiiframework.com/license/
10 | rem -------------------------------------------------------------
11 |
12 | @setlocal
13 |
14 | set YII_PATH=%~dp0
15 |
16 | if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
17 |
18 | "%PHP_COMMAND%" "%YII_PATH%yii_acceptance" %*
19 |
20 | @endlocal
21 |
--------------------------------------------------------------------------------
/tests/codeception/config/acceptance.php:
--------------------------------------------------------------------------------
1 | [
7 | 'db' => [
8 | 'dsn' => 'mysql:host=localhost;dbname=yii2_basic_tests',
9 | ],
10 | 'mailer' => [
11 | 'useFileTransport' => true,
12 | ],
13 | 'urlManager' => [
14 | 'showScriptName' => true,
15 | ],
16 | ],
17 | ];
18 |
--------------------------------------------------------------------------------
/tests/codeception/config/functional.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that about works');
7 | AboutPage::openBy($I);
8 | $I->see('About', 'h1');
9 |
--------------------------------------------------------------------------------
/tests/codeception/functional/ContactCept.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that contact works');
7 |
8 | $contactPage = ContactPage::openBy($I);
9 |
10 | $I->see('Contact', 'h1');
11 |
12 | $I->amGoingTo('submit contact form with no data');
13 | $contactPage->submit([]);
14 | $I->expectTo('see validations errors');
15 | $I->see('Contact', 'h1');
16 | $I->see('Name cannot be blank');
17 | $I->see('Email cannot be blank');
18 | $I->see('Subject cannot be blank');
19 | $I->see('Body cannot be blank');
20 | $I->see('The verification code is incorrect');
21 |
22 | $I->amGoingTo('submit contact form with not correct email');
23 | $contactPage->submit([
24 | 'name' => 'tester',
25 | 'email' => 'tester.email',
26 | 'subject' => 'test subject',
27 | 'body' => 'test content',
28 | 'verifyCode' => 'testme',
29 | ]);
30 | $I->expectTo('see that email adress is wrong');
31 | $I->dontSee('Name cannot be blank', '.help-inline');
32 | $I->see('Email is not a valid email address.');
33 | $I->dontSee('Subject cannot be blank', '.help-inline');
34 | $I->dontSee('Body cannot be blank', '.help-inline');
35 | $I->dontSee('The verification code is incorrect', '.help-inline');
36 |
37 | $I->amGoingTo('submit contact form with correct data');
38 | $contactPage->submit([
39 | 'name' => 'tester',
40 | 'email' => 'tester@example.com',
41 | 'subject' => 'test subject',
42 | 'body' => 'test content',
43 | 'verifyCode' => 'testme',
44 | ]);
45 | $I->dontSeeElement('#contact-form');
46 | $I->see('Thank you for contacting us. We will respond to you as soon as possible.');
47 |
--------------------------------------------------------------------------------
/tests/codeception/functional/HomeCept.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that home page works');
5 | $I->amOnPage(Yii::$app->homeUrl);
6 | $I->see('My Company');
7 | $I->seeLink('About');
8 | $I->click('About');
9 | $I->see('This is the About page.');
10 |
--------------------------------------------------------------------------------
/tests/codeception/functional/LoginCept.php:
--------------------------------------------------------------------------------
1 | wantTo('ensure that login works');
7 |
8 | $loginPage = LoginPage::openBy($I);
9 |
10 | $I->see('Login', 'h1');
11 |
12 | $I->amGoingTo('try to login with empty credentials');
13 | $loginPage->login('', '');
14 | $I->expectTo('see validations errors');
15 | $I->see('Username cannot be blank.');
16 | $I->see('Password cannot be blank.');
17 |
18 | $I->amGoingTo('try to login with wrong credentials');
19 | $loginPage->login('admin', 'wrong');
20 | $I->expectTo('see validations errors');
21 | $I->see('Incorrect username or password.');
22 |
23 | $I->amGoingTo('try to login with correct credentials');
24 | $loginPage->login('admin', 'admin');
25 | $I->expectTo('see user info');
26 | $I->see('Logout (admin)');
27 |
--------------------------------------------------------------------------------
/tests/codeception/functional/_bootstrap.php:
--------------------------------------------------------------------------------
1 | mailer->fileTransportCallback = function ($mailer, $message) {
17 | return 'testing_message.eml';
18 | };
19 | }
20 |
21 | protected function tearDown()
22 | {
23 | unlink($this->getMessageFile());
24 | parent::tearDown();
25 | }
26 |
27 | public function testContact()
28 | {
29 | $model = $this->getMock('app\models\ContactForm', ['validate']);
30 | $model->expects($this->once())->method('validate')->will($this->returnValue(true));
31 |
32 | $model->attributes = [
33 | 'name' => 'Tester',
34 | 'email' => 'tester@example.com',
35 | 'subject' => 'very important letter subject',
36 | 'body' => 'body of current message',
37 | ];
38 |
39 | $model->contact('admin@example.com');
40 |
41 | $this->specify('email should be send', function () {
42 | expect('email file should exist', file_exists($this->getMessageFile()))->true();
43 | });
44 |
45 | $this->specify('message should contain correct data', function () use ($model) {
46 | $emailMessage = file_get_contents($this->getMessageFile());
47 |
48 | expect('email should contain user name', $emailMessage)->contains($model->name);
49 | expect('email should contain sender email', $emailMessage)->contains($model->email);
50 | expect('email should contain subject', $emailMessage)->contains($model->subject);
51 | expect('email should contain body', $emailMessage)->contains($model->body);
52 | });
53 | }
54 |
55 | private function getMessageFile()
56 | {
57 | return Yii::getAlias(Yii::$app->mailer->fileTransportPath) . '/testing_message.eml';
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/tests/codeception/unit/models/LoginFormTest.php:
--------------------------------------------------------------------------------
1 | user->logout();
17 | parent::tearDown();
18 | }
19 |
20 | public function testLoginNoUser()
21 | {
22 | $model = new LoginForm([
23 | 'username' => 'not_existing_username',
24 | 'password' => 'not_existing_password',
25 | ]);
26 |
27 | $this->specify('user should not be able to login, when there is no identity', function () use ($model) {
28 | expect('model should not login user', $model->login())->false();
29 | expect('user should not be logged in', Yii::$app->user->isGuest)->true();
30 | });
31 | }
32 |
33 | public function testLoginWrongPassword()
34 | {
35 | $model = new LoginForm([
36 | 'username' => 'demo',
37 | 'password' => 'wrong_password',
38 | ]);
39 |
40 | $this->specify('user should not be able to login with wrong password', function () use ($model) {
41 | expect('model should not login user', $model->login())->false();
42 | expect('error message should be set', $model->errors)->hasKey('password');
43 | expect('user should not be logged in', Yii::$app->user->isGuest)->true();
44 | });
45 | }
46 |
47 | public function testLoginCorrect()
48 | {
49 | $model = new LoginForm([
50 | 'username' => 'demo',
51 | 'password' => 'demo',
52 | ]);
53 |
54 | $this->specify('user should be able to login with correct credentials', function () use ($model) {
55 | expect('model should login user', $model->login())->true();
56 | expect('error message should not be set', $model->errors)->hasntKey('password');
57 | expect('user should be logged in', Yii::$app->user->isGuest)->false();
58 | });
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/tests/codeception/unit/models/UserTest.php:
--------------------------------------------------------------------------------
1 | loadFixtures(['user']);
14 | }
15 |
16 | // TODO add test methods here
17 | }
18 |
--------------------------------------------------------------------------------
/tests/codeception/unit/templates/fixtures/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cheikh-wang/LazyWaimai-Web/c054ee4c11e11ad4e92abc376889335ad93fb4e3/tests/codeception/unit/templates/fixtures/.gitkeep
--------------------------------------------------------------------------------
/upload/images/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/views/activity/index.php:
--------------------------------------------------------------------------------
1 | title = '活动列表';
15 |
16 | ?>
17 |
18 |
19 | = Html::a(' 添加活动', ['product/add'], ['class' => 'btn btn-primary']) ?>
20 |
21 |
22 |
23 |
24 | = GridView::widget([
25 | 'dataProvider' => $dataProvider,
26 | 'tableOptions' => ['class' => 'table table-striped table-bordered table-center'],
27 | 'summaryOptions' => ['tag' => 'p', 'class' => 'text-right text-info'],
28 | 'columns' => [
29 | [
30 | 'class' => 'yii\grid\SerialColumn',
31 | 'header' => '编号',
32 | 'headerOptions' => ['class' => 'col-md-1'],
33 | ],
34 | [
35 | 'attribute' => 'name',
36 | 'label' => '活动名称',
37 | 'format' => 'html',
38 | 'headerOptions' => ['class' => 'col-md-3'],
39 | 'value' => function ($data) {
40 | return Html::tag('span', $data['icon_name'], [
41 | 'style' => [
42 | 'background-color' => $data['icon_color'],
43 | 'color' => 'white'
44 | ]
45 | ]).' '.$data['name'];
46 | }
47 | ],
48 | [
49 | 'attribute' => 'description',
50 | 'label' => '活动描述',
51 | 'headerOptions' => ['class' => 'col-md-4']
52 | ],
53 | [
54 | 'class' => 'yii\grid\ActionColumn',
55 | 'header' => '操作',
56 | 'headerOptions' => ['class' => 'col-md-4'],
57 | 'template' => '{update} {delete} {surplus}',
58 | 'buttons' => [
59 | 'delete' => function ($url, $model, $key) {
60 | return Html::a('', $url, ['title' => '删除']);
61 | },
62 | 'surplus' => function ($url, $model, $key) {
63 | return Html::a('', $url, ['title' => '库存变化记录']);
64 | },
65 | ]
66 | ]
67 | ]
68 | ]) ?>
69 |
70 |
71 |
--------------------------------------------------------------------------------
/views/business/profile.php:
--------------------------------------------------------------------------------
1 | title = '商家资料';
10 |
11 | ?>
12 |
13 |
14 | ['enctype' => 'multipart/form-data']
16 | ]); ?>
17 | = $form->field($model, 'name') ?>
18 | = $form->field($model, 'phone') ?>
19 | pic_url)) :?>
20 | = $form->field($model, 'image')->widget(FileInput::className(), [
21 | 'options' => ['accept' => 'image/*'],
22 | 'pluginOptions' => [
23 | 'showUpload' => false,
24 | 'browseLabel' => '选择图片',
25 | 'removeLabel' => '删除'
26 | ],
27 | ]) ?>
28 |
29 | = $form->field($model, 'image')->widget(FileInput::className(), [
30 | 'options' => ['accept' => 'image/*'],
31 | 'pluginOptions' => [
32 | 'showUpload' => false,
33 | 'browseLabel' => '选择图片',
34 | 'removeLabel' => '删除',
35 | 'initialPreview' => Html::img($model->pic_url, ['class' => 'file-preview-image'])
36 | ],
37 | ]) ?>
38 |
39 | = $form->field($model, 'address')->textarea(['rows' => '3']) ?>
40 |
41 | = Html::submitButton(' 修改', ['class' => 'btn btn-primary']) ?>
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/views/business/scene.php:
--------------------------------------------------------------------------------
1 | title = '店面实景';
12 |
13 | ?>
14 |
15 |
门面图
16 |
17 | = Gallery::widget([
18 | 'images' => $frontImages,
19 | 'type' => BusinessScene::TYPE_FRONT,
20 | 'apiRoute' => 'business/gallery'
21 | ]) ?>
22 |
23 |
24 |
25 |
大堂图
26 |
27 | = Gallery::widget([
28 | 'images' => $foyerImages,
29 | 'type' => BusinessScene::TYPE_FOYER,
30 | 'apiRoute' => 'business/gallery'
31 | ]) ?>
32 |
33 |
34 |
35 |
后厨图
36 |
37 | = Gallery::widget([
38 | 'images' => $kitchenImages,
39 | 'type' => BusinessScene::TYPE_KITCHEN,
40 | 'apiRoute' => 'business/gallery'
41 | ]) ?>
42 |
43 |
--------------------------------------------------------------------------------
/views/business/setup.php:
--------------------------------------------------------------------------------
1 | registerJsFile('@web/js/setup.js', [
18 | 'depends' => [
19 | 'app\assets\ClockPickerAsset',
20 | 'app\assets\MultiSelectAsset',
21 | ]
22 | ]);
23 |
24 | $this->title = '店铺设置';
25 |
26 | $html = '';
27 | foreach ($bookingTimes as $item) {
28 | $html .= "";
37 | }
38 | ?>
39 |
40 |
41 |
42 | = $form->field($model, 'status')->widget(SwitchInput::classname(), [
43 | 'type' => SwitchInput::CHECKBOX,
44 | ]); ?>
45 | = $form->field($model, 'bulletin')->textarea(['rows' => '5']) ?>
46 | = $form->field($model, 'opening_time', [
47 | 'inputTemplate' => '{input}
'
48 | ])->hiddenInput() ?>
49 | = $form->field($model, 'booking_times', [
50 | 'inputTemplate' => '{input}
'
51 | ])->hiddenInput() ?>
52 | = $form->field($model, 'shipping_fee', [
53 | 'inputTemplate' => '
¥{input}.00
',
54 | ]) ?>
55 | = $form->field($model, 'package_fee', [
56 | 'inputTemplate' => '
¥{input}.00
',
57 | ]) ?>
58 | = $form->field($model, 'min_price', [
59 | 'inputTemplate' => '
¥{input}.00
',
60 | ]) ?>
61 |
62 | = Html::submitButton(' 提交', ['class' => 'btn btn-primary']) ?>
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/views/category/form.php:
--------------------------------------------------------------------------------
1 | title = $model->isNewRecord ? '添加分类' : '更新分类';
10 |
11 | ?>
12 |
13 |
14 |
15 | = $form->field($model, 'name') ?>
16 | = $form->field($model, 'description')->textarea() ?>
17 |
18 | = Html::submitButton(' 保存', ['class' => 'btn btn-primary']) ?>
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/views/category/index.php:
--------------------------------------------------------------------------------
1 | title = '商品分类列表';
13 |
14 | ?>
15 |
16 | = Html::a(' 添加商品分类', ['category/add'], ['class' => 'btn btn-primary']) ?>
17 |
18 |
19 |
20 |
21 | = GridView::widget([
22 | 'dataProvider' => $dataProvider,
23 | 'filterModel' => $searchModel,
24 | 'tableOptions' => ['class' => 'table table-striped table-bordered table-center'],
25 | 'summaryOptions' => ['tag' => 'p', 'class' => 'text-right text-info'],
26 | 'columns' => [
27 | [
28 | 'attribute' => 'id',
29 | 'headerOptions' => ['class' => 'col-md-1']
30 | ],
31 | [
32 | 'attribute' => 'name',
33 | 'headerOptions' => ['class' => 'col-md-1'],
34 | 'filterInputOptions' => ['class' => 'form-control input-sm']
35 | ],
36 | [
37 | 'attribute' => 'description',
38 | 'headerOptions' => ['class' => 'col-md-2'],
39 | 'filterInputOptions' => ['class' => 'form-control input-sm']
40 | ],
41 | [
42 | 'attribute' => 'created_at',
43 | 'format' => ['date', 'php:Y-m-d H:i'],
44 | 'filter' => DatePicker::widget([
45 | 'model' => $searchModel,
46 | 'type' => DatePicker::TYPE_COMPONENT_APPEND,
47 | 'attribute' => 'date',
48 | 'options' => ['class' => 'input-sm'],
49 | 'pluginOptions' => [
50 | 'autoclose' => true,
51 | 'format' => 'yyyy-mm-dd'
52 | ]
53 | ]),
54 | 'headerOptions' => ['class' => 'col-md-1']
55 | ],
56 | [
57 | 'class' => 'yii\grid\ActionColumn',
58 | 'header' => '操作',
59 | 'headerOptions' => ['class' => 'col-md-1'],
60 | 'template' => '{update} {delete}',
61 | 'buttons' => [
62 | 'update' => function ($url, $model, $key) {
63 | return Html::a('', $url, ['title' => '修改']);
64 | },
65 | 'delete' => function ($url, $model, $key) {
66 | return Html::a('', $url, ['title' => '删除']);
67 | },
68 | ]
69 | ]
70 | ]
71 | ]) ?>
72 |
73 |
74 |
--------------------------------------------------------------------------------
/views/gallery.php:
--------------------------------------------------------------------------------
1 |
6 | */
7 | use yii\helpers\Html;
8 | use yii\web\View;
9 |
10 | ?>
11 |
12 |
13 |
14 |
33 |
34 |
35 |
36 |
37 |
41 |
42 |
43 |
44 |
45 |
46 | 拖拽文件到这儿
47 |
48 |
49 |
50 |
70 |
71 |
--------------------------------------------------------------------------------
/views/layouts/base.php:
--------------------------------------------------------------------------------
1 |
12 |
13 | beginPage() ?>
14 |
15 |
16 |
17 |
18 |
19 |
20 | = Html::csrfMetaTags() ?>
21 | = Html::encode($this->title) ?>
22 | head() ?>
23 |
24 |
25 | beginBody() ?>
26 | = $content ?>
27 | endBody() ?>
28 |
29 |
30 | endPage() ?>
31 |
--------------------------------------------------------------------------------
/views/layouts/main.php:
--------------------------------------------------------------------------------
1 | user->id);
16 | $route = Yii::$app->requestedAction->uniqueId;
17 |
18 | ?>
19 | beginContent('@app/views/layouts/base.php'); ?>
20 |
21 |
22 |
23 |
50 |
51 |
126 |
127 |
128 |
129 |
134 |
135 |
136 | = Alert::widget() ?>
137 |
138 |
139 | = $content ?>
140 |
141 |
142 |
143 |
144 |
145 |
146 | endContent() ?>
--------------------------------------------------------------------------------
/views/order/index.php:
--------------------------------------------------------------------------------
1 | title = isset($statusList[$status]) ? $statusList[$status] : '';
16 |
17 | $buttons = [];
18 | if ($status == Order::STATUS_WAIT_ACCEPT) {
19 | $buttons['status'] = function ($url, $model, $key) {
20 | return Html::a('接单', ['/order/status',
21 | 'id' => $model->id,
22 | 'current' => Order::STATUS_WAIT_ACCEPT,
23 | 'target' => Order::STATUS_WAIT_SEND
24 | ], ['class' => 'btn btn-primary']);
25 | };
26 | } else if ($status == Order::STATUS_WAIT_SEND) {
27 | $buttons['status'] = function ($url, $model, $key) {
28 | return Html::a('配送', ['/order/status',
29 | 'id' => $model->id,
30 | 'current' => Order::STATUS_WAIT_SEND,
31 | 'target' => Order::STATUS_WAIT_ARRIVE
32 | ], ['class' => 'btn btn-primary']);
33 | };
34 | }
35 |
36 | ?>
37 |
38 |
39 |
40 | = GridView::widget([
41 | 'dataProvider' => $dataProvider,
42 | 'filterModel' => $searchModel,
43 | 'tableOptions' => ['class' => 'table table-striped table-bordered table-center'],
44 | 'summaryOptions' => ['tag' => 'p', 'class' => 'text-right text-info'],
45 | 'columns' => [
46 | [
47 | 'attribute' => 'id',
48 | 'headerOptions' => ['class' => 'col-lg-1'],
49 | 'filterInputOptions' => ['class' => 'form-control input-sm']
50 | ],
51 | [
52 | 'attribute' => 'consignee',
53 | 'headerOptions' => ['class' => 'col-lg-1'],
54 | 'filterInputOptions' => ['class' => 'form-control input-sm']
55 | ],
56 | [
57 | 'attribute' => 'phone',
58 | 'headerOptions' => ['class' => 'col-lg-1'],
59 | 'filterInputOptions' => ['class' => 'form-control input-sm']
60 | ],
61 | [
62 | 'attribute' => 'address',
63 | 'headerOptions' => ['class' => 'col-lg-2'],
64 | 'filterInputOptions' => ['class' => 'form-control input-sm']
65 | ],
66 | [
67 | 'attribute' => 'total_price',
68 | 'headerOptions' => ['class' => 'col-lg-1'],
69 | 'format' => 'html',
70 | 'filterInputOptions' => ['class' => 'form-control input-sm', 'title' => '支持运算符'],
71 | 'value' => function ($model, $key, $index, $column) {
72 | /** @var $model Order */
73 | return '¥ ' . $model->total_price;
74 | }
75 | ],
76 | [
77 | 'attribute' => 'pay_method',
78 | 'filter' => Order::getPayMethodList(),
79 | 'filterInputOptions' => ['class' => 'form-control input-sm'],
80 | 'headerOptions' => ['class' => 'col-lg-1'],
81 | 'value' => function ($model, $key, $index, $column) {
82 | /** @var $model Order */
83 | $list = Order::getPayMethodList();
84 |
85 | return $list[$model->pay_method];
86 | }
87 | ],
88 | [
89 | 'attribute' => 'remark',
90 | 'headerOptions' => ['class' => 'col-lg-2'],
91 | 'filterInputOptions' => ['class' => 'form-control input-sm']
92 | ],
93 | [
94 | 'attribute' => 'created_at',
95 | 'headerOptions' => ['class' => 'col-lg-2'],
96 | 'format' => ['date', 'php:Y-m-d H:i'],
97 | 'filter' => DatePicker::widget([
98 | 'model' => $searchModel,
99 | 'type' => DatePicker::TYPE_INPUT,
100 | 'attribute' => 'date',
101 | 'options' => ['class' => 'input-sm'],
102 | 'pluginOptions' => [
103 | 'autoclose' => true,
104 | 'format' => 'yyyy-mm-dd hh:mm'
105 | ]
106 | ]),
107 | ],
108 | [
109 | 'class' => 'yii\grid\ActionColumn',
110 | 'header' => '操作',
111 | 'headerOptions' => ['class' => 'col-lg-1'],
112 | 'template' => '{status}',
113 | 'buttons' => $buttons
114 | ]
115 | ]
116 | ]) ?>
117 |
118 |
119 |
--------------------------------------------------------------------------------
/views/product/form.php:
--------------------------------------------------------------------------------
1 | title = $model->isNewRecord ? '添加商品' : '更新商品';
12 |
13 | ?>
14 |
15 |
16 | ['enctype' => 'multipart/form-data']
18 | ]); ?>
19 | = $form->field($model, 'name') ?>
20 | = $form->field($model, 'description')->textarea() ?>
21 | = $form->field($model, 'category_id')->widget(Select2::className(), [
22 | 'data' => Category::getKeyValuePairs(),
23 | 'options' => ['placeholder' => '请选择分类'],
24 | 'pluginOptions' => [
25 | 'allowClear' => true
26 | ],
27 | ]) ?>
28 | isNewRecord) :?>
29 | = $form->field($model, 'image')->widget(FileInput::className(), [
30 | 'options' => ['accept' => 'image/*'],
31 | 'pluginOptions' => [
32 | 'showUpload' => false,
33 | 'browseLabel' => '选择图片',
34 | 'removeLabel' => '删除'
35 | ],
36 | ]) ?>
37 |
38 | = $form->field($model, 'image')->widget(FileInput::className(), [
39 | 'options' => ['accept' => 'image/*'],
40 | 'pluginOptions' => [
41 | 'showUpload' => false,
42 | 'browseLabel' => '选择图片',
43 | 'removeLabel' => '删除',
44 | 'initialPreview' => Html::img($model->image_path, ['class' => 'file-preview-image'])
45 | ],
46 | ]) ?>
47 |
48 | = $form->field($model, 'price') ?>
49 | = $form->field($model, 'left_num') ?>
50 |
51 | = Html::submitButton(' 保存', ['class' => 'btn btn-primary']) ?>
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/views/product/index.php:
--------------------------------------------------------------------------------
1 | title = '商品列表';
15 |
16 | ?>
17 |
18 |
19 | = Html::a(' 添加商品', ['product/add'], ['class' => 'btn btn-primary']) ?>
20 |
21 |
22 |
23 |
24 | = GridView::widget([
25 | 'dataProvider' => $dataProvider,
26 | 'filterModel' => $searchModel,
27 | 'tableOptions' => ['class' => 'table table-striped table-bordered table-center'],
28 | 'summaryOptions' => ['tag' => 'p', 'class' => 'text-right text-info'],
29 | 'columns' => [
30 | [
31 | 'attribute' => 'id',
32 | 'headerOptions' => ['class' => 'col-md-1'],
33 | ],
34 | [
35 | 'attribute' => 'image_path',
36 | 'headerOptions' => ['class' => 'col-md-1'],
37 | 'format' => 'raw',
38 | 'value' => function($model){
39 | /** @var $model Product */
40 | return Html::img($model->image_path, [
41 | 'class' => 'img-rounded',
42 | 'width' => 50,
43 | 'height' => 50
44 | ]);
45 | }
46 | ],
47 | [
48 | 'attribute' => 'name',
49 | 'headerOptions' => ['class' => 'col-md-1'],
50 | 'filterInputOptions' => ['class' => 'form-control input-sm']
51 | ],
52 | [
53 | 'attribute' => 'description',
54 | 'headerOptions' => ['class' => 'col-md-2'],
55 | 'filterInputOptions' => ['class' => 'form-control input-sm']
56 | ],
57 | [
58 | 'attribute' => 'category_id',
59 | 'headerOptions' => ['class' => 'col-md-1'],
60 | 'filter' => Category::getKeyValuePairs(),
61 | 'filterInputOptions' => ['class' => 'form-control input-sm'],
62 | 'value' => function ($model, $key, $index, $column) {
63 | return $model->category->name;
64 | }
65 | ],
66 | [
67 | 'attribute' => 'price',
68 | 'headerOptions' => ['class' => 'col-md-1'],
69 | 'format' => 'html',
70 | 'filterInputOptions' => ['class' => 'form-control input-sm', 'title' => '支持运算符'],
71 | 'value' => function ($model, $key, $index, $column) {
72 | return '¥ ' . $model->price;
73 | }
74 | ],
75 | [
76 | 'attribute' => 'month_sales',
77 | 'headerOptions' => ['class' => 'col-md-1'],
78 | 'filterInputOptions' => ['class' => 'form-control input-sm', 'title' => '支持运算符']
79 | ],
80 | [
81 | 'attribute' => 'left_num',
82 | 'headerOptions' => ['class' => 'col-md-1'],
83 | 'filterInputOptions' => ['class' => 'form-control input-sm', 'title' => '支持运算符']
84 | ],
85 | [
86 | 'attribute' => 'created_at',
87 | 'headerOptions' => ['class' => 'col-md-2'],
88 | 'format' => ['date', 'php:Y-m-d H:i'],
89 | 'filter' => DatePicker::widget([
90 | 'model' => $searchModel,
91 | 'type' => DatePicker::TYPE_COMPONENT_APPEND,
92 | 'attribute' => 'date',
93 | 'options' => ['class' => 'input-sm'],
94 | 'pluginOptions' => [
95 | 'autoclose' => true,
96 | 'format' => 'yyyy-mm-dd'
97 | ]
98 | ]),
99 | ],
100 | [
101 | 'class' => 'yii\grid\ActionColumn',
102 | 'header' => '操作',
103 | 'headerOptions' => ['class' => 'col-md-1'],
104 | 'template' => '{update} {delete} {surplus}',
105 | 'buttons' => [
106 | 'delete' => function ($url, $model, $key) {
107 | return Html::a('', $url, ['title' => '删除']);
108 | },
109 | 'surplus' => function ($url, $model, $key) {
110 | return Html::a('', $url, ['title' => '库存变化记录']);
111 | },
112 | ]
113 | ]
114 | ]
115 | ]) ?>
116 |
117 |
118 |
--------------------------------------------------------------------------------
/views/site/error.php:
--------------------------------------------------------------------------------
1 | title = $name;
10 |
11 | ?>
12 |
13 |
14 | = nl2br(Html::encode($message)) ?>
15 |
16 |
服务器处理您的请求时出现以上错误。
17 |
请联系我们如果您觉得该错误很严重的话,谢谢。
18 |
19 |
--------------------------------------------------------------------------------
/views/user/login.php:
--------------------------------------------------------------------------------
1 | registerJsFile('@web/js/login.js', [
19 | 'depends' => [
20 | 'app\assets\CountDownAsset',
21 | ]
22 | ]);
23 |
24 | $this->title = '登录';
25 |
26 | ?>
27 |
28 |
29 | = Html::img('@web/images/logo.png') ?>
30 |
31 |
32 |
33 |
70 |
71 |
72 |
73 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/views/user/profile.php:
--------------------------------------------------------------------------------
1 | title = '个人资料';
10 |
11 | ?>
12 |
13 |
14 |
15 | = $form->field($model, 'user_name')->textInput(['disabled' => true]) ?>
16 | = $form->field($model, 'real_name')->textInput(['disabled' => true]) ?>
17 | = $form->field($model, 'identity_num')->textInput(['disabled' => true]) ?>
18 | = $form->field($model, 'phone')->textInput(['disabled' => true]) ?>
19 | = $form->field($model, 'email')->textInput(['disabled' => true]) ?>
20 | = $form->field($model, 'gender')->dropDownList(Admin::getGenderList()) ?>
21 |
22 | = Html::submitButton(' 保存', ['class' => 'btn btn-primary']) ?>
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/views/user/update-password.php:
--------------------------------------------------------------------------------
1 | title = '修改密码';
10 |
11 | ?>
12 |
13 |
14 |
15 | = $form->field($model, 'old_password')->passwordInput() ?>
16 | = $form->field($model, 'new_password')->passwordInput() ?>
17 | = $form->field($model, 'repeat_password')->passwordInput() ?>
18 |
19 | = Html::submitButton(' 确认修改', ['class' => 'btn btn-primary']) ?>
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/views/user/update-phone.php:
--------------------------------------------------------------------------------
1 | registerJsFile('@web/js/update-phone.js', [
18 | 'depends' => [
19 | 'app\assets\CountDownAsset',
20 | ]
21 | ]);
22 |
23 | $this->title = '修改手机号';
24 |
25 | ?>
26 |
27 |
28 |
29 |
40 |
41 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/web/.htaccess:
--------------------------------------------------------------------------------
1 | Options +FollowSymLinks
2 | IndexIgnore */*
3 |
4 | RewriteEngine on
5 |
6 | # if a directory or a file exists, use it directly
7 | RewriteCond %{REQUEST_FILENAME} !-f
8 | RewriteCond %{REQUEST_FILENAME} !-d
9 |
10 | # otherwise forward it to index.php
11 | RewriteRule . index.php
--------------------------------------------------------------------------------
/web/assets/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/web/css/gallery.css:
--------------------------------------------------------------------------------
1 | /* Photo Gallery */
2 | .gallery {
3 | position: relative;
4 | border: 1px solid #DDD;
5 | -webkit-border-radius: 4px;
6 | -moz-border-radius: 4px;
7 | border-radius: 4px;
8 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);
9 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);
10 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);
11 | min-height: 250px;
12 | }
13 |
14 | .gallery .photo {
15 | position: relative;
16 | float: left;
17 | background-color: #fff;
18 | margin: 4px;
19 | width: 145px;
20 | height: 185px;
21 | display: block;
22 | padding: 4px;
23 | line-height: 1;
24 | border: 1px solid #DDD;
25 | -webkit-border-radius: 4px;
26 | -moz-border-radius: 4px;
27 | border-radius: 4px;
28 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);
29 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);
30 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075);
31 | }
32 |
33 | .gallery .photo img {
34 | width: 145px;
35 | height: 145px;
36 | }
37 |
38 | .gallery .photo .actions {
39 | float: right;
40 | position: absolute;
41 | bottom: 4px;
42 | right: 4px;
43 | }
44 |
45 | .gallery hr {
46 | margin: 0 4px;
47 | }
48 |
49 | .gallery .btn-file {
50 | position: relative;
51 | overflow: hidden;
52 | }
53 |
54 | .gallery .btn-file input[type=file] {
55 | position: absolute;
56 | top: 0;
57 | right: 0;
58 | min-width: 100%;
59 | min-height: 100%;
60 | font-size: 999px;
61 | text-align: right;
62 | filter: alpha(opacity=0);
63 | opacity: 0;
64 | outline: none;
65 | background: white;
66 | cursor: inherit;
67 | display: block;
68 | }
69 |
70 | .gallery .image-preview {
71 | height: 200px;
72 | overflow: hidden;
73 | }
74 |
75 | .gallery .photo-select {
76 | position: absolute;
77 | bottom: 8px;
78 | left: 8px;
79 | }
80 |
81 | .gallery .photo.selected {
82 | background-color: #cef;
83 | border-color: blue;
84 | }
85 |
86 | .gallery .overlay {
87 | display: none;
88 | position: absolute;
89 | top: 0;
90 | left: 0;
91 | width: 100%;
92 | height: 100%;
93 | }
94 |
95 | .gallery .overlay-bg {
96 | position: absolute;
97 | top: 0;
98 | left: 0;
99 | background-color: #efefef;
100 | opacity: .5;
101 | width: 100%;
102 | height: 100%;
103 | }
104 |
105 | .gallery.over .overlay {
106 | display: block;
107 | }
108 |
109 | .gallery .drop-hint {
110 | background-color: #efefef;
111 | border: 2px #777 dashed;
112 | position: absolute;
113 | top: 50%;
114 | left: 50%;
115 | height: 100px;
116 | width: 50%;
117 | margin: -50px 0 0 -25%;
118 | text-align: center;
119 | }
120 |
121 | .gallery .drop-hint-info {
122 | color: #777;
123 | font-weight: bold;
124 | font-size: 30px;
125 | margin-top: 35px;
126 | vertical-align: middle;
127 | display: inline-block;
128 | }
129 |
130 | .gallery .progress-overlay {
131 | display: none;
132 | position: absolute;
133 | top: 0;
134 | left: 0;
135 | width: 100%;
136 | height: 100%;
137 | }
138 |
139 | .gallery .progress-overlay .modal {
140 | position: relative;
141 | display: block;
142 | top: 80px;
143 | }
144 |
145 | .editor-modal .photo-editor {
146 | min-height: 156px;
147 | padding: 4px;
148 | }
149 |
150 | .editor-modal .photo-editor:not(:last-child) {
151 | border-bottom: 1px solid #DDD;
152 | }
153 |
--------------------------------------------------------------------------------
/web/css/lightbox.css:
--------------------------------------------------------------------------------
1 | /* Preload images */
2 | body:after {
3 | content: url(../images/close.png) url(../images/loading.gif) url(../images/prev.png) url(../images/next.png);
4 | display: none;
5 | }
6 |
7 | body.lb-disable-scrolling {
8 | overflow: hidden;
9 | }
10 |
11 | .lightboxOverlay {
12 | position: absolute;
13 | top: 0;
14 | left: 0;
15 | z-index: 9999;
16 | background-color: black;
17 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
18 | opacity: 0.8;
19 | display: none;
20 | }
21 |
22 | .lightbox {
23 | position: absolute;
24 | left: 0;
25 | width: 100%;
26 | z-index: 10000;
27 | text-align: center;
28 | line-height: 0;
29 | font-weight: normal;
30 | }
31 |
32 | .lightbox .lb-image {
33 | display: block;
34 | height: auto;
35 | max-width: inherit;
36 | border-radius: 3px;
37 | }
38 |
39 | .lightbox a img {
40 | border: none;
41 | }
42 |
43 | .lb-outerContainer {
44 | position: relative;
45 | background-color: white;
46 | *zoom: 1;
47 | width: 250px;
48 | height: 250px;
49 | margin: 0 auto;
50 | border-radius: 4px;
51 | }
52 |
53 | .lb-outerContainer:after {
54 | content: "";
55 | display: table;
56 | clear: both;
57 | }
58 |
59 | .lb-container {
60 | padding: 4px;
61 | }
62 |
63 | .lb-loader {
64 | position: absolute;
65 | top: 43%;
66 | left: 0;
67 | height: 25%;
68 | width: 100%;
69 | text-align: center;
70 | line-height: 0;
71 | }
72 |
73 | .lb-cancel {
74 | display: block;
75 | width: 32px;
76 | height: 32px;
77 | margin: 0 auto;
78 | background: url(../images/loading.gif) no-repeat;
79 | }
80 |
81 | .lb-nav {
82 | position: absolute;
83 | top: 0;
84 | left: 0;
85 | height: 100%;
86 | width: 100%;
87 | z-index: 10;
88 | }
89 |
90 | .lb-container > .nav {
91 | left: 0;
92 | }
93 |
94 | .lb-nav a {
95 | outline: none;
96 | background-image: url('data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==');
97 | }
98 |
99 | .lb-prev, .lb-next {
100 | height: 100%;
101 | cursor: pointer;
102 | display: block;
103 | }
104 |
105 | .lb-nav a.lb-prev {
106 | width: 34%;
107 | left: 0;
108 | float: left;
109 | background: url(../images/prev.png) left 48% no-repeat;
110 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
111 | opacity: 0;
112 | -webkit-transition: opacity 0.6s;
113 | -moz-transition: opacity 0.6s;
114 | -o-transition: opacity 0.6s;
115 | transition: opacity 0.6s;
116 | }
117 |
118 | .lb-nav a.lb-prev:hover {
119 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
120 | opacity: 1;
121 | }
122 |
123 | .lb-nav a.lb-next {
124 | width: 64%;
125 | right: 0;
126 | float: right;
127 | background: url(../images/next.png) right 48% no-repeat;
128 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
129 | opacity: 0;
130 | -webkit-transition: opacity 0.6s;
131 | -moz-transition: opacity 0.6s;
132 | -o-transition: opacity 0.6s;
133 | transition: opacity 0.6s;
134 | }
135 |
136 | .lb-nav a.lb-next:hover {
137 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
138 | opacity: 1;
139 | }
140 |
141 | .lb-dataContainer {
142 | margin: 0 auto;
143 | padding-top: 5px;
144 | *zoom: 1;
145 | width: 100%;
146 | -moz-border-radius-bottomleft: 4px;
147 | -webkit-border-bottom-left-radius: 4px;
148 | border-bottom-left-radius: 4px;
149 | -moz-border-radius-bottomright: 4px;
150 | -webkit-border-bottom-right-radius: 4px;
151 | border-bottom-right-radius: 4px;
152 | }
153 |
154 | .lb-dataContainer:after {
155 | content: "";
156 | display: table;
157 | clear: both;
158 | }
159 |
160 | .lb-data {
161 | padding: 0 4px;
162 | color: #ccc;
163 | }
164 |
165 | .lb-data .lb-details {
166 | width: 85%;
167 | float: left;
168 | text-align: left;
169 | line-height: 1.1em;
170 | }
171 |
172 | .lb-data .lb-caption {
173 | font-size: 13px;
174 | font-weight: bold;
175 | line-height: 1em;
176 | }
177 |
178 | .lb-data .lb-number {
179 | display: block;
180 | clear: left;
181 | padding-bottom: 1em;
182 | font-size: 12px;
183 | color: #999999;
184 | }
185 |
186 | .lb-data .lb-close {
187 | display: block;
188 | float: right;
189 | width: 30px;
190 | height: 30px;
191 | background: url(../images/close.png) top right no-repeat;
192 | text-align: right;
193 | outline: none;
194 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70);
195 | opacity: 0.7;
196 | -webkit-transition: opacity 0.2s;
197 | -moz-transition: opacity 0.2s;
198 | -o-transition: opacity 0.2s;
199 | transition: opacity 0.2s;
200 | }
201 |
202 | .lb-data .lb-close:hover {
203 | cursor: pointer;
204 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
205 | opacity: 1;
206 | }
207 |
--------------------------------------------------------------------------------
/web/css/login.css:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 | /* CSS Document */
3 |
4 | body{
5 | background: url(../images/login_bgx.gif);
6 | }
7 |
8 | #login-box {
9 | width: 403px;
10 | margin: 100px auto 0;
11 | }
12 |
13 | #login-box .logo {
14 | text-align: center;
15 | margin-bottom: 20px;
16 | }
17 |
18 | #login-box .logo img {
19 | width: 196px;
20 | height: 46px;
21 | border: none;
22 | }
23 |
24 | #login-box .border {
25 | background: url(../images/login_border_bg.png) no-repeat;
26 | width: 403px;
27 | height: 302px;
28 | }
29 |
30 | #login-box .border {
31 | padding: 30px 40px;
32 | }
33 |
34 | #login-box .border form#mobile-login-form {
35 | display: block;
36 | }
37 |
38 | #login-box .border form#account-login-form {
39 | opacity: 0;
40 | display: none;
41 | }
--------------------------------------------------------------------------------
/web/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cheikh-wang/LazyWaimai-Web/c054ee4c11e11ad4e92abc376889335ad93fb4e3/web/favicon.ico
--------------------------------------------------------------------------------
/web/images/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cheikh-wang/LazyWaimai-Web/c054ee4c11e11ad4e92abc376889335ad93fb4e3/web/images/close.png
--------------------------------------------------------------------------------
/web/images/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cheikh-wang/LazyWaimai-Web/c054ee4c11e11ad4e92abc376889335ad93fb4e3/web/images/loading.gif
--------------------------------------------------------------------------------
/web/images/login_bgx.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cheikh-wang/LazyWaimai-Web/c054ee4c11e11ad4e92abc376889335ad93fb4e3/web/images/login_bgx.gif
--------------------------------------------------------------------------------
/web/images/login_border_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cheikh-wang/LazyWaimai-Web/c054ee4c11e11ad4e92abc376889335ad93fb4e3/web/images/login_border_bg.png
--------------------------------------------------------------------------------
/web/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cheikh-wang/LazyWaimai-Web/c054ee4c11e11ad4e92abc376889335ad93fb4e3/web/images/logo.png
--------------------------------------------------------------------------------
/web/images/next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cheikh-wang/LazyWaimai-Web/c054ee4c11e11ad4e92abc376889335ad93fb4e3/web/images/next.png
--------------------------------------------------------------------------------
/web/images/prev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cheikh-wang/LazyWaimai-Web/c054ee4c11e11ad4e92abc376889335ad93fb4e3/web/images/prev.png
--------------------------------------------------------------------------------
/web/index-test.php:
--------------------------------------------------------------------------------
1 | run();
17 |
--------------------------------------------------------------------------------
/web/index.php:
--------------------------------------------------------------------------------
1 | run();
13 |
--------------------------------------------------------------------------------
/web/js/login.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | var mobileLoginFrom = $('#mobile-login-form');
4 | var accountLoginForm = $('#account-login-form');
5 |
6 | ////////////////////////////////////////////
7 | //////// 切换登录方式 /////////////
8 | ////////////////////////////////////////////
9 | var speed = 400;
10 | $('#to-account-login').click(function(){
11 | mobileLoginFrom.fadeTo(speed, 0.01).hide();
12 | accountLoginForm.fadeTo(speed, 1).show();
13 | });
14 | $('#to-mobile-login').click(function(){
15 | accountLoginForm.fadeTo(speed, 0.01).hide()
16 | mobileLoginFrom.fadeTo(speed, 1).show();
17 | });
18 |
19 |
20 | ////////////////////////////////////////////
21 | //////// 发送验证码 /////////////
22 | ////////////////////////////////////////////
23 | $('button[name="send-sms-btn"]', mobileLoginFrom).click(function() {
24 | var phone = $('input[name="phone"]', mobileLoginFrom).val();
25 | if (phone == '') {
26 | $.toaster({message : '手机号不能为空', title : '', priority : 'danger'});
27 | return false;
28 | }
29 |
30 | var $this = $(this);
31 |
32 | // 发送验证码的 url
33 | var sendSmsUrl = $this.attr('data-url');
34 |
35 | // 通过ajax发送短信验证码
36 | $.ajax({
37 | url : sendSmsUrl,
38 | type : 'post',
39 | data : {phone : phone},
40 | dataType : 'json',
41 | beforeSend : function() {
42 | // 避免重复点击
43 | $this.attr('disabled', true);
44 | },
45 | success : function(data) {
46 | if (data.status === 'ok') {
47 | $.toaster({message : '短信发送成功, 请注意查收...', title : '', priority : 'success'});
48 |
49 | $this.html('60 秒后可重发');
50 | $this.find('em').countdown((new Date()).getTime() + 59000, function (event) {
51 | $(this).text(event.strftime('%S'));
52 | }).on('finish.countdown', function(event) {
53 | $this.attr('disabled', false);
54 | $this.text('重新发送');
55 | });
56 | } else {
57 | $.toaster({message : data.message, title : '', priority : 'danger'});
58 | $this.attr('disabled', false);
59 | }
60 | },
61 | error : function() {
62 | $.toaster({message : '系统异常', title : '', priority : 'danger'});
63 | $this.attr('disabled', false);
64 | }
65 | });
66 | });
67 |
68 |
69 | ////////////////////////////////////////////
70 | //////// 使用手机号登录 ////////////
71 | ////////////////////////////////////////////
72 | $('button[name="login-btn"]', mobileLoginFrom).click(function() {
73 | var phone = $('input[name="phone"]', mobileLoginFrom).val();
74 | var code = $('input[name="code"]', mobileLoginFrom).val();
75 | var remember = $('input[name="remember"]', mobileLoginFrom).is(':checked') ? 1 : 0;
76 | if (phone == '') {
77 | $.toaster({message : '手机号不能为空', title : '', priority : 'danger'});
78 | return false;
79 | }
80 | if (code == '') {
81 | $.toaster({message : '验证码不能为空', title : '', priority : 'danger'});
82 | return false;
83 | }
84 |
85 | var $this = $(this);
86 |
87 | // 验证手机号登录的url
88 | var loginUrl = $this.attr('data-url');
89 |
90 | // 通过ajax验证手机号登录
91 | $.ajax({
92 | url : loginUrl,
93 | type : 'post',
94 | data : {
95 | phone : phone,
96 | code : code,
97 | remember : remember
98 | },
99 | dataType : 'json',
100 | beforeSend : function() { // 避免重复点击
101 | $this.attr('disabled', true);
102 | },
103 | success : function(data) {
104 | if (data.status === 'err') {
105 | $.toaster({message : data.message, title : '', priority : 'danger'});
106 | $this.attr('disabled', false);
107 | }
108 | },
109 | error : function() {
110 | $.toaster({message : '系统异常', title : '', priority : 'danger'});
111 | $this.attr('disabled', false);
112 | }
113 | });
114 | });
115 |
116 |
117 | ////////////////////////////////////////////
118 | //////// 使用账户登录 /////////////
119 | ////////////////////////////////////////////
120 | $('button[name="login-btn"]', accountLoginForm).click(function() {
121 | var username = $('input[name="username"]', accountLoginForm).val();
122 | var password = $('input[name="password"]', accountLoginForm).val();
123 | var remember = $('input[name="remember"]', accountLoginForm).is(':checked') ? 1 : 0;
124 | if (username == '') {
125 | $.toaster({message : '帐号不能为空', title : '', priority : 'danger'});
126 | return false;
127 | }
128 | if (password == '') {
129 | $.toaster({message : '密码不能为空', title : '', priority : 'danger'});
130 | return false;
131 | }
132 |
133 | var $this = $(this);
134 |
135 | // 验证手机号登录的url
136 | var loginUrl = $this.attr('data-url');
137 |
138 | // 通过ajax验证手机号登录
139 | $.ajax({
140 | url : loginUrl,
141 | type : 'post',
142 | data : {
143 | username : username,
144 | password : password,
145 | remember : remember
146 | },
147 | dataType : 'json',
148 | beforeSend : function() { // 避免重复点击
149 | $this.attr('disabled', true);
150 | },
151 | success : function(data) {
152 | if (data.status === 'err') {
153 | $.toaster({message : data.message, title : '', priority : 'danger'});
154 | $this.attr('disabled', false);
155 | }
156 | },
157 | error : function() {
158 | $.toaster({message : '系统异常', title : '', priority : 'danger'});
159 | $this.attr('disabled', false);
160 | }
161 | });
162 | });
163 | });
--------------------------------------------------------------------------------
/web/js/setup.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | var rowTemplate = '';
20 | var addBtnTemplate = '';
23 | var deleteBtnTemplate = '';
26 |
27 | var timeRows = $('#time-rows');
28 |
29 | function addTimeRow(openTime, closeTime) {
30 | var row = $(rowTemplate);
31 | var operateBtn = $('.operate-btn', row);
32 |
33 | if (timeRows.children().length == 0) {
34 | operateBtn.append(addBtnTemplate);
35 | } else {
36 | operateBtn.append(deleteBtnTemplate);
37 | }
38 | timeRows.append(row);
39 |
40 | row.find('.clockpicker').clockpicker({
41 | afterDone: updateTimes
42 | });
43 |
44 | if (openTime != null && closeTime != null) {
45 | row.find('input.open-time').val(openTime);
46 | row.find('input.close-time').val(closeTime);
47 | }
48 | }
49 |
50 | function deleteTimeRow() {
51 | var row = $(this).closest('div.time-row');
52 | row.remove();
53 | updateTimes();
54 | }
55 |
56 | function updateTimes() {
57 | var times = [];
58 | $('div.time-row', timeRows).each(function() {
59 | var openTime = $(this).find('input.open-time').val();
60 | var closeTime = $(this).find('input.close-time').val();
61 | times.push({
62 | 'open_time': openTime,
63 | 'close_time': closeTime
64 | });
65 | });
66 | timeRows.prev('input').val(JSON.stringify(times));
67 | }
68 |
69 | function setupDefaultTimes() {
70 | var json = timeRows.prev('input').val();
71 | var times = null;
72 | try {
73 | times = $.parseJSON(json);
74 | } catch (e) {
75 | //console.error(e);
76 | // noting to do...
77 | }
78 | if (times == null || times.length == 0) {
79 | addTimeRow(null, null);
80 | } else {
81 | for (var i = 0; i < times.length; i++) {
82 | var time = times[i];
83 | addTimeRow(time['open_time'], time['close_time']);
84 | }
85 | }
86 | }
87 |
88 | $(document).on('click', '.add-time-row', addTimeRow);
89 | $(document).on('click', '.delete-time-row', deleteTimeRow);
90 |
91 | setupDefaultTimes();
92 |
93 |
94 |
95 |
96 |
97 | $('#booking-times').multiselect({
98 | enableCollapsibleOptGroups: true,
99 | onDropdownHidden: function() {
100 | var times = [];
101 | $('#booking-times option:selected').each(function() {
102 | times.push($(this).val());
103 | });
104 | $('#booking-times').prev('input').val(times.join(','));
105 | }
106 | });
107 | });
--------------------------------------------------------------------------------
/web/js/site.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | $('#side-menu').metisMenu();
4 |
5 | var element = $('#side-menu li.active').parent().addClass('in').parent();
6 | if (element.is('li')) {
7 | element.addClass('active');
8 | }
9 | });
--------------------------------------------------------------------------------
/web/js/update-phone.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | function toastSuccess(message) {
4 | $.toaster({message : message, title : '', priority : 'success'});
5 | }
6 |
7 | function toastError(message) {
8 | $.toaster({message : message, title : '', priority : 'danger'});
9 | }
10 |
11 | ////////////////////////////////////////////
12 | //////// 发送验证码 /////////////
13 | ////////////////////////////////////////////
14 | $('#send-sms-btn').click(function() {
15 | var phone = $('#changephoneform-phone').val();
16 | if (phone == '') {
17 | toastError('手机号不能为空');
18 | return false;
19 | }
20 |
21 | var $this = $(this);
22 |
23 | // 发送验证码的 url
24 | var sendSmsUrl = $(this).attr('data-url');
25 |
26 | // 通过ajax发送短信验证码
27 | $.ajax({
28 | url : sendSmsUrl,
29 | type : 'post',
30 | data : {phone : phone},
31 | dataType : 'json',
32 | beforeSend : function() {
33 | // 避免重复点击
34 | $this.attr('disabled', true);
35 | }
36 | }).done(function (data) {
37 | if (data.status === 'ok') {
38 | toastSuccess('短信发送成功,请注意查收...');
39 |
40 | $this.html('60 秒后可重发');
41 | $this.find('em').countdown((new Date()).getTime() + 59000, function (event) {
42 | $(this).text(event.strftime('%S'));
43 | }).on('finish.countdown', function(event) {
44 | $this.attr('disabled', false);
45 | $this.text('重新发送');
46 | });
47 | } else {
48 | $this.attr('disabled', false);
49 |
50 | toastError(data.message);
51 | }
52 | });
53 | });
54 | });
--------------------------------------------------------------------------------
/web/raw/booking_times.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "group_label": "上午",
4 | "group_items": [
5 | "09:00-09:15",
6 | "09:15-09:30",
7 | "09:30-09:45",
8 | "09:45-10:00",
9 | "10:00-10:15",
10 | "10:15-10:30",
11 | "10:30-10:45",
12 | "10:45-11:00",
13 | "11:00-11:15",
14 | "11:15-11:30",
15 | "11:30-11:45",
16 | "11:45-12:00",
17 | "12:00-12:15",
18 | "12:15-12:30",
19 | "12:30-12:45",
20 | "12:45-13:00",
21 | "13:00-13:15",
22 | "13:15-13:30",
23 | "13:30-13:45",
24 | "13:45-14:00"
25 | ]
26 | },
27 | {
28 | "group_label": "下午",
29 | "group_items": [
30 | "16:00-16:15",
31 | "16:15-16:30",
32 | "16:30-16:45",
33 | "16:45-17:00",
34 | "17:00-17:15",
35 | "17:15-17:30",
36 | "17:30-17:45",
37 | "17:45-18:00",
38 | "18:00-18:15",
39 | "18:15-18:30",
40 | "18:30-18:45",
41 | "18:45-19:00",
42 | "19:00-19:15",
43 | "19:15-19:30",
44 | "19:30-19:45",
45 | "19:45-20:00"
46 | ]
47 | }
48 | ]
--------------------------------------------------------------------------------
/web/robots.txt:
--------------------------------------------------------------------------------
1 | User-Agent: *
2 | Disallow: /
3 |
--------------------------------------------------------------------------------
/widgets/Alert.php:
--------------------------------------------------------------------------------
1 | getSession()->setFlash('error', 'This is the message');
16 | * \Yii::$app->getSession()->setFlash('success', 'This is the message');
17 | * \Yii::$app->getSession()->setFlash('info', 'This is the message');
18 | * ```
19 | *
20 | * Multiple messages could be set as follows:
21 | *
22 | * ```php
23 | * \Yii::$app->getSession()->setFlash('error', ['Error 1', 'Error 2']);
24 | * ```
25 | *
26 | * @author Kartik Visweswaran
27 | * @author Alexander Makarov
28 | */
29 | class Alert extends \yii\bootstrap\Widget
30 | {
31 | /**
32 | * @var array the alert types configuration for the flash messages.
33 | * This array is setup as $key => $value, where:
34 | * - $key is the name of the session flash variable
35 | * - $value is the bootstrap alert type (i.e. danger, success, info, warning)
36 | */
37 | public $alertTypes = [
38 | 'error' => 'alert-danger',
39 | 'danger' => 'alert-danger',
40 | 'success' => 'alert-success',
41 | 'info' => 'alert-info',
42 | 'warning' => 'alert-warning'
43 | ];
44 |
45 | /**
46 | * @var array the options for rendering the close button tag.
47 | */
48 | public $closeButton = [];
49 |
50 | public function init()
51 | {
52 | parent::init();
53 |
54 | $session = \Yii::$app->getSession();
55 | $flashes = $session->getAllFlashes();
56 | $appendCss = isset($this->options['class']) ? ' ' . $this->options['class'] : '';
57 |
58 | foreach ($flashes as $type => $data) {
59 | if (isset($this->alertTypes[$type])) {
60 | $data = (array) $data;
61 | foreach ($data as $i => $message) {
62 | /* initialize css class for each alert box */
63 | $this->options['class'] = $this->alertTypes[$type] . $appendCss;
64 |
65 | /* assign unique id to each alert box */
66 | $this->options['id'] = $this->getId() . '-' . $type . '-' . $i;
67 |
68 | echo \yii\bootstrap\Alert::widget([
69 | 'body' => $message,
70 | 'closeButton' => $this->closeButton,
71 | 'options' => $this->options,
72 | ]);
73 | }
74 |
75 | $session->removeFlash($type);
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/widgets/Gallery.php:
--------------------------------------------------------------------------------
1 | apiRoute === null) {
33 | throw new Exception('$apiRoute must be set.', 500);
34 | }
35 |
36 | $images = [];
37 | foreach ($this->images as $image) {
38 | /** @var $image BusinessScene */
39 | $images[] = array(
40 | 'id' => $image->id,
41 | 'original_url' => $image->original_url,
42 | 'thumb_url' => $image->thumb_url,
43 | 'rank' => $image->rank
44 | );
45 | }
46 |
47 | $baseUrl = [
48 | $this->apiRoute,
49 | 'type' => $this->type
50 | ];
51 |
52 | $opts = array(
53 | 'uploadUrl' => Url::to($baseUrl + ['action' => 'ajaxUpload']),
54 | 'deleteUrl' => Url::to($baseUrl + ['action' => 'delete']),
55 | 'arrangeUrl' => Url::to($baseUrl + ['action' => 'order']),
56 | 'photos' => $images,
57 | );
58 |
59 | $opts = Json::encode($opts);
60 | $view = $this->getView();
61 | GalleryAsset::register($view);
62 | $view->registerJs("$('#{$this->id}').gallery({$opts});");
63 |
64 | $this->options['id'] = $this->id;
65 | $this->options['class'] = 'gallery';
66 |
67 | return $this->render('@app/views/gallery', [
68 | 'options' => $this->options
69 | ]);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/yii:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | run();
24 | exit($exitCode);
25 |
--------------------------------------------------------------------------------
/yii.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | rem -------------------------------------------------------------
4 | rem Yii command line bootstrap script for Windows.
5 | rem
6 | rem @author Qiang Xue
7 | rem @link http://www.yiiframework.com/
8 | rem @copyright Copyright (c) 2008 Yii Software LLC
9 | rem @license http://www.yiiframework.com/license/
10 | rem -------------------------------------------------------------
11 |
12 | @setlocal
13 |
14 | set YII_PATH=%~dp0
15 |
16 | if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
17 |
18 | "%PHP_COMMAND%" "%YII_PATH%yii" %*
19 |
20 | @endlocal
21 |
--------------------------------------------------------------------------------