李浩杰 před 4 roky
rodič
revize
5e2a26975e
47 změnil soubory, kde provedl 984 přidání a 64 odebrání
  1. 1 1
      app/Http/Controllers/Admin/TestController.php
  2. 8 2
      app/Http/Controllers/Api/mini/AuthController.php
  3. 21 1
      app/Http/Controllers/Api/mini/DataController.php
  4. 24 0
      app/Http/Controllers/Api/mini/FeedbackController.php
  5. 2 0
      app/Http/Controllers/Api/mini/OrderController.php
  6. 30 0
      app/Http/Controllers/Api/mini/UploadController.php
  7. 20 0
      app/Http/Controllers/Api/mini/UserController.php
  8. 1 1
      app/Http/Middleware/AuthMini.php
  9. 8 0
      app/Models/Feedback.php
  10. 2 2
      config/filesystems.php
  11. 30 0
      database/migrations/2020_12_21_004940_add_project_id_to_order_devices.php
  12. 34 0
      database/migrations/2020_12_21_065100_create_feedback_table.php
  13. 1 0
      database/seeds/InnerOrderSeeder.php
  14. 1 0
      database/seeds/MiniSeeder.php
  15. 15 6
      mini/app.js
  16. 8 2
      mini/app.json
  17. 5 1
      mini/app.wxss
  18. 66 0
      mini/pages/about/index.js
  19. 3 0
      mini/pages/about/index.json
  20. 2 0
      mini/pages/about/index.wxml
  21. 1 0
      mini/pages/about/index.wxss
  22. 13 2
      mini/pages/account/index.js
  23. 36 2
      mini/pages/account/index.wxml
  24. 74 0
      mini/pages/bind/index.js
  25. 3 0
      mini/pages/bind/index.json
  26. 6 0
      mini/pages/bind/index.wxml
  27. 1 0
      mini/pages/bind/index.wxss
  28. 9 6
      mini/pages/data/index.js
  29. 3 3
      mini/pages/data/index.wxml
  30. 146 0
      mini/pages/feedback/index.js
  31. 3 0
      mini/pages/feedback/index.json
  32. 21 0
      mini/pages/feedback/index.wxml
  33. 1 0
      mini/pages/feedback/index.wxss
  34. 1 31
      mini/pages/login/index.js
  35. 111 0
      mini/pages/password/index.js
  36. 3 0
      mini/pages/password/index.json
  37. 19 0
      mini/pages/password/index.wxml
  38. 1 0
      mini/pages/password/index.wxss
  39. 145 0
      mini/pages/user/index.js
  40. 4 0
      mini/pages/user/index.json
  41. 18 0
      mini/pages/user/index.wxml
  42. 1 0
      mini/pages/user/index.wxss
  43. 4 0
      mini/utils/env.js
  44. 1 2
      mini/utils/http.js
  45. 70 2
      mini/utils/util.js
  46. binární
      public/images/logo.png
  47. 7 0
      routes/api.php

+ 1 - 1
app/Http/Controllers/Admin/TestController.php

xqd
@@ -17,7 +17,7 @@ class TestController extends Controller
 {
     public function index(Request $request)
     {
-        dd(in_array('2020', ['2020']));
+        dd(OrderDevice::where('device_id', 1)->pluck('order_id')->unique());
     	return view('admin.test.index');
     }
 }

+ 8 - 2
app/Http/Controllers/Api/mini/AuthController.php

xqd
@@ -30,8 +30,14 @@ class AuthController extends BaseController
             if(isset($res['session_key'])) {
                 $data = $app->encryptor->decryptData($res['session_key'], $request->input('iv'), $request->input('encryptedData'));
                 if(isset($data['openId'])) {
-                    $user = User::where('open_id', $data['openId'])->first();
-                    if(empty($user)) return $this->error(['msg' => '用户不存在,请先绑定账户']);
+                    if($request->input('bind')) {
+                        $token = $request->header('X-Token');
+                        $user = User::where('token', $token)->first();
+                        if(!$user) return $this->error(['msg' => '找不到用户']);
+                    } else {
+                        $user = User::where('open_id', $data['openId'])->first();
+                        if(empty($user)) return $this->error(['msg' => '用户不存在,请先绑定账户']);
+                    }
                     $user->update([
                         'nickname' => $data['nickName'],
                         'avatar' => $data['avatarUrl']

+ 21 - 1
app/Http/Controllers/Api/mini/DataController.php

xqd xqd xqd
@@ -3,6 +3,7 @@
 namespace App\Http\Controllers\Api\mini;
 
 use App\Models\Order;
+use App\Models\OrderDevice;
 use App\Models\Project;
 use Carbon\Carbon;
 use Illuminate\Http\Request;
@@ -12,9 +13,12 @@ class DataController extends BaseController
 {
     protected $order;
 
+    protected $order_device;
+
     public function __construct()
     {
         $this->order = new Order();
+        $this->order_device = new OrderDevice();
     }
 
     public function getYearsAndMonths(Request $request)
@@ -59,7 +63,23 @@ class DataController extends BaseController
         $date_data = $this->parseDate($date);
         $date = Carbon::createFromDate($date_data['year'], $date_data['month'], $date_data['day'])->toDateTimeString();
         $ids = $request->input('project_ids');
-        $orders = $this->order->whereIn('project_id', $ids)->where('created_at', '>=', $date)->orderBy('created_at')->get();
+        $order_devices = $this->order_device->whereIn('project_id', $ids);
+
+        $in_items = ['device_ids', 'device_name_ids', 'spec_ids', 'rent_type_ids'];
+        $key_items = ['device_id', 'device_name_id', 'spec_id', 'rent_type_id'];
+        foreach ($in_items as $key => $item) {
+            if($request->input($item)) {
+                $item_ids = collect($request->input($item))->filter(function($id) {
+                    return $id;
+                });
+                if($item_ids->count() > 0) {
+                    $order_devices = $order_devices->whereIn($key_items[$key], $item_ids);
+                }
+            }
+        }
+
+        $order_ids = $order_devices->pluck('order_id')->unique();
+        $orders = $this->order->whereIn('id', $order_ids)->get();
         $names = [];
         $values = [];
         // year|month

+ 24 - 0
app/Http/Controllers/Api/mini/FeedbackController.php

xqd
@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Http\Controllers\Api\mini;
+
+use App\Models\Feedback;
+use Illuminate\Http\Request;
+
+class FeedbackController extends BaseController
+{
+    protected $model;
+
+    public function __construct()
+    {
+        $this->model = new Feedback();
+    }
+
+    public function create(Request $request)
+    {
+        $data = $request->only(['content', 'imgs', 'contact']);
+        $res = $this->model->create($data);
+        if(!$res) return $this->error(['msg' => '创建失败']);
+        return $this->success();
+    }
+}

+ 2 - 0
app/Http/Controllers/Api/mini/OrderController.php

xqd xqd
@@ -54,6 +54,7 @@ class OrderController extends BaseController
             $data = $this->getNameSpecRent($device);
             OrderDevice::create(array_merge([
                 'order_id' => $order->id,
+                'project_id' => $order->project_id,
                 'quantity' => $device['quantity'],
                 'price' => $price,
                 'start_date' => $device['start_date'],
@@ -135,6 +136,7 @@ class OrderController extends BaseController
             OrderDevice::create([
                 'name' => $device['name'],
                 'order_id' => $order->id,
+                'project_id' => $order->project_id,
                 'inner_device_id' => $device['id'],
                 'start_date' => $device['start_date'],
                 'end_date' => $device['end_date']

+ 30 - 0
app/Http/Controllers/Api/mini/UploadController.php

xqd
@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Http\Controllers\Api\mini;
+
+use Carbon\Carbon;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Log;
+
+class UploadController extends BaseController
+{
+    protected $model;
+
+    public function __construct()
+    {
+
+    }
+
+    public function upload(Request $request)
+    {
+        if($request->hasFile('file')) {
+            $item = $request->file('file');
+            $fileName = uniqid() . '.' . $item->extension();
+            $date = date("Ymd");
+            $res = $item->storeAs('upload/images' . '/' . $date, $fileName);
+            $path = url($res);
+            return $this->success(['data' => ['path' => $path]]);
+        }
+        return $this->error(['msg' => '上传失败']);
+    }
+}

+ 20 - 0
app/Http/Controllers/Api/mini/UserController.php

xqd xqd
@@ -4,6 +4,8 @@ namespace App\Http\Controllers\Api\mini;
 
 use App\Models\User;
 use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Hash;
 
 class UserController extends BaseController
 {
@@ -42,4 +44,22 @@ class UserController extends BaseController
 
         return $this->success(['msg' => '操作成功', 'data' => $items]);
     }
+
+    public function update(Request $request)
+    {
+        $user = Auth::guard('mini')->user();
+        $user->update($request->only('avatar', 'name', 'phone'));
+        return $this->success(['data' => $user]);
+    }
+
+    public function changePassword(Request $request)
+    {
+        $user = Auth::guard('mini')->user();
+        if(!$user) return $this->error(['msg' => '找不到用户']);
+        if(Hash::check($request->input('new'), $user->password)) {
+            $res = $user->update(['password' => bcrypt($user->password)]);
+            if($res) return $this->success();
+        }
+        return $this->error(['msg' => '原密码错误']);
+    }
 }

+ 1 - 1
app/Http/Middleware/AuthMini.php

xqd
@@ -18,7 +18,7 @@ class AuthMini
      */
     public function handle($request, Closure $next)
     {
-        if(in_array($request->path(), ['api/mini/login', 'api/mini/reset', 'api/mini/test', 'api/mini/loginByWechat'])) return $next($request);
+        if(in_array($request->path(), ['api/mini/login', 'api/mini/reset', 'api/mini/test', 'api/mini/loginByWechat', 'api/mini/uploadFile'])) return $next($request);
         $token = $request->header('X-Token');
         if($token) {
             $user = User::where('token', $token)->first();

+ 8 - 0
app/Models/Feedback.php

xqd
@@ -0,0 +1,8 @@
+<?php
+
+namespace App\Models;
+
+class Feedback extends BaseModel
+{
+    //
+}

+ 2 - 2
config/filesystems.php

xqd xqd
@@ -13,7 +13,7 @@ return [
     |
     */
 
-    'default' => 'local',
+    'default' => 'public',
 
     /*
     |--------------------------------------------------------------------------
@@ -55,7 +55,7 @@ return [
 
         'public' => [
             'driver' => 'local',
-            'root' => storage_path('app/public'),
+            'root' => public_path(),
             'url' => env('APP_URL').'/storage',
             'visibility' => 'public',
         ],

+ 30 - 0
database/migrations/2020_12_21_004940_add_project_id_to_order_devices.php

xqd
@@ -0,0 +1,30 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class AddProjectIdToOrderDevices extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('order_devices', function (Blueprint $table) {
+            $table->unsignedInteger('project_id')->after('order_id')->nullable();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        //
+    }
+}

+ 34 - 0
database/migrations/2020_12_21_065100_create_feedback_table.php

xqd
@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class CreateFeedbackTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('feedback', function (Blueprint $table) {
+            $table->increments('id');
+            $table->text('content')->nullable();
+            $table->text('imgs')->nullable();
+            $table->string('contact', 50)->nullable();
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('feedback');
+    }
+}

+ 1 - 0
database/seeds/InnerOrderSeeder.php

xqd
@@ -159,6 +159,7 @@ class InnerOrderSeeder extends Seeder
             $device = \App\Models\InnerDevice::where('status', $free_id)->where('id', '>', 10)->first();
             \App\Models\OrderDevice::create([
                 'order_id' => $order['id'],
+                'project_id' => $order['project_id'],
                 'inner_device_id' => $device->id,
                 'start_date' => $start_date,
                 'end_date' => $end_date

+ 1 - 0
database/seeds/MiniSeeder.php

xqd
@@ -230,6 +230,7 @@ class MiniSeeder extends Seeder
             \App\Models\OrderDevice::create([
                 'name' => '设备' . $j,
                 'order_id' => $order['id'],
+                'project_id' => $order['project_id'],
                 'device_id' => $spec->device_id,
                 'device_name_id' => $spec->device_name_id,
                 'spec_id' => $spec->id,

+ 15 - 6
mini/app.js

xqd xqd xqd
@@ -1,4 +1,6 @@
 //app.js
+import http from './utils/http'
+
 App({
   onLaunch: function () {
 
@@ -46,12 +48,6 @@ App({
   globalData: {
     userInfo: null
   },
-  loginCallback: function (data) {
-    this.updateUserInfo(data)
-    wx.switchTab({
-      url: '/pages/index/index',
-    })
-  },
   updateUserInfo: function (info) {
     this.globalData.userInfo = info
     wx.setStorageSync('sg-userinfo', info)
@@ -85,5 +81,18 @@ App({
       rent_type_ids: ['']
     }
     wx.setStorageSync('sg-data-filters', data)
+  },
+  navigate(e) {
+    var url = e.currentTarget.dataset.url
+    wx.navigateTo({
+      url: url,
+    })
+  },
+  updateInput(that, e) {
+    var name = e.currentTarget.dataset.name
+    var val = e.detail.value
+    that.setData({
+      [name]: val
+    })
   }
 })

+ 8 - 2
mini/app.json

xqd xqd xqd
@@ -1,5 +1,11 @@
 {
   "pages": [
+    "pages/about/index",
+    "pages/feedback/index",
+    "pages/account/index",
+    "pages/password/index",
+    "pages/bind/index",
+    "pages/user/index",
     "pages/data/index",
     "pages/filter-data/index",
     "pages/index/index",
@@ -18,7 +24,6 @@
     "pages/create-order-inner/index",
     "pages/order-detail-inner/index",
     "pages/order-detail/index",
-    "pages/account/index",
     "pages/create-project-role/index",
     "pages/project-user/index",
     "pages/create-project/index",
@@ -72,6 +77,7 @@
     "van-collapse": "@vant/weapp/collapse/index",
     "van-collapse-item": "@vant/weapp/collapse-item/index",
     "van-picker": "@vant/weapp/picker/index",
-    "ec-canvas": "/ec-canvas/ec-canvas"
+    "ec-canvas": "/ec-canvas/ec-canvas",
+    "van-uploader": "@vant/weapp/uploader/index"
   }
 }

+ 5 - 1
mini/app.wxss

xqd xqd xqd
@@ -37,7 +37,7 @@
   padding: 50rpx;
   box-sizing: border-box;
 }
-.sg-center {
+.sg-text-center {
   text-align: center;
 }
 .sg-primary-color {
@@ -128,6 +128,7 @@
 }
 .sg-avatar {
   width: 80rpx;
+  height: 80rpx !important;
   border-radius: 50%;
   border: 2px solid white;
 }
@@ -178,6 +179,9 @@
   background: #ee0a24;
   color: white;
 }
+.sg-deep-blue-bg {
+  background: #4281F0;
+}
 .sg-green {
   color: #07c160;
 }

+ 66 - 0
mini/pages/about/index.js

xqd
@@ -0,0 +1,66 @@
+// pages/about/index.js
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: function (options) {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload: function () {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh: function () {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom: function () {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function () {
+
+  }
+})

+ 3 - 0
mini/pages/about/index.json

xqd
@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 2 - 0
mini/pages/about/index.wxml

xqd
@@ -0,0 +1,2 @@
+<!--pages/about/index.wxml-->
+<text>pages/about/index.wxml</text>

+ 1 - 0
mini/pages/about/index.wxss

xqd
@@ -0,0 +1 @@
+/* pages/about/index.wxss */

+ 13 - 2
mini/pages/account/index.js

xqd
@@ -1,19 +1,30 @@
 // pages/account/index.js
-var app = getApp()
+import http from '../../utils/http'
+import api from '../../utils/api'
+const app = getApp()
 Page({
 
   /**
    * 页面的初始数据
    */
   data: {
-
+    userInfo: null
   },
 
   /**
    * 生命周期函数--监听页面加载
    */
   onLoad: function (options) {
+    if (app.globalData.userInfo) {
+      this.setData({
+        userInfo: app.globalData.userInfo,
+        hasUserInfo: true
+      })
+    }
+  },
 
+  navigate: function(e) {
+    app.navigate(e)
   },
 
   logout() {

+ 36 - 2
mini/pages/account/index.wxml

xqd
@@ -1,4 +1,38 @@
 <!--pages/account/index.wxml-->
 <view class="sg-container">
-  <van-button type="primary" block bind:click="logout">退出登录</van-button>
-</view>
+  <view class="sg-index-bg sg-pad">
+    <view class="sg-flex sg-align-center">
+      <image class="sg-avatar sg-margin-right" src="{{ userInfo.avatar }}" mode="widthFix"></image>
+      <view class="sg-white">
+        <view class="sg-font-lg">{{ userInfo ? userInfo.name : '请登录' }}</view>
+        <view class="sg-font-small" bindtap="navigate" data-url="{{userInfo ? '/pages/user/index' : '/pages/login/index'}}">{{ userInfo ? '修改用户信息' : '点击按钮进行登录操作' }}</view>
+      </view>
+    </view>
+    <view wx:if="{{!userInfo}}" class="sg-pad-sm sg-font-small sg-margin-tb sg-white sg-deep-blue-bg sg-flex sg-align-center sg-space-between" bindtap="navigate" data-url="/pages/login/index">
+      <text>你还未登录,暂时不能查看项目相关数据</text>
+      <van-icon name="arrow" class="sg-icon"></van-icon>
+    </view>
+  </view>
+  <view class="sg-white-bg">
+    <view class="sg-flex sg-align-center sg-space-between sg-pad sg-bottom-border" bindtap="navigate" data-url="/pages/bind/index">
+      <view>绑定微信</view>
+      <van-icon class="sg-icon" name="arrow"></van-icon>
+    </view>
+    <view class="sg-flex sg-align-center sg-space-between sg-pad sg-bottom-border" bindtap="navigate" data-url="/pages/password/index">
+      <view>修改密码</view>
+      <van-icon class="sg-icon" name="arrow"></van-icon>
+    </view>
+    <view class="sg-flex sg-align-center sg-space-between sg-pad sg-bottom-border" bindtap="navigate" data-url="/pages/feedback/index">
+      <view>用户反馈</view>
+      <van-icon class="sg-icon" name="arrow"></van-icon>
+    </view>
+    <view class="sg-flex sg-align-center sg-space-between sg-pad sg-bottom-border" bindtap="navigate" data-url="/pages/about/index">
+      <view>关于我们</view>
+      <van-icon class="sg-icon" name="arrow"></van-icon>
+    </view>
+    <view class="sg-flex sg-align-center sg-space-between sg-pad sg-bottom-border" bindtap="logout">
+      <view>退出登录</view>
+      <van-icon class="sg-icon" name="arrow"></van-icon>
+    </view>
+  </view>
+</view>

+ 74 - 0
mini/pages/bind/index.js

xqd
@@ -0,0 +1,74 @@
+// pages/bind/index.js
+import util from '../../utils/util'
+
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: function (options) {
+
+  },
+
+  getUserInfo: function(e) {
+    util.wechatLogin(e, false, function(res) {
+      util.success('绑定成功')
+    }, true)
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload: function () {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh: function () {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom: function () {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function () {
+
+  }
+})

+ 3 - 0
mini/pages/bind/index.json

xqd
@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 6 - 0
mini/pages/bind/index.wxml

xqd
@@ -0,0 +1,6 @@
+<!--pages/bind/index.wxml-->
+<view class="sg-container">
+  <view class="sg-pad">
+    <van-button type="primary" block open-type="getUserInfo" bind:getuserinfo="getUserInfo">绑定微信</van-button>
+  </view>
+</view>

+ 1 - 0
mini/pages/bind/index.wxss

xqd
@@ -0,0 +1 @@
+/* pages/bind/index.wxss */

+ 9 - 6
mini/pages/data/index.js

xqd xqd
@@ -136,11 +136,16 @@ Page({
   getData() {
     var ids = this.data.project_ids
     if (ids.length <= 0) return false
-    var data = Object.assign({}, {
+    var filter = this.data.filter
+    var data = {
       project_ids: ids,
       date: this.data.date,
-      type: this.data.dateIndex == 0 ? 'year' : 'month'
-    }, this.data.filter)
+      type: this.data.dateIndex == 0 ? 'year' : 'month',
+      device_ids: filter.device_ids,
+      device_name_ids: filter.device_name_ids,
+      spec_ids: filter.spec_ids,
+      rent_type_ids: filter.rent_type_ids
+    }
     var that = this
     api.getByName(this, 'data/getStat', 'data', data, function(res) {
       that.updateChart()
@@ -248,13 +253,11 @@ Page({
    */
   onShow: function () {
     this.getTabBar().init();
-    this.setData({
-      project_id: ''
-    })
     var filter = wx.getStorageSync('sg-data-filters')
     this.setData({
       filter: filter
     })
+    this.getData()
   },
 
   navigate: function(e) {

+ 3 - 3
mini/pages/data/index.wxml

xqd xqd
@@ -6,7 +6,7 @@
       <view class="sg-flex-grow">{{project_names ? project_names : '选择查看项目'}}</view>
       <van-icon name="arrow-down"></van-icon>
     </view>
-    <van-icon name="search" class="sg-icon sg-white sg-pad-sm" style="padding-right: 0" bindtap="search"></van-icon>
+    <van-icon name="search" class="sg-icon sg-white sg-pad-sm" style="padding-right: 0" bindtap="getData"></van-icon>
   </view>
   <van-popup show="{{ projectShow }}" position="bottom" custom-style="height: 100%;">
     <view>
@@ -25,11 +25,11 @@
   </van-popup>
   <view class="sg-white sg-bold sg-flex sg-align-center sg-space-around">
     <view class="sg-center">
-      <view class="sg-font-lg">{{data.total_project}}</view>
+      <view class="sg-font-lg">{{data.total_project ? data.total_project : 0}}</view>
       <view class="sg-font-xs sg-pad-tb-sm">项目累计消费</view>
     </view>
     <view class="sg-center">
-      <view class="sg-font-lg">{{data.total_month}}</view>
+      <view class="sg-font-lg">{{data.total_month ? data.total_month : 0}}</view>
       <view class="sg-font-xs sg-pad-tb-sm">当月累计消费</view>
     </view>
     <view class="sg-center">

+ 146 - 0
mini/pages/feedback/index.js

xqd
@@ -0,0 +1,146 @@
+// pages/feedback/index.js
+import http from '../../utils/http'
+import util from '../../utils/util'
+import api from '../../utils/api'
+
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+    files: [],
+    content: '',
+    contact: '',
+    imgs: []
+  },
+  
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: function (options) {
+
+  },
+
+  afterRead: function(e) {
+    const { file } = e.detail;
+    var files = this.data.files
+    if(files.length >= 4) {
+      util.error('最多上传4张图片')
+      return false
+    }
+    file.path = ''
+    files.push(file)
+    this.setData({
+      files
+    })
+  },
+
+  delete: function(e) {
+    var files = this.data.files
+    var index = e.detail.index
+    files.splice(index, 1)
+    this.setData({files})
+  },
+
+  updateInput(e) {
+    getApp().updateInput(this, e)
+  },
+
+  save: function() {
+    if(!this.data.content) {
+      util.error('反馈意见必填')
+      return false
+    }
+    if(!this.data.contact) {
+      util.error('联系方式必填')
+      return false
+    }
+    var files = this.data.files
+    var that = this
+    if(files.length != this.data.imgs.length) {
+      for(var i = 0; i < files.length; ++i) {
+        util.uploadFile(files[i].url, function(res) {
+          // console.log(res)
+          var imgs = that.data.imgs
+          imgs.push(res.data.path)
+          that.setData({imgs})
+          that.updateInfo()
+        })
+      }
+    } else {
+      that.updateInfo()
+    }
+  },
+
+  updateInfo: function() {
+    var files = this.data.files
+    var imgs = this.data.imgs
+    if(imgs.length != files.length) return false;
+    http({
+      url: 'feedback/create',
+      data: {
+        content: this.data.content,
+        contact: this.data.contact,
+        imgs: imgs.join(',')
+      },
+      success: function (res) {
+        if (res.code == 0) {
+          util.success('操作成功')
+        } else {
+          util.error('操作失败')
+        }
+      }
+    })
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload: function () {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh: function () {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom: function () {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function () {
+
+  }
+})

+ 3 - 0
mini/pages/feedback/index.json

xqd
@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 21 - 0
mini/pages/feedback/index.wxml

xqd
@@ -0,0 +1,21 @@
+<!--pages/feedback/index.wxml-->
+<view class="sg-container">
+  <view class="sg-white-bg sg-pad">
+    <view class="sg-pad-tb sg-bottom-border">
+      <view class="sg-pad-tb-sm sg-bottom-border">反馈意见</view>
+      <textarea value="{{content}}" placeholder="请填写10个字以上的问题描述以便我们提供更好的帮助" bindinput="updateInput" data-name="content" class="sg-pad-tb-sm"></textarea>
+      <view class="sg-text-right sg-gray-color">{{content.length ? content.length : 0}}/200</view>
+    </view>
+    <view class="sg-pad-tb sg-bottom-border">
+      <view class="sg-pad-tb-sm">相关截图(选填)({{files.length ? files.length : 0}}/4)</view>
+      <view class="sg-pad-tb-sm">
+        <van-uploader file-list="{{ files }}" bind:after-read="afterRead" bind:delete="delete"/>
+      </view>
+    </view>
+    <view class="sg-pad-tb sg-flex sg-align-center sg-space-between">
+      <view class="sg-margin-right">联系方式</view>
+      <input value="{{contact}}" bindinput="updateInput" data-name="contact" placeholder="邮箱/手机号" class="sg-flex-grow"/>
+    </view>
+  </view>
+  <view class="sg-fix-bottom sg-pad sg-text-center sg-white sg-index-bg" bindtap="save">提交</view>
+</view>

+ 1 - 0
mini/pages/feedback/index.wxss

xqd
@@ -0,0 +1 @@
+/* pages/feedback/index.wxss */

+ 1 - 31
mini/pages/login/index.js

xqd
@@ -26,37 +26,7 @@ Page({
   },
 
   getUserInfo: function(e) {
-    var that = this
-    if(e.detail.errMsg == 'getUserInfo:ok') {
-      // wx.checkSession({
-      //   success () {
-      //     that.wechatLogin(e.detail)
-      //   },
-      //   fail () {
-          // session_key 已经失效,需要重新执行登录流程
-          wx.login({
-            success (res) {
-              if(res.code) {
-                that.wechatLogin(Object.assign({}, e.detail, {code: res.code}))
-              }
-            }
-          }) //重新登录
-        // }
-      // })
-    }
-  },
-
-  wechatLogin: function(data) {
-    http({
-      url: 'loginByWechat',
-      data: data,
-      loadTitle: '登录中',
-      success: function(res) {
-        if(res.code == 0) {
-          app.loginCallback(res.data)
-        }
-      }
-    })
+    util.wechatLogin(e, redirect)
   },
 
   switchType: function(e) {

+ 111 - 0
mini/pages/password/index.js

xqd
@@ -0,0 +1,111 @@
+// pages/password/index.js
+import http from '../../utils/http'
+import api from '../../utils/api'
+import util from '../../utils/util'
+const app = getApp()
+
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+    old: '',
+    new: '',
+    new_confirm: ''
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: function (options) {
+
+  },
+
+  updateInput: function(e) {
+    app.updateInput(this, e)
+  },
+
+  save: function(e) {
+    if(!this.data.old) {
+      util.error('旧密码必填')
+      return false
+    }
+    if(!this.data.new) {
+      util.error('新密码必填')
+      return false
+    }
+    if(this.data.new.length < 6) {
+      util.error('密码至少6位')
+      return false
+    }
+    if(!util.checkPass(this.data.new)) {
+      util.error('密码必须同时包含数字和字母')
+      return false
+    }
+    if(this.data.new != this.data.new_confirm) {
+      util.error('两次输入的密码不一致')
+      return false
+    }
+    http({
+      url: 'users/changePassword',
+      data: this.data,
+      success: function(res) {
+        if(res.code == 0) {
+          util.success('更改成功')
+        } else {
+          util.error(res.msg)
+        }
+      }
+    })
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload: function () {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh: function () {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom: function () {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function () {
+
+  }
+})

+ 3 - 0
mini/pages/password/index.json

xqd
@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 19 - 0
mini/pages/password/index.wxml

xqd
@@ -0,0 +1,19 @@
+<!--pages/password/index.wxml-->
+<view class="sg-container">
+  <view class="sg-white-bg">
+    <view class="sg-flex sg-align-center sg-space-between sg-pad sg-bottom-border">
+      <view>旧密码</view>
+      <input class="sg-input sg-text-right" value="{{old}}" bindinput="updateInput" data-name="old" placeholder="请输入旧密码" password="{{true}}"></input>
+    </view>
+    <view class="sg-flex sg-align-center sg-space-between sg-pad sg-bottom-border">
+      <view>新密码</view>
+      <input class="sg-input sg-text-right" value="{{new}}" bindinput="updateInput" data-name="new" placeholder="请输入新密码" password="{{true}}"></input>
+    </view>
+    <view class="sg-flex sg-align-center sg-space-between sg-pad sg-bottom-border">
+      <view>重复新密码</view>
+      <input class="sg-input sg-text-right" value="{{new_confirm}}" bindinput="updateInput" data-name="new_confirm" placeholder="请重复新密码" password="{{true}}"></input>
+    </view>
+  </view>
+  <view class="sg-pad sg-gray-color sg-font-small">密码长度6~32位,须包含数字、字母</view>
+  <view class="sg-fix-bottom sg-pad sg-text-center sg-white-bg" bindtap="save">保存</view>
+</view>

+ 1 - 0
mini/pages/password/index.wxss

xqd
@@ -0,0 +1 @@
+/* pages/password/index.wxss */

+ 145 - 0
mini/pages/user/index.js

xqd
@@ -0,0 +1,145 @@
+// pages/user/index.js
+import http from '../../utils/http'
+import api from '../../utils/api'
+import util from '../../utils/util'
+const app = getApp()
+
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+    userInfo: null,
+    avatar: '',
+    name: '',
+    phone: ''
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: function (options) {
+    if(app.globalData.userInfo) {
+      var user = app.globalData.userInfo
+      this.setData({
+        userInfo: user,
+        avatar: user.avatar,
+        name: user.name,
+        phone: user.phone
+      })
+    }
+  },
+
+  chooseImage() {
+    var that = this
+    wx.chooseImage({
+      count: 1,
+      sizeType: ['original', 'compressed'],
+      sourceType: ['album', 'camera'],
+      success (res) {
+        const paths = res.tempFilePaths
+        if(paths.length > 0) {
+          that.setData({
+            avatar: paths[0]
+          })
+        }
+      }
+    })
+  },
+
+  updateInput: function(e) {
+    app.updateInput(this, e)
+  },
+
+  updateInfo: function() {
+    http({
+      url: 'users/update',
+      data: {
+        avatar: this.data.avatar,
+        name: this.data.name,
+        phone: this.data.phone
+      },
+      success: function (res) {
+        if (res.code == 0) {
+          util.success('操作成功')
+          app.updateUserInfo(res.data)
+        } else {
+          util.error('操作失败')
+        }
+      }
+    })
+  },
+
+  save: function() {
+    if(!util.checkMobile(this.data.phone)) {
+      util.error('手机号错误')
+      return false
+    }
+    if(!this.data.name) {
+      util.error('姓名必填')
+      return false
+    }
+    if(this.data.userInfo.avatar != this.data.avatar) {
+      var that = this;
+      util.uploadFile(this.data.avatar, function(res) {
+        that.setData({
+          avatar: res.path
+        })
+        that.updateInfo()
+      })
+    } else {
+      that.updateInfo()
+    }
+    
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload: function () {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh: function () {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom: function () {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function () {
+
+  }
+})

+ 4 - 0
mini/pages/user/index.json

xqd
@@ -0,0 +1,4 @@
+{
+  "navigationBarTitleText": "个人信息",
+  "usingComponents": {}
+}

+ 18 - 0
mini/pages/user/index.wxml

xqd
@@ -0,0 +1,18 @@
+<!--pages/user/index.wxml-->
+<view class="sg-container">
+  <view class="sg-white-bg">
+    <view class="sg-flex sg-align-center sg-space-between sg-pad sg-bottom-border" bindtap="chooseImage">
+      <view class="sg-gray-color">更换头像</view>
+      <image class="sg-avatar" src="{{ avatar }}" mode="widthFix"></image>
+    </view>
+    <view class="sg-flex sg-align-center sg-space-between sg-pad sg-bottom-border">
+      <view class="sg-gray-color">姓名</view>
+      <input class="sg-input sg-text-right" value="{{name}}" bindinput="updateInput" data-name="name"></input>
+    </view>
+    <view class="sg-flex sg-align-center sg-space-between sg-pad sg-bottom-border">
+      <view class="sg-gray-color">手机号码</view>
+      <input class="sg-input sg-text-right" value="{{phone}}" bindinput="updateInput" data-name="phone"></input>
+    </view>
+  </view>
+  <view class="sg-fix-bottom sg-pad sg-text-center sg-white-bg" bindtap="save">保存</view>
+</view>

+ 1 - 0
mini/pages/user/index.wxss

xqd
@@ -0,0 +1 @@
+/* pages/user/index.wxss */

+ 4 - 0
mini/utils/env.js

xqd
@@ -0,0 +1,4 @@
+const isTest = true;
+const baseUrl = isTest ? 'http://app.rt/api/mini/' : 'http://t18.9026.com/api/mini/';
+
+export default baseUrl

+ 1 - 2
mini/utils/http.js

xqd
@@ -1,5 +1,4 @@
-const isTest = true;
-const baseUrl = isTest ? 'http://app.rt/api/mini/' : 'http://t18.9026.com/api/mini/';
+import baseUrl from '../utils/env'
 
 const http = (data) => {
   var data = Object.assign({}, {

+ 70 - 2
mini/utils/util.js

xqd xqd
@@ -1,5 +1,8 @@
+import http from '../utils/http'
+import baseUrl from '../utils/env'
+
 const formatDate = (date, fmt = 'yyyy-MM-dd') => {
-  
+
   var o = {
     "M+": date.getMonth() + 1, //月份
     "d+": date.getDate(), //日
@@ -44,10 +47,75 @@ const firstCase = str => {
   return str.charAt(0).toUpperCase() + str.slice(1)
 }
 
+const uploadFile = (path, cb = null) => {
+  wx.uploadFile({
+    filePath: path,
+    name: 'file',
+    url: baseUrl + 'uploadFile',
+    success: function (res) {
+      var data = JSON.parse(res.data)
+      if (data.code == 0) {
+        typeof cb === "function" && cb(data)
+      } else {
+        error('上传文件失败')
+      }
+    }
+  })
+}
+
+const callLogin = (data, redirect, cb) => {
+  http({
+    url: 'loginByWechat',
+    data: data,
+    loadTitle: redirect ? '登录中' : '',
+    success: function (res) {
+      if (res.code == 0) {
+        getApp().updateUserInfo(res.data)
+        if (redirect) {
+          wx.switchTab({
+            url: '/pages/index/index',
+          })
+        }
+        typeof cb === "function" && cb(res.data)
+      }
+    }
+  })
+}
+
+const wechatLogin = (e, redirect = false, cb = null, bind = false) => {
+  if (e.detail.errMsg == 'getUserInfo:ok') {
+    // wx.checkSession({
+    //   success () {
+    //     that.wechatLogin(e.detail)
+    //   },
+    //   fail () {
+    // session_key 已经失效,需要重新执行登录流程
+    wx.login({
+      success(res) {
+        if (res.code) {
+          callLogin(Object.assign({}, e.detail, {
+            code: res.code,
+            bind: bind
+          }), redirect, cb)
+        }
+      }
+    }) //重新登录
+    // }
+    // })
+  }
+}
+
+const checkPass = (str) => {
+  return /^(\d+[a-zA-Z]+|[a-zA-Z]+\d+)([0-9a-zA-Z]*)$/.test(str)
+}
+
 module.exports = {
   formatDate,
   checkMobile: checkMobile,
   error,
   success,
-  firstCase
+  firstCase,
+  uploadFile,
+  wechatLogin,
+  checkPass
 }

binární
public/images/logo.png


+ 7 - 0
routes/api.php

xqd
@@ -79,4 +79,11 @@ $api->version('v1', ['namespace' => 'App\Http\Controllers\Api\mini', 'prefix' =>
     $api->any('data/getStat', 'DataController@getStat');
 
     $api->any('device-names/get', 'DeviceNameController@get');
+
+    $api->any('uploadFile', 'UploadController@upload');
+
+    $api->any('users/update', 'UserController@update');
+    $api->any('users/changePassword', 'UserController@changePassword');
+
+    $api->any('feedback/create', 'FeedbackController@create');
 });