Ver código fonte

项目管理+人员管理

李浩杰 4 anos atrás
pai
commit
2285b976e5

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

xqd xqd
@@ -2,6 +2,7 @@
 
 namespace App\Http\Controllers\Admin;
 
+use App\Models\ProjectUser;
 use App\Models\User;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Auth;
@@ -11,7 +12,7 @@ class TestController extends Controller
 {
     public function index(Request $request)
     {
-        dd(User::first()->projects->implode('name', ','));
+        dd(ProjectUser::first()->projectRole);
     	return view('admin.test.index');
     }
 }

+ 34 - 1
app/Http/Controllers/Api/mini/ProjectController.php

xqd xqd xqd
@@ -7,6 +7,7 @@ use App\Models\ProjectRole;
 use App\Models\ProjectUser;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Auth;
+use Illuminate\Database\Eloquent\Builder;
 
 class ProjectController extends BaseController
 {
@@ -34,6 +35,17 @@ class ProjectController extends BaseController
         return $this->success('操作成功');
     }
 
+    public function updateUser(Request $request)
+    {
+        $res = ProjectUser::where('id', '=', $request->input('id'))->update([
+            'project_id' => $request->input('project_id'),
+            'user_id' => $request->input('user_id'),
+            'project_role_id' => $request->input('project_role_id')
+        ]);
+        if(!$res) return $this->error('操作失败');
+        return $this->success('操作成功');
+    }
+
     public function create(Request $request)
     {
         if(empty($request->input('name'))) return $this->error(['msg' => '项目名称不能为空']);
@@ -75,6 +87,27 @@ class ProjectController extends BaseController
     {
         $project = $this->model->find($request->input('id'));
         $project->role = $project->getUserProjectRole();
-        return $this->success(['msg' => '创建成功', 'data' => $project]);
+        return $this->success(['msg' => '操作成功', 'data' => $project]);
+    }
+
+    public function getUsers(Request $request)
+    {
+//        $list = ProjectUser::where('project_id', $request->input('id'))->join('users', 'project_users.user_id', '=', 'users.id')->join('project_roles', 'project_users.project_role_id', '=', 'project_roles.id')->select('project_users.*', 'users.avatar', 'users.name as user', 'project_roles.name as role_name');
+        $list = ProjectUser::where('project_id', $request->input('id'))->with(['projectRole', 'user']);
+        if($request->has('keyword')) {
+            $keyword = '%' . $request->input('keyword') . '%';
+            $list = $list->whereHas('user', function (Builder $query) use($keyword) {
+                $query->where('phone', 'like', $keyword)->orWhere('name', 'like', $keyword);
+            });
+        }
+        $list = $list->get();
+        return $this->success(['msg' => '操作成功', 'data' => $list]);
+    }
+
+    public function deleteUser(Request $request)
+    {
+        $res = ProjectUser::where('id', '=', $request->input('id'))->delete();
+        if($res) return $this->success(['msg' => '操作成功']);
+        return $this->error();
     }
 }

+ 23 - 0
app/Http/Controllers/Api/mini/ProjectUserController.php

xqd
@@ -0,0 +1,23 @@
+<?php
+
+namespace App\Http\Controllers\Api\mini;
+
+use App\Models\ProjectRole;
+use App\Models\ProjectUser;
+use Illuminate\Http\Request;
+
+class ProjectUserController extends BaseController
+{
+    protected $model;
+
+    public function __construct()
+    {
+        $this->model = new ProjectUser();
+    }
+
+    public function detail(Request $request)
+    {
+        $item = $this->model->find($request->input('id'));
+        return $this->success(['data' => $item]);
+    }
+}

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

xqd
@@ -24,6 +24,13 @@ class UserController extends BaseController
             }
         }
 
+        $select_items = collect(['id']);
+        foreach($select_items as $tmp_item) {
+            if($request->has($tmp_item) && !empty($request->input($tmp_item))) {
+                $items = $items->where($tmp_item, '=', $request->input($tmp_item));
+            }
+        }
+
         $items = $items->limit(1)->get();
 
         foreach($items as $item) {

+ 9 - 1
app/Models/ProjectUser.php

xqd
@@ -4,5 +4,13 @@ namespace App\Models;
 
 class ProjectUser extends BaseModel
 {
-    //
+    public function projectRole()
+    {
+        return $this->belongsTo('App\Models\ProjectRole', 'project_role_id');
+    }
+
+    public function user()
+    {
+        return $this->belongsTo('App\Models\User', 'user_id');
+    }
 }

+ 4 - 2
mini/app.json

xqd xqd
@@ -1,8 +1,9 @@
 {
   "pages": [
-    "pages/project/index",
     "pages/index/index",
     "pages/create-project-role/index",
+    "pages/project-user/index",
+    "pages/project/index",
     "pages/create-project/index",
     "pages/login/index",
     "pages/logs/logs"
@@ -20,6 +21,7 @@
     "van-divider": "@vant/weapp/divider/index",
     "navbar": "/components/navbar",
     "van-icon": "@vant/weapp/icon/index",
-    "van-search": "@vant/weapp/search/index"
+    "van-search": "@vant/weapp/search/index",
+    "van-dialog": "@vant/weapp/dialog/index"
   }
 }

+ 8 - 1
mini/app.wxss

xqd xqd
@@ -2,7 +2,7 @@
 .sg-container {
   min-height: 100vh;
   padding-bottom: 100rpx;
-  background: #f7f7f7;
+  background: #f0f0f0;
 }
 .sg-bg {
   width: 100%;
@@ -101,4 +101,11 @@
   width: 130rpx;
   margin-right: 30rpx;
   border-radius: 50%;
+}
+.sg-red {
+  color: #ee0a24;
+}
+.sg-red-bg {
+  background: #ee0a24;
+  color: white;
 }

+ 85 - 11
mini/pages/create-project-role/index.js

xqd xqd xqd xqd xqd xqd
@@ -12,7 +12,12 @@ Page({
     projectIndex: -1,
     users: [],
     userIndex: -1,
-    roleIndex: -1
+    roleIndex: -1,
+    id: -1,
+    // create/edit
+    type: 'create',
+    project_user: null,
+    roles: []
   },
 
   /**
@@ -21,6 +26,62 @@ Page({
   onLoad: function (options) {
     this.getProjects()
     this.getRoles()
+    if(options.id) {
+      this.setData({
+        id: options.id,
+        type: 'edit'
+      })
+      this.getProjectUser()
+      wx.setNavigationBarTitle({
+        title: '修改权限'
+      })
+    }
+  },
+
+  updateIndex: function() {
+    var project_user = this.data.project_user
+    if(!project_user) return false;
+    var projects = this.data.projects
+    if(projects.length > 0) {
+      for(var i = 0; i < projects.length; ++i) {
+        if(projects[i].id == project_user.project_id) {
+          this.setData({
+            projectIndex: i
+          })
+          break;
+        }
+      }
+    }
+    var roles = this.data.roles
+    if(roles.length > 0) {
+      for(var i = 0; i < roles.length; ++i) {
+        if(roles[i].id == project_user.project_role_id) {
+          this.setData({
+            roleIndex: i
+          })
+          break;
+        }
+      }
+    }
+  },
+
+  getProjectUser() {
+    var that = this
+    http({
+      url: 'project-users/detail',
+      data: {
+        id: this.data.id
+      },
+      success: function(res) {
+        if(res.code == 0) {
+          that.setData({
+            project_user: res.data
+          })
+          that.updateIndex()
+          that.search()
+        }
+      }
+    })
   },
 
   submit() {
@@ -39,13 +100,18 @@ Page({
     var user_id = this.data.users[0].id;
     var project_id = this.data.projects[this.data.projectIndex].id;
     var role_id = this.data.roles[this.data.roleIndex].id;
+    var url = this.data.type == 'create' ? 'projects/addUser' : 'projects/updateUser'
+    var data = {
+      user_id: user_id,
+      project_id: project_id,
+      project_role_id: role_id
+    }
+    if(this.data.type == 'edit') {
+      data.id = this.data.project_user.id
+    }
     http({
-      url: 'projects/addUser',
-      data: {
-        user_id: user_id,
-        project_id: project_id,
-        project_role_id: role_id
-      },
+      url: url,
+      data: data,
       success: function(res) {
         if(res.code == 0) {
           util.success('操作成功')
@@ -69,12 +135,18 @@ Page({
 
   search: function() {
     var that = this
-    if(!this.data.keyword) return false;
+    if(!this.data.keyword && this.data.type == 'create') return false;
+    var data = {
+      keyword: this.data.keyword
+    }
+    if(this.type == 'edit') {
+      data = {
+        id: this.data.project_user.user_id
+      }
+    }
     http({
       url: 'users/search',
-      data: {
-        keyword: this.data.keyword
-      },
+      data: data,
       success: function(res) {
         if(res.code == 0) {
           if(res.data.length <= 0) {
@@ -105,6 +177,7 @@ Page({
           that.setData({
             roles: res.data
           })
+          if(that.data.type == 'edit') that.updateIndex()
         }
       }
     })
@@ -120,6 +193,7 @@ Page({
           that.setData({
             projects: res.data
           })
+          if(that.data.type == 'edit') that.updateIndex()
         }
       }
     })

+ 2 - 2
mini/pages/create-project-role/index.wxml

xqd xqd
@@ -1,6 +1,6 @@
 <!--pages/create-project-role/index.wxml-->
 <view class="sg-container">
-  <view class="sg-search-box sg-shadow">
+  <view class="sg-search-box sg-shadow" wx:if="{{type == 'create'}}">
     <van-icon name="search" class="sg-index-color sg-search-icon" />
     <input class="sg-search-input" placeholder="搜索成员的姓名或绑定手机号" bindblur="blur" bindinput="updateInput"
       data-name="keyword" value="{{keyword}}"></input>
@@ -39,5 +39,5 @@
       </picker>
     </view>
   </view>
-  <view class="sg-bottom-submit-box sg-pad sg-center" bindtap="submit">立即添加</view>
+  <view class="sg-bottom-submit-box sg-pad sg-center" bindtap="submit">{{ type == 'create' ? '立即添加' : '保存修改' }}</view>
 </view>

+ 7 - 1
mini/pages/create-project-role/index.wxss

xqd xqd
@@ -1,12 +1,15 @@
 /* pages/create-project-role/index.wxss */
 .sg-search-box {
-  margin: 30rpx;
+  margin: 0 30rpx 30rpx 30rpx;
   display: flex;
   align-items: center;
   background: white;
   border-radius: 50rpx;
   padding: 15rpx 20rpx;
 }
+.sg-container {
+  padding-top: 30rpx;
+}
 .sg-search-icon {
   margin-right: 20rpx;
 }
@@ -18,6 +21,9 @@
   align-items: center;
   background: white;
 }
+.sg-pick-item picker {
+  flex-grow: 1;
+}
 .sg-user-item {
   background: white;
   display: flex;

+ 6 - 1
mini/pages/index/index.js

xqd
@@ -20,8 +20,13 @@ Page({
       url: '../logs/logs'
     })
   },
-  onLoad: function () {
+  onShow: function() {
+    this.setData({
+      list: []
+    })
     this.getList();
+  },
+  onLoad: function () {
     if (app.globalData.userInfo) {
       this.setData({
         userInfo: app.globalData.userInfo,

+ 1 - 1
mini/pages/index/index.wxml

xqd
@@ -5,7 +5,7 @@
       <van-icon name="edit" class="sg-icon"/>
       <view class="sg-top-name">新建项目</view>
     </view>
-    <view class="sg-top-item sg-font-xs">
+    <view class="sg-top-item sg-font-xs" bindtap="navigate" data-url="/pages/create-project-role/index">
       <van-icon name="user-o" class="sg-icon"/>
       <view class="sg-top-name">成员添加</view>
     </view>

+ 216 - 0
mini/pages/project-user/index.js

xqd
@@ -0,0 +1,216 @@
+// pages/project-user/index.js
+import http from '../../utils/http'
+import util from '../../utils/util'
+import Dialog from '../../miniprogram_npm/@vant/weapp/dialog/dialog';
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+    id: -1,
+    list: [],
+    keyword: '',
+    project: null,
+    showAction: false
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: function (options) {
+    var id = options.id ? options.id : 1
+    this.setData({
+      id: id
+    })
+  },
+
+  change: function() {
+    var user = this.getUser()
+    var id = user.id ? user.id : -1
+    wx.navigateTo({
+      url: '/pages/create-project-role/index?id=' + user.id,
+    })
+  },
+
+  getUser() {
+    var list = this.data.list
+    for(var i = 0; i < list.length; ++i) {
+      if(list[i].selected) {
+        return list[i]
+      }
+    }
+    return null
+  },
+
+  deleteUser: function() {
+    var user = this.getUser()
+    var id = user ? user.id : -1
+    var that = this
+    http({
+      url: 'projects/deleteUser',
+      data: {
+        id: id
+      },
+      success: function (res) {
+        if (res.code == 0) {
+          util.success('操作成功')
+          setTimeout(function() {
+            that.getList()
+          }, 300)
+        } else {
+          util.error('操作失败')
+        }
+      }
+    })
+  },
+
+  delete: function () {
+    var that = this
+    Dialog.confirm({
+        title: '确认删除该成员吗',
+        message: '删除该成员后,该成员将不再显示在该项目中,请再次确定是否需要删除该成员',
+      })
+      .then(() => {
+        that.deleteUser()
+      })
+      .catch(() => {
+        // on cancel
+      });
+  },
+
+  selectUser: function (e) {
+    var index = e.currentTarget.dataset.index
+    var list = this.data.list
+    for (var i = 0; i < list.length; ++i) {
+      if (i != index) list[i].selected = false
+    }
+    list[index].selected = !list[index].selected
+    this.setData({
+      list,
+      showAction: list[index].selected
+    })
+    // this.updateShowAction()
+  },
+
+  updateShowAction: function () {
+    var list = this.data.list
+    for (var i = 0; i < list.length; ++i) {
+      if (list[i].selected) {
+        this.setData({
+          showAction: true
+        })
+        return false;
+      }
+    }
+    this.setData({
+      showAction: false
+    })
+  },
+
+  getList: function () {
+    var that = this
+    http({
+      url: 'projects/getUsers',
+      data: {
+        id: this.data.id,
+        keyword: this.data.keyword
+      },
+      success: function (res) {
+        if (res.code == 0) {
+          that.setData({
+            list: res.data
+          })
+          that.updateShowAction()
+        }
+      }
+    })
+  },
+
+  getProject() {
+    var that = this
+    http({
+      url: 'projects/detail',
+      data: {
+        id: this.data.id
+      },
+      success: function (res) {
+        if (res.code == 0) {
+          that.setData({
+            project: res.data
+          })
+        }
+      }
+    })
+  },
+
+  clear: function () {
+    this.setData({
+      keyword: ''
+    })
+  },
+
+  blur: function () {
+    var that = this
+    setTimeout(function () {
+      that.getList()
+    }, 300)
+  },
+
+  updateInput: function (e) {
+    var name = e.currentTarget.dataset.name
+    this.setData({
+      [name]: e.detail.value
+    })
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow: function () {
+    this.getProject()
+    this.getList();
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload: function () {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh: function () {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom: function () {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function () {
+
+  }
+})

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

xqd
@@ -0,0 +1,4 @@
+{
+  "navigationBarTitleText": "人员管理",
+  "usingComponents": {}
+}

+ 29 - 0
mini/pages/project-user/index.wxml

xqd
@@ -0,0 +1,29 @@
+<!--pages/project-user/index.wxml-->
+<view class="sg-container">
+  <view class="sg-search-box sg-shadow">
+    <van-icon name="search" class="sg-index-color sg-search-icon" />
+    <input class="sg-search-input" placeholder="搜索成员的姓名或绑定手机号" bindblur="blur" bindinput="updateInput"
+      data-name="keyword" value="{{keyword}}"></input>
+    <van-icon name="close" class="sg-search-icon" bind:click="clear"/>
+  </view>
+  <view class="sg-user-box">
+    <view class="sg-user-list sg-margin-tb">
+      <view class="sg-user-item sg-pad sg-margin-top-sm {{ item.selected ? 'sg-selected' : '' }}" wx:for="{{list}}" wx:key="index" bindtap="selectUser" data-index="{{index}}">
+        <image class="sg-big-avatar" src="{{item.user ? item.user.avatar : ''}}" mode="widthFix"></image>
+        <view class="sg-right sg-flex-grow">
+          <view class="sg-name">{{item.user ? item.user.name : ''}}</view>
+          <view class="sg-project-role sg-gray-color sg-font-small sg-flex sg-align-center sg-space-between sg-margin-tb-sm">
+            <view class="sg-project">{{ project.name }}</view>
+            <view class="sg-role">{{ item.project_role ? item.project_role.name : '' }}</view>
+          </view>
+          <view class="sg-phone sg-gray-color sg-font-small">{{ item.user ? item.user.phone : '' }}</view>
+        </view>
+      </view>
+    </view>
+  </view>
+  <view class="sg-bottom-submit-box sg-center" wx:if="{{showAction}}">
+    <view class="sg-action sg-delete sg-red-bg sg-pad" bindtap="delete">删除成员</view>
+    <view class="sg-action sg-change sg-pad" bindtap="change">修改权限</view>
+  </view>
+  <van-dialog id="van-dialog" />
+</view>

+ 41 - 0
mini/pages/project-user/index.wxss

xqd
@@ -0,0 +1,41 @@
+/* pages/project-user/index.wxss */
+.sg-search-box {
+  margin: 0 30rpx 30rpx 30rpx;
+  display: flex;
+  align-items: center;
+  background: white;
+  border-radius: 50rpx;
+  padding: 15rpx 20rpx;
+}
+.sg-container {
+  padding-top: 30rpx;
+}
+.sg-search-icon {
+  margin-right: 20rpx;
+}
+.sg-search-box .sg-search-input {
+  flex-grow: 1;
+}
+.sg-pick-item {
+  display: flex;
+  align-items: center;
+  background: white;
+}
+.sg-user-item {
+  background: white;
+  display: flex;
+  align-items: center;
+}
+.sg-user-item .sg-name {
+  font-size: 1.1rem;
+}
+.sg-user-item.sg-selected {
+  background: lightskyblue;
+}
+.sg-bottom-submit-box {
+  display: flex;
+  align-items: center;
+}
+.sg-bottom-submit-box .sg-action {
+  width: 50%;
+}

+ 34 - 3
mini/pages/project/index.js

xqd xqd xqd
@@ -26,7 +26,33 @@ Page({
       img: 'http://t18.9026.com/mini/all-order.png',
       title: '所有订单',
       desc: '查看全部设备租赁订单'
-    },]
+    }],
+    device_use_menus: [{
+      img: 'http://t18.9026.com/mini/use-apply.png',
+      title: '调用申请',
+      desc: '机电负责人调用设备'
+    }, {
+      img: 'http://t18.9026.com/mini/rent-check.png',
+      title: '调用审核',
+      desc: '设备调用订单审核'
+    }, {
+      img: 'http://t18.9026.com/mini/error-handle.png',
+      title: '异常处理',
+      desc: '订单流程变更审核处理'
+    }, {
+      img: 'http://t18.9026.com/mini/all-order.png',
+      title: '所有订单',
+      desc: '查看全部设备调用订单'
+    }],
+    device_depot_menus: [{
+      img: 'http://t18.9026.com/mini/all-order.png',
+      title: '所有设备',
+      desc: '查看仓库所有设备'
+    }, {
+      img: 'http://t18.9026.com/mini/rent-check.png',
+      title: '维修上报',
+      desc: '设备维修上报'
+    }]
   },
 
   /**
@@ -38,7 +64,12 @@ Page({
       id: id,
       userInfo: app.globalData.userInfo
     })
-    this.getProject()
+  },
+
+  navigate: function(e) {
+    wx.navigateTo({
+      url: e.currentTarget.dataset.url,
+    })
   },
 
   getProject() {
@@ -72,7 +103,7 @@ Page({
    * 生命周期函数--监听页面显示
    */
   onShow: function () {
-
+    this.getProject()
   },
 
   /**

+ 28 - 2
mini/pages/project/index.wxml

xqd xqd xqd
@@ -1,7 +1,7 @@
 <!--pages/project/index.wxml-->
 <view class="sg-container">
   <view class="sg-user-box sg-pad sg-flex sg-align-center">
-    <image class="sg-big-avatar" src="{{userInfo.avatar}}" mode="widthFix"></image>
+    <image class="sg-big-avatar" src="{{userInfo.avatarUrl}}" mode="widthFix"></image>
     <view class="sg-right sg-flex-grow">
       <view class="sg-name">{{userInfo.name}}</view>
       <view
@@ -11,7 +11,7 @@
       </view>
       <view class="sg-phone-user sg-flex sg-align-center sg-space-between">
         <view class="sg-phone sg-gray-color sg-font-small">{{ userInfo.phone }}</view>
-        <view class="sg-user sg-font-small sg-index-color sg-flex sg-align-center"><van-icon name="user-o" /><text>人员管理</text></view>
+        <view class="sg-user sg-font-small sg-index-color sg-flex sg-align-center" bindtap="navigate" data-url="/pages/project-user/index?id={{id}}"><van-icon name="user-o" /><text>人员管理</text></view>
       </view>
     </view>
   </view>
@@ -28,4 +28,30 @@
       </view>
     </view>
   </view>
+  <view class="sg-menu-box sg-pad">
+    <view class="sg-menu-header">
+      <text class="sg-title sg-bold">设备调用</text>
+      <text class="sg-sub-title sg-gray-color">内部设备调用</text>
+    </view>
+    <view class="sg-menu-list">
+      <view class="sg-item sg-flex sg-align-center sg-pad" wx:for="{{device_rent_menus}}" wx:key="index">
+        <image class="sg-img" src="{{item.img}}" mode="widthFix"></image>
+        <view class="sg-name sg-margin-top-sm">{{item.title}}</view>
+        <view class="sg-desp sg-margin-top-sm sg-gray-color sg-font-xs">{{item.desc}}</view>
+      </view>
+    </view>
+  </view>
+  <view class="sg-menu-box sg-pad">
+    <view class="sg-menu-header">
+      <text class="sg-title sg-bold">设备仓库</text>
+      <text class="sg-sub-title sg-gray-color">内部设备管理</text>
+    </view>
+    <view class="sg-menu-list">
+      <view class="sg-item sg-flex sg-align-center sg-pad" wx:for="{{device_depot_menus}}" wx:key="index">
+        <image class="sg-img" src="{{item.img}}" mode="widthFix"></image>
+        <view class="sg-name sg-margin-top-sm">{{item.title}}</view>
+        <view class="sg-desp sg-margin-top-sm sg-gray-color sg-font-xs">{{item.desc}}</view>
+      </view>
+    </view>
+  </view>
 </view>

+ 3 - 1
mini/pages/project/index.wxss

xqd
@@ -2,10 +2,12 @@
 .sg-user-box {
   background: white;
 }
+.sg-menu-box {
+  padding-bottom: 0;
+}
 .sg-menu-list {
   display: flex;
   flex-wrap: wrap;
-  margin-bottom: 30rpx;
 }
 .sg-menu-list .sg-item .sg-img {
   width: 130rpx;

+ 5 - 0
routes/api.php

xqd
@@ -25,10 +25,15 @@ $api->version('v1', ['namespace' => 'App\Http\Controllers\Api\mini', 'prefix' =>
     $api->any('projects/getAll', 'ProjectController@getAll');
     $api->any('projects/addUser', 'ProjectController@addUser');
     $api->any('projects/detail', 'ProjectController@detail');
+    $api->any('projects/getUsers', 'ProjectController@getUsers');
+    $api->any('projects/deleteUser', 'ProjectController@deleteUser');
+    $api->any('projects/updateUser', 'ProjectController@updateUser');
 
     $api->any('project-roles/create', 'ProjectRoleController@create');
     $api->any('project-roles/get', 'ProjectRoleController@get');
     $api->any('project-roles/getAll', 'ProjectRoleController@getAll');
 
+    $api->any('project-users/detail', 'ProjectUserController@detail');
+
     $api->any('users/search', 'UserController@search');
 });