diff --git a/app/Http/Controllers/Api/DialogController.php b/app/Http/Controllers/Api/DialogController.php
index 38fc9ec13..e685f8674 100755
--- a/app/Http/Controllers/Api/DialogController.php
+++ b/app/Http/Controllers/Api/DialogController.php
@@ -2777,6 +2777,83 @@ class DialogController extends AbstractController
]);
}
+ /**
+ * @api {get} api/dialog/common/list 56. 共同群组群聊
+ *
+ * @apiDescription 需要token身份,按置顶时间、用户在群组中的最后活跃时间倒序排列
+ * @apiVersion 1.0.0
+ * @apiGroup dialog
+ * @apiName common__list
+ *
+ * @apiParam {Number} [target_userid] 目标用户ID(和谁的共同群组,不传则获取自己所有群组)
+ * @apiParam {Number} [page] 当前页数,默认为1
+ * @apiParam {Number} [pagesize] 每页显示条数,默认为20,最大100
+ * @apiParam {String} [only_count] 是否只返回数量,传入 'yes' 则只返回数量不返回列表
+ *
+ * @apiSuccess {Number} ret 返回状态码(1正确、0错误)
+ * @apiSuccess {String} msg 返回信息(错误描述)
+ * @apiSuccess {Object} data 返回数据
+ *
+ * - 当 only_count=yes 时:
+ * @apiSuccess {Number} data.total 群组数量
+ *
+ * - 当获取列表时,返回 Laravel 标准分页格式:
+ * @apiSuccess {Array} data.data 群组列表数据
+ * @apiSuccess {Number} data.current_page 当前页数
+ * @apiSuccess {Number} data.per_page 每页显示条数
+ * @apiSuccess {Number} data.total 总数量
+ * @apiSuccess {String} data.first_page_url 第一页链接
+ * @apiSuccess {String} data.last_page_url 最后页链接
+ * @apiSuccess {String} data.next_page_url 下一页链接
+ * @apiSuccess {String} data.prev_page_url 上一页链接
+ */
+ public function common__list()
+ {
+ $user = User::auth();
+ //
+ $target_userid = intval(Request::input('target_userid'));
+ $only_count = trim(Request::input('only_count')) === 'yes';
+
+ // 参考getDialogList的查询模式
+ $builder = DB::table('web_socket_dialog_users as u')
+ ->select(['d.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', 'u.updated_at as user_at'])
+ ->join('web_socket_dialogs as d', 'u.dialog_id', '=', 'd.id')
+ ->where('u.userid', $user->userid)
+ ->where('d.type', 'group')
+ ->where('d.group_type', 'user')
+ ->whereNull('d.deleted_at');
+
+ if ($target_userid) {
+ // 获取与目标用户的共同群组
+ $builder->whereExists(function($query) use ($target_userid) {
+ $query->select(DB::raw(1))
+ ->from('web_socket_dialog_users as du2')
+ ->whereColumn('du2.dialog_id', 'd.id')
+ ->where('du2.userid', $target_userid);
+ });
+ }
+
+ if ($only_count) {
+ // 只返回数量
+ return Base::retSuccess('success', [
+ 'total' => $builder->count()
+ ]);
+ }
+
+ // 返回分页列表,参考getDialogList的排序逻辑
+ $list = $builder
+ ->orderByDesc('u.top_at')
+ ->orderByDesc('u.last_at')
+ ->paginate(Base::getPaginate(100, 20));
+
+ // 处理分页数据,与getDialogList保持一致的处理方式
+ $list->transform(function ($item) use ($user) {
+ return WebSocketDialog::synthesizeData($item, $user->userid);
+ });
+
+ return Base::retSuccess('success', $list);
+ }
+
/**
* @api {post} api/dialog/okr/add 56. 创建OKR评论会话
*
diff --git a/resources/assets/js/pages/manage/components/UserDetail.vue b/resources/assets/js/pages/manage/components/UserDetail.vue
index 796a791a7..c5652fde5 100755
--- a/resources/assets/js/pages/manage/components/UserDetail.vue
+++ b/resources/assets/js/pages/manage/components/UserDetail.vue
@@ -6,46 +6,93 @@
:mask-closable="false"
:footer-hide="true"
width="600">
-
-
-
- -
-
{{userData.nickname}}
- {{$L('已删除')}}
- {{$L('已离职')}}
+
+
+
+ -
+
{{userData.nickname}}
+ {{$L('已删除')}}
+ {{$L('已离职')}}
+
+ -
+ {{$L('共同群聊')}}:
+ {{ $L('(*)个', commonDialog.total) }}
+
+
+ -
+ {{$L('部门')}}:
+ {{userData.department_name || '-'}}
-
- -
- {{$L('部门')}}:
- {{userData.department_name || '-'}}
-
- -
- {{$L('职位/职称')}}:
- {{userData.profession || '-'}}
-
- -
- {{$L('最后在线')}}:
- {{$A.newDateString(userData.line_at, 'YYYY-MM-DD HH:mm') || '-'}}
-
- -
- {{$L('删除时间')}}: {{$A.newDateString(userData.delete_at, 'YYYY-MM-DD HH:mm')}}
-
- -
- {{$L('离职时间')}}: {{$A.newDateString(userData.disable_at, 'YYYY-MM-DD HH:mm')}}
-
-
-
-
+
-
+ {{$L('职位/职称')}}:
+ {{userData.profession || '-'}}
+
+
-
+ {{$L('最后在线')}}:
+ {{$A.newDateString(userData.line_at, 'YYYY-MM-DD HH:mm') || '-'}}
+
+
-
+ {{$L('删除时间')}}: {{$A.newDateString(userData.delete_at, 'YYYY-MM-DD HH:mm')}}
+
+
-
+ {{$L('离职时间')}}: {{$A.newDateString(userData.disable_at, 'YYYY-MM-DD HH:mm')}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{$L('(*)人', dialog.people || 0)}}
+ {{$A.timeFormat(dialog.last_at)}}
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/sass/dark.scss b/resources/assets/sass/dark.scss
index 1154a32b9..d51006d5a 100644
--- a/resources/assets/sass/dark.scss
+++ b/resources/assets/sass/dark.scss
@@ -321,6 +321,18 @@ body.dark-mode-reverse {
}
}
+ .common-dialog-content {
+ .dialog-list {
+ .dialog-item {
+ .dialog-avatar {
+ .icon-avatar {
+ color: #1c1917;
+ }
+ }
+ }
+ }
+ }
+
.file-icon {
&:before {
background-image: url("../images/file/dark/other.svg");
diff --git a/resources/assets/sass/pages/components/user-detail.scss b/resources/assets/sass/pages/components/user-detail.scss
index c4514a562..40c68680f 100755
--- a/resources/assets/sass/pages/components/user-detail.scss
+++ b/resources/assets/sass/pages/components/user-detail.scss
@@ -82,3 +82,158 @@
}
}
}
+
+// 共同群组弹窗样式
+.common-dialog-content {
+ margin: -16px -32px 0;
+
+ .loading-wrapper {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+ padding-top: 60px;
+ padding-bottom: 100px;
+ }
+
+ .empty-wrapper {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding-top: 40px;
+ padding-bottom: 80px;
+
+ .empty-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+ width: 100%;
+ color: #999;
+ > i {
+ opacity: 0.3;
+ }
+ }
+ }
+
+ .dialog-list {
+ padding: 0 12px;
+ overflow-y: auto;
+ max-height: calc(100vh - 310px);
+
+ @media (height <= 900px) {
+ max-height: calc(100vh - 180px);
+ }
+
+ .dialog-item {
+ display: flex;
+ align-items: center;
+ padding: 12px 16px;
+ cursor: pointer;
+ border-radius: 6px;
+ margin: 4px 0;
+ transition: background-color 0.2s;
+
+ &:hover {
+ background-color: #f5f7fa;
+ }
+
+ .dialog-avatar {
+ flex-shrink: 0;
+ margin-right: 12px;
+
+ .img-avatar,
+ .user-avatar,
+ .icon-avatar {
+ width: 42px;
+ height: 42px;
+ margin-right: 2px;
+ flex-grow: 0;
+ flex-shrink: 0;
+ }
+
+ .img-avatar {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ > img {
+ width: 100%;
+ height: 100%;
+ }
+ }
+
+ .icon-avatar {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 50%;
+ font-size: 26px;
+ background-color: #61B2F9;
+ color: #ffffff;
+
+ &.department {
+ background-color: #5BC7B0;
+ }
+
+ &.project {
+ background-color: #6E99EB;
+ }
+
+ &.task {
+ background-color: #9B96DF;
+ font-size: 24px;
+ }
+ }
+ }
+
+ .dialog-info {
+ flex: 1;
+ min-width: 0;
+
+ .dialog-name {
+ font-size: 14px;
+ font-weight: 500;
+ color: #17233d;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ margin-bottom: 4px;
+ }
+
+ .dialog-meta {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ font-size: 12px;
+ color: #808695;
+
+ .member-count {
+ flex-shrink: 0;
+ }
+
+ .last-time {
+ flex-shrink: 0;
+ }
+ }
+ }
+
+ .enter-icon {
+ flex-shrink: 0;
+ color: #c5c8ce;
+ font-size: 16px;
+ margin-left: 8px;
+ }
+ }
+
+ &:last-child {
+ padding-bottom: 16px;
+ }
+ }
+
+ .load-more-wrapper {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 12px 0;
+ }
+}