From 865ac80b99d3a2ffcbb853c0f5ba886a8274ece7 Mon Sep 17 00:00:00 2001 From: kuaifan Date: Thu, 17 Nov 2022 20:49:47 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E5=AE=8C=E5=96=84=E9=83=A8=E9=97=A8?= =?UTF-8?q?=E7=BE=A4=E7=BB=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Api/UsersController.php | 52 ++++++- app/Models/User.php | 15 ++ app/Models/UserDepartment.php | 64 +++++++- app/Models/WebSocketDialog.php | 10 +- app/Models/WebSocketDialogUser.php | 2 +- ...2022_11_17_103811_add_users_department.php | 34 +++++ .../manage/components/DialogGroupInfo.vue | 10 +- .../pages/manage/components/DialogWrapper.vue | 8 +- .../manage/components/TeamManagement.vue | 139 ++++++++++++++++-- .../assets/js/pages/manage/messenger.vue | 3 +- .../sass/pages/components/dialog-wrapper.scss | 11 ++ .../pages/components/team-management.scss | 22 +++ .../assets/sass/pages/page-messenger.scss | 3 + 13 files changed, 343 insertions(+), 30 deletions(-) create mode 100644 database/migrations/2022_11_17_103811_add_users_department.php diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index b99386e1b..b9dc866f2 100755 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -502,6 +502,7 @@ class UsersController extends AbstractController * - yes: 已认证 * - no: 未认证 * - 其他值: 全部(默认) + * - keys.department 部门ID(0表示默认部门,不赋值获取所有部门) * * @apiParam {Number} [page] 当前页,默认:1 * @apiParam {Number} [pagesize] 每页显示数量,默认:20,最大:50 @@ -560,6 +561,15 @@ class UsersController extends AbstractController } elseif ($keys['email_verity'] === 'no') { $builder->whereEmailVerity(0); } + if (isset($keys['department'])) { + if ($keys['department'] == '0') { + $builder->where(function($query) { + $query->where("department", "")->orWhere("department", ",,"); + }); + } else { + $builder->where("department", "like", "%,{$keys['department']},%"); + } + } } else { $builder->whereNull('disable_at'); } @@ -580,6 +590,7 @@ class UsersController extends AbstractController * @apiParam {String} [type] 操作 * - setadmin 设为管理员 * - clearadmin 取消管理员 + * - department 修改部门(需要参数 department) * - setdisable 设为离职(需要参数 disable_time、transfer_userid) * - cleardisable 取消离职 * - delete 删除会员(需要参数 delete_reason) @@ -588,6 +599,7 @@ class UsersController extends AbstractController * @apiParam {String} [password] 新的密码 * @apiParam {String} [nickname] 昵称 * @apiParam {String} [profession] 职位 + * @apiParam {String} [department] 部门 * @apiParam {String} [disable_time] 离职时间 * @apiParam {String} [transfer_userid] 离职交接人 * @apiParam {String} [delete_reason] 删除原因 @@ -622,6 +634,18 @@ class UsersController extends AbstractController $upArray['identity'] = array_diff($userInfo->identity, ['admin']); break; + case 'department': + if (!is_array($data['department'])) { + $data['department'] = []; + } + foreach ($data['department'] as $id) { + if (!UserDepartment::whereId($id)->exists()) { + return Base::retError('修改部门不存在'); + } + } + $upArray['department'] = $data['department']; + break; + case 'setdisable': if ($userInfo->userid === $user->userid) { return Base::retError('不能操作自己离职'); @@ -660,6 +684,9 @@ class UsersController extends AbstractController if (isset($upArray['identity'])) { $upArray['identity'] = "," . implode(",", $upArray['identity']) . ","; } + if (isset($upArray['department'])) { + $upArray['department'] = "," . implode(",", $upArray['department']) . ","; + } // 邮箱 if (Arr::exists($data, 'email')) { $email = trim($data['email']); @@ -710,9 +737,29 @@ class UsersController extends AbstractController } if ($upArray) { AbstractModel::transaction(function() use ($user, $type, $upArray, $userInfo, $transferUser) { + $exitIds = array_diff($userInfo->department, Base::explodeInt($upArray['department'])); + $joinIds = array_diff(Base::explodeInt($upArray['department']), $userInfo->department); $userInfo->updateInstance($upArray); $userInfo->save(); - if ($type === 'setdisable') { + if ($type === 'department') { + $userids = [$userInfo->userid]; + // 退出群组 + $exitDepartments = UserDepartment::whereIn('id', $exitIds)->get(); + foreach ($exitDepartments as $exitDepartment) { + if ($exitDepartment->dialog_id > 0 && $exitDialog = WebSocketDialog::find($exitDepartment->dialog_id)) { + $exitDialog->exitGroup($userids, 'remove', false); + $exitDialog->pushMsg("groupExit", null, $userids); + } + } + // 加入群组 + $joinDepartments = UserDepartment::whereIn('id', $joinIds)->get(); + foreach ($joinDepartments as $joinDepartment) { + if ($joinDepartment->dialog_id > 0 && $joinDialog = WebSocketDialog::find($joinDepartment->dialog_id)) { + $joinDialog->joinGroup($userids, 0, true); + $joinDialog->pushMsg("groupJoin", null, $userids); + } + } + } elseif ($type === 'setdisable') { $userTransfer = UserTransfer::createInstance([ 'original_userid' => $userInfo->userid, 'new_userid' => $transferUser->userid, @@ -1193,12 +1240,11 @@ class UsersController extends AbstractController return Base::retError('请选择正确的部门负责人'); } // - $userDepartment->updateInstance([ + $userDepartment->saveDepartment([ 'name' => $name, 'parent_id' => $parent_id, 'owner_userid' => $owner_userid, ]); - $userDepartment->saveDepartment(); // return Base::retSuccess($parent_id > 0 ? '保存成功' : '新建成功'); } diff --git a/app/Models/User.php b/app/Models/User.php index 5c5ad7c9d..14c276781 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -13,6 +13,7 @@ use Carbon\Carbon; * * @property int $userid * @property array $identity 身份 + * @property array $department 所属部门 * @property string|null $az A-Z * @property string|null $pinyin 拼音(主要用于搜索) * @property string|null $email 邮箱 @@ -42,6 +43,7 @@ use Carbon\Carbon; * @method static \Illuminate\Database\Eloquent\Builder|User whereChangepass($value) * @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedIp($value) + * @method static \Illuminate\Database\Eloquent\Builder|User whereDepartment($value) * @method static \Illuminate\Database\Eloquent\Builder|User whereDisableAt($value) * @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value) * @method static \Illuminate\Database\Eloquent\Builder|User whereEmailVerity($value) @@ -133,6 +135,19 @@ class User extends AbstractModel return array_filter(is_array($value) ? $value : explode(",", trim($value, ","))); } + /** + * 部门 + * @param $value + * @return array + */ + public function getDepartmentAttribute($value) + { + if (empty($value)) { + return []; + } + return array_filter(is_array($value) ? $value : explode(",", trim($value, ","))); + } + /** * 是否在线 * @return bool diff --git a/app/Models/UserDepartment.php b/app/Models/UserDepartment.php index 3c7cfa4bf..a1f485445 100644 --- a/app/Models/UserDepartment.php +++ b/app/Models/UserDepartment.php @@ -2,6 +2,8 @@ namespace App\Models; +use App\Exceptions\ApiException; + /** * App\Models\UserDepartment * @@ -26,14 +28,48 @@ namespace App\Models; */ class UserDepartment extends AbstractModel { - /** * 保存部门 - * @return bool + * @param $data */ - public function saveDepartment() { - // todo 聊天室相关 - return $this->save(); + public function saveDepartment($data = []) { + AbstractModel::transaction(function () use ($data) { + $oldUser = null; + $newUser = null; + if ($data['owner_userid'] !== $this->owner_userid) { + $oldUser = User::find($this->owner_userid); + $newUser = User::find($data['owner_userid']); + } + $this->updateInstance($data); + // + if ($this->dialog_id > 0) { + $dialog = WebSocketDialog::find($this->dialog_id); + if ($dialog) { + $dialog->name = $this->name; + $dialog->owner_id = $this->owner_userid; + $dialog->save(); + } + } else { + $dialog = WebSocketDialog::createGroup($this->name, [$this->owner_userid], 'department', $this->owner_userid); + if (empty($dialog)) { + throw new ApiException("创建群组失败"); + } + $this->dialog_id = $dialog->id; + } + $this->save(); + // + if ($oldUser) { + $oldUser->department = array_diff($oldUser->department, [$this->id]); + $oldUser->department = "," . implode(",", $oldUser->department) . ","; + $oldUser->save(); + } + if ($newUser) { + $newUser->department = array_diff($newUser->department, [$this->id]); + $newUser->department = array_merge($newUser->department, [$this->id]); + $newUser->department = "," . implode(",", $newUser->department) . ","; + $newUser->save(); + } + }); } /** @@ -41,11 +77,27 @@ class UserDepartment extends AbstractModel * @return void */ public function deleteDepartment() { + // 删除子部门 $list = self::whereParentId($this->id)->get(); foreach ($list as $item) { $item->deleteDepartment(); } - // todo 移动成员 + // 移出成员 + User::where("department", "like", "%,{$this->id},%")->chunk(100, function($items) { + /** @var User $user */ + foreach ($items as $user) { + $user->department = array_diff($user->department, [$this->id]); + $user->department = "," . implode(",", $user->department) . ","; + $user->save(); + } + }); + // 解散群组 + $dialog = WebSocketDialog::find($this->dialog_id); + if ($dialog) { + $dialog->deleteDialog(); + $dialog->pushMsg("groupDelete"); + } + // $this->delete(); } } diff --git a/app/Models/WebSocketDialog.php b/app/Models/WebSocketDialog.php index 52a87cbd0..2de6044dc 100644 --- a/app/Models/WebSocketDialog.php +++ b/app/Models/WebSocketDialog.php @@ -144,11 +144,12 @@ class WebSocketDialog extends AbstractModel * 加入聊天室 * @param int|array $userid 加入的会员ID或会员ID组 * @param int $inviter 邀请人 + * @param bool $important 重要人员 * @return bool */ - public function joinGroup($userid, $inviter) + public function joinGroup($userid, $inviter, $important = false) { - AbstractModel::transaction(function () use ($inviter, $userid) { + AbstractModel::transaction(function () use ($important, $inviter, $userid) { foreach (is_array($userid) ? $userid : [$userid] as $value) { if ($value > 0) { WebSocketDialogUser::updateInsert([ @@ -156,6 +157,7 @@ class WebSocketDialog extends AbstractModel 'userid' => $value, ], [ 'inviter' => $inviter, + 'important' => $important ? 1 : 0, ]); WebSocketDialogMsg::sendMsg(null, $this->id, 'notice', [ 'notice' => User::userid2nickname($value) . " 已加入群组" @@ -198,7 +200,7 @@ class WebSocketDialog extends AbstractModel throw new ApiException('群主不可' . $typeDesc); } if ($item->important) { - throw new ApiException('项目人员或任务人员不可' . $typeDesc); + throw new ApiException('部门成员、项目人员或任务人员不可' . $typeDesc); } } // @@ -396,7 +398,7 @@ class WebSocketDialog extends AbstractModel 'name' => $name ?: '', 'group_type' => $group_type, 'owner_id' => $owner_id, - 'last_at' => in_array($group_type, ['user', 'all']) ? Carbon::now() : null, + 'last_at' => in_array($group_type, ['user', 'department', 'all']) ? Carbon::now() : null, ]); $dialog->save(); foreach (is_array($userid) ? $userid : [$userid] as $value) { diff --git a/app/Models/WebSocketDialogUser.php b/app/Models/WebSocketDialogUser.php index acf7c1177..1f48cf039 100644 --- a/app/Models/WebSocketDialogUser.php +++ b/app/Models/WebSocketDialogUser.php @@ -11,7 +11,7 @@ namespace App\Models; * @property string|null $top_at 置顶时间 * @property int|null $mark_unread 是否标记为未读:0否,1是 * @property int|null $inviter 邀请人 - * @property int|null $important 是否不可移出(项目、任务人员) + * @property int|null $important 是否不可移出(项目、任务、部门人员) * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser newModelQuery() diff --git a/database/migrations/2022_11_17_103811_add_users_department.php b/database/migrations/2022_11_17_103811_add_users_department.php new file mode 100644 index 000000000..7b26cb125 --- /dev/null +++ b/database/migrations/2022_11_17_103811_add_users_department.php @@ -0,0 +1,34 @@ +string('department', 255)->nullable()->default('')->after('identity')->comment('所属部门'); + } + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn("department"); + }); + } +} diff --git a/resources/assets/js/pages/manage/components/DialogGroupInfo.vue b/resources/assets/js/pages/manage/components/DialogGroupInfo.vue index 2ca5e9763..910fa56ec 100644 --- a/resources/assets/js/pages/manage/components/DialogGroupInfo.vue +++ b/resources/assets/js/pages/manage/components/DialogGroupInfo.vue @@ -34,7 +34,9 @@
- +
@@ -45,7 +47,10 @@ :mask-closable="false">
- + +
{{$L('此操作仅加入群成员并不会加入部门')}}
+
{{$L('此操作仅加入群成员并不会加入项目')}}
+
{{$L('此操作仅加入群成员并不会加入任务负责人')}}
@@ -95,6 +100,7 @@ export default { groupType() { const {group_type} = this.dialogData + if (group_type === 'department') return '部门群组' if (group_type === 'project') return '项目群组' if (group_type === 'task') return '任务群组' if (group_type === 'user') return '个人群组' diff --git a/resources/assets/js/pages/manage/components/DialogWrapper.vue b/resources/assets/js/pages/manage/components/DialogWrapper.vue index e86c6e577..a7d2d275e 100644 --- a/resources/assets/js/pages/manage/components/DialogWrapper.vue +++ b/resources/assets/js/pages/manage/components/DialogWrapper.vue @@ -20,7 +20,8 @@
@@ -40,6 +41,8 @@

{{dialogData.name}}

({{peopleNum}}) + {{$L('全员')}} + {{$L('部门')}}
  • @@ -295,7 +298,8 @@
    diff --git a/resources/assets/js/pages/manage/components/TeamManagement.vue b/resources/assets/js/pages/manage/components/TeamManagement.vue index d2ba44660..8fa2a17e6 100644 --- a/resources/assets/js/pages/manage/components/TeamManagement.vue +++ b/resources/assets/js/pages/manage/components/TeamManagement.vue @@ -9,14 +9,14 @@
      -
    • +
    • {{$L('默认部门')}}
      - +
      {{$L('添加子部门')}}
      @@ -24,7 +24,11 @@
    • -
    • +
    • {{$L('部门负责人')}}

      @@ -33,7 +37,7 @@ size="medium" trigger="click" @command="onOpDepartment"> - +
      {{$L('添加子部门')}}
      @@ -163,10 +167,30 @@
    + + +
    + {{$L(`正在进行帐号【ID:${departmentEditData.userid},${departmentEditData.nickname}】部门修改。`)}} + +
    {{departmentEditData.old || '-'}}
    +
    + + + +
    +
    + + +
    +
    +
    {{$L(`正在进行帐号【ID:${disableData.userid},${disableData.nickname}】离职操作。`)}} @@ -223,12 +247,17 @@ export default { total: 0, noText: '', + departmentEditShow: false, + departmentEditLoading: 0, + departmentEditData: {}, + disableShow: false, disableLoading: 0, disableData: {}, departmentShow: false, departmentLoading: 0, + departmentSelect: -1, departmentData: { id: 0, name: '', @@ -248,6 +277,9 @@ export default { this.keys = {} this.setPage(1) } + }, + departmentSelect() { + this.setPage(1) } }, computed: { @@ -378,6 +410,43 @@ export default { ]); }, }, + { + title: this.$L('部门'), + key: 'department', + minWidth: 80, + render: (h, {row}) => { + let departments = [] + row.department.some(did => { + const data = this.departmentList.find(d => d.id == did) + if (data) { + departments.push(data.name) + } + }) + if (departments.length === 0) { + return h('div', this.$L('默认部门')); + } else { + const tmp = [] + tmp.push(h('span', departments[0])) + if (departments.length > 1) { + departments = departments.splice(1) + tmp.push(h('ETooltip', [ + h('div', { + slot: 'content', + domProps: { + innerHTML: departments.join("
    ") + } + }), + h('div', { + class: 'department-tag-num' + }, ` +${departments.length}`) + ])) + } + return h('div', { + class: 'team-table-department-warp' + }, tmp); + } + }, + }, { title: this.$L('最后在线'), key: 'line_at', @@ -416,6 +485,12 @@ export default { }, }, [h('div', this.$L('修改密码'))])) + dropdownItems.push(h('EDropdownItem', { + props: { + command: 'department', + }, + }, [h('div', this.$L('修改部门'))])) + if (identity.includes('disable')) { dropdownItems.push(h('EDropdownItem', { props: { @@ -487,10 +562,16 @@ export default { getLists() { this.loadIng++; this.keyIs = $A.objImplode(this.keys) != ""; + let keys = $A.cloneJSON(this.keys) + if (this.departmentSelect > -1) { + keys = Object.assign(keys, { + department: this.departmentSelect + }) + } this.$store.dispatch("call", { url: 'users/lists', data: { - keys: this.keys, + keys, page: Math.max(this.page, 1), pagesize: Math.max($A.runNum(this.pageSize), 10), }, @@ -498,7 +579,7 @@ export default { this.page = data.current_page; this.total = data.total; this.list = data.data; - this.noText = '没有相关的数据'; + this.noText = '没有相关的成员'; }).catch(() => { this.noText = '数据加载失败'; }).finally(_ => { @@ -551,6 +632,24 @@ export default { }); break; + case 'department': + let departments = [] + row.department.some(did => { + const data = this.departmentList.find(d => d.id == did) + if (data) { + departments.push(data.name) + } + }) + this.departmentEditData = { + type: 'department', + userid: row.userid, + nickname: row.nickname, + department: row.department.map(id => parseInt(id)), + old: departments.join(", ") + }; + this.departmentEditShow = true; + break; + case 'setdisable': this.disableData = { type: 'setdisable', @@ -602,7 +701,9 @@ export default { operationUser(data, tipErr) { return new Promise((resolve, reject) => { - if (data.type == 'setdisable') { + if (data.type == 'department') { + this.departmentEditLoading++; + } else if (data.type == 'setdisable') { this.disableLoading++; } else { this.loadIng++; @@ -614,7 +715,9 @@ export default { $A.messageSuccess(msg); this.getLists(); resolve() - if (data.type == 'setdisable') { + if (data.type == 'department') { + this.departmentEditShow = false; + } else if (data.type == 'setdisable') { this.disableShow = false; } }).catch(({msg}) => { @@ -624,7 +727,9 @@ export default { this.getLists(); reject(msg) }).finally(_ => { - if (data.type == 'setdisable') { + if (data.type == 'department') { + this.departmentEditLoading--; + } else if (data.type == 'setdisable') { this.disableLoading--; } else { this.loadIng--; @@ -676,6 +781,7 @@ export default { }).then(({msg}) => { $A.messageSuccess(msg) this.getDepartmentLists() + this.getLists() this.departmentShow = false }).catch(({msg}) => { $A.modalError(msg); @@ -684,6 +790,14 @@ export default { }) }, + onSelectDepartment(id) { + if (this.departmentSelect === id) { + this.departmentSelect = -1 + return + } + this.departmentSelect = id + }, + onOpDepartment(val) { if ($A.leftExists(val, 'add_')) { this.onShowDepartment({ @@ -699,7 +813,7 @@ export default { if (delItem) { $A.modalConfirm({ title: this.$L('删除部门'), - content: `
    ${this.$L(`你确定要删除【${delItem.name}】部门吗?`)}
    ${this.$L(`注意:此操作不可恢复,部门下的成员将向上移动。`)}
    `, + content: `
    ${this.$L(`你确定要删除【${delItem.name}】部门吗?`)}
    ${this.$L(`注意:此操作不可恢复,部门下的成员将移至默认部门。`)}
    `, language: false, loading: true, onOk: () => { @@ -710,6 +824,9 @@ export default { id: delItem.id }, }).then(({msg}) => { + if (delItem.id === this.departmentSelect) { + this.departmentSelect = -1 + } resolve(msg); this.getDepartmentLists(); }).catch(({msg}) => { diff --git a/resources/assets/js/pages/manage/messenger.vue b/resources/assets/js/pages/manage/messenger.vue index 5d9922217..141e1e5ff 100644 --- a/resources/assets/js/pages/manage/messenger.vue +++ b/resources/assets/js/pages/manage/messenger.vue @@ -55,7 +55,8 @@ })" v-longpress="handleLongpress"> diff --git a/resources/assets/sass/pages/components/dialog-wrapper.scss b/resources/assets/sass/pages/components/dialog-wrapper.scss index 3a528195b..4e1e38ba1 100644 --- a/resources/assets/sass/pages/components/dialog-wrapper.scss +++ b/resources/assets/sass/pages/components/dialog-wrapper.scss @@ -128,6 +128,10 @@ background-color: #61B2F9; color: #ffffff; + &.department { + background-color: #5BC7B0; + } + &.project { background-color: #6E99EB; } @@ -160,6 +164,10 @@ &.ivu-tag-success { padding: 0 6px; } + + &.after { + margin: 0 0 0 6px; + } } .ivu-icon { @@ -1420,6 +1428,9 @@ font-size: 18px; background-color: #61B2F9; color: #ffffff; + &.department { + background-color: #5BC7B0; + } &.project { background-color: #6E99EB; } diff --git a/resources/assets/sass/pages/components/team-management.scss b/resources/assets/sass/pages/components/team-management.scss index b0c034d1e..511a685b7 100644 --- a/resources/assets/sass/pages/components/team-management.scss +++ b/resources/assets/sass/pages/components/team-management.scss @@ -130,3 +130,25 @@ height: 0; } } + +.team-table-department-warp { + display: flex; + align-items: center; + + .department-tag-num { + background-color: #515a6e; + border-radius: 9px; + color: #fff; + cursor: pointer; + flex-shrink: 0; + font-size: 12px; + height: 18px; + line-height: 18px; + margin-left: 4px; + min-width: 18px; + padding: 0 5px; + text-align: center; + transform: scale(0.9); + transform-origin: right center; + } +} diff --git a/resources/assets/sass/pages/page-messenger.scss b/resources/assets/sass/pages/page-messenger.scss index 04452637e..f60c708fb 100644 --- a/resources/assets/sass/pages/page-messenger.scss +++ b/resources/assets/sass/pages/page-messenger.scss @@ -126,6 +126,9 @@ font-size: 26px; background-color: #61B2F9; color: #ffffff; + &.department { + background-color: #5BC7B0; + } &.project { background-color: #6E99EB; }