From ab4640382dde48dcda8587b7a34c2ab358ad40cb Mon Sep 17 00:00:00 2001 From: kuaifan Date: Thu, 6 Nov 2025 02:01:15 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=BC=9A=E5=91=98?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E4=BF=A1=E6=81=AF=E6=8E=A5=E5=8F=A3=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=94=A8=E6=88=B7=E8=AF=A6=E6=83=85=E5=92=8C?= =?UTF-8?q?=E4=B8=AA=E4=BA=BA=E8=AE=BE=E7=BD=AE=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Api/UsersController.php | 78 +++++++++++++++--- app/Models/User.php | 2 +- .../js/pages/manage/components/UserDetail.vue | 12 ++- .../pages/manage/components/UserTagsModal.vue | 26 +++--- .../js/pages/manage/setting/personal.vue | 82 +++++++++++++++++-- resources/assets/js/store/actions.js | 60 +++++++++++++- resources/assets/js/store/state.js | 1 + .../sass/pages/components/user-detail.scss | 1 - 8 files changed, 221 insertions(+), 41 deletions(-) diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 1207de77d..dda8c3939 100755 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -388,9 +388,6 @@ class UsersController extends AbstractController $data['nickname_original'] = $user->getRawOriginal('nickname'); $data['department_name'] = $user->getDepartmentName(); $data['department_owner'] = UserDepartment::where('parent_id',0)->where('owner_userid', $user->userid)->exists(); // 适用默认部门下第1级负责人才能添加部门OKR - $tagMeta = UserTag::listWithMeta($user->userid, $user); - $data['personal_tags'] = $tagMeta['top']; - $data['personal_tags_total'] = $tagMeta['total']; return Base::retSuccess('success', $data); } @@ -809,12 +806,8 @@ class UsersController extends AbstractController public function basic() { $sharekey = Request::header('sharekey'); - $shareInfo = $sharekey ? Meeting::getShareInfo($sharekey) : null; - $viewer = null; - if (empty($shareInfo)) { - $viewer = User::auth(); - } elseif (Doo::userId() > 0) { - $viewer = User::whereUserid(Doo::userId())->first(); + if (empty($sharekey) || !Meeting::getShareInfo($sharekey)) { + User::auth(); } // $userid = Request::input('userid'); @@ -832,16 +825,75 @@ class UsersController extends AbstractController $basic = UserDelete::userid2basic($id); } if ($basic) { - $tagMeta = UserTag::listWithMeta($basic->userid, $viewer); - $basic->personal_tags = $tagMeta['top']; - $basic->personal_tags_total = $tagMeta['total']; - // $retArray[] = $basic; } } return Base::retSuccess('success', $retArray); } + /** + * @api {get} api/users/extra 获取会员扩展信息 + * + * @apiDescription 需要token身份 + * @apiVersion 1.0.0 + * @apiGroup users + * @apiName extra + * + * @apiParam {Number} [userid] 会员ID(不传默认为当前用户) + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function extra() + { + $user = User::auth(); + // + $userid = intval(Request::input('userid')); + if ($userid <= 0) { + $userid = $user->userid; + } + if ($userid <= 0) { + return Base::retError('会员不存在'); + } + + $user = User::query() + ->select(['userid', 'birthday', 'address', 'introduction']) + ->whereUserid($userid) + ->first(); + + $birthday = null; + $address = null; + $introduction = null; + + if ($user) { + $birthday = $user->birthday; + $address = $user->address; + $introduction = $user->introduction; + } else { + $deleted = UserDelete::whereUserid($userid)->first(); + if (empty($deleted) || empty($deleted->cache)) { + return Base::retError('会员不存在'); + } + $birthday = $deleted->cache['birthday'] ?? null; + $address = $deleted->cache['address'] ?? null; + $introduction = $deleted->cache['introduction'] ?? null; + } + + $tagMeta = UserTag::listWithMeta($userid, $user); + + $data = [ + 'userid' => $userid, + 'birthday' => $birthday, + 'address' => $address, + 'introduction' => $introduction, + 'personal_tags' => $tagMeta['top'], + 'personal_tags_total' => $tagMeta['total'], + ]; + + return Base::retSuccess('success', $data); + } + /** * @api {get} api/users/lists 会员列表(限管理员) * diff --git a/app/Models/User.php b/app/Models/User.php index f02d19206..4c3463af3 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -92,7 +92,7 @@ class User extends AbstractModel public static $defaultAvatarMode = 'auto'; // 基本信息的字段 - public static $basicField = ['userid', 'email', 'nickname', 'profession', 'birthday', 'address', 'introduction', 'department', 'userimg', 'bot', 'az', 'pinyin', 'line_at', 'disable_at']; + public static $basicField = ['userid', 'email', 'nickname', 'profession', 'department', 'userimg', 'bot', 'az', 'pinyin', 'line_at', 'disable_at']; /** * 昵称 diff --git a/resources/assets/js/pages/manage/components/UserDetail.vue b/resources/assets/js/pages/manage/components/UserDetail.vue index 06fe95afc..bdf659c27 100755 --- a/resources/assets/js/pages/manage/components/UserDetail.vue +++ b/resources/assets/js/pages/manage/components/UserDetail.vue @@ -198,10 +198,14 @@ export default { return; } this.$store.dispatch("showSpinner", 600); - this.$store - .dispatch("getUserData", userid) - .then((user) => { - this.userData = user; + Promise.all([ + this.$store.dispatch("getUserData", userid).catch(() => null), + this.$store.dispatch("getUserExtra", userid).catch(() => null), + ]) + .then(([user, extra]) => { + const baseData = $A.isJson(user) ? user : {}; + const extraData = $A.isJson(extra) ? extra : {}; + this.userData = Object.assign({}, baseData, extraData); this.ensureTagDefaults(); this.showModal = true; this.loadCommonDialogCount(); diff --git a/resources/assets/js/pages/manage/components/UserTagsModal.vue b/resources/assets/js/pages/manage/components/UserTagsModal.vue index 0fb8d249c..3cc7bd426 100644 --- a/resources/assets/js/pages/manage/components/UserTagsModal.vue +++ b/resources/assets/js/pages/manage/components/UserTagsModal.vue @@ -93,7 +93,7 @@ @@ -213,17 +213,14 @@ export default { }, emitUpdated(payload) { this.$emit('updated', payload); - if (this.userid === this.$store.state.userInfo.userid) { - const info = Object.assign({}, this.$store.state.userInfo, { - personal_tags: payload.top, - personal_tags_total: payload.total - }); - this.$store.dispatch('saveUserInfoBase', info); - } - this.$store.dispatch('saveUserBasic', { + this.$store.dispatch('saveUserExtra', { userid: this.userid, - personal_tags: payload.top, - personal_tags_total: payload.total + data: { + personal_tags: Array.isArray(payload?.top) ? payload.top : [], + personal_tags_total: typeof payload?.total === 'number' + ? payload.total + : (Array.isArray(payload?.top) ? payload.top.length : 0) + } }); }, handleAdd() { @@ -418,10 +415,15 @@ export default { padding: 6px 12px; border-radius: 12px; font-size: 13px; - line-height: 1; user-select: none; background-color: #f5f5f5; color: #606266; + line-height: 14px; + height: 26px; + max-width: 160px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; &.is-recognized { color: #67c23a; } diff --git a/resources/assets/js/pages/manage/setting/personal.vue b/resources/assets/js/pages/manage/setting/personal.vue index dd6ca90e2..96ea7abc1 100644 --- a/resources/assets/js/pages/manage/setting/personal.vue +++ b/resources/assets/js/pages/manage/setting/personal.vue @@ -89,16 +89,20 @@ export default { loadIng: 0, formData: { + // 基本信息 userimg: '', email: '', tel: '', nickname: '', profession: '', + // 拓展信息 生日、地址、个人简介 birthday: '', address: '', introduction: '' }, + extraInfo: {}, + ruleData: { email: [ {required: true, message: this.$L('请输入邮箱地址!'), trigger: 'change'}, @@ -120,6 +124,7 @@ export default { }, mounted() { this.initData(); + this.loadUserExtra(); }, computed: { ...mapState(['userInfo', 'formOptions']), @@ -131,27 +136,60 @@ export default { watch: { userInfo() { this.initData(); + this.loadUserExtra(); } }, methods: { initData() { + const extra = this.extraInfo || {}; this.$set(this.formData, 'userimg', $A.strExists(this.userInfo.userimg, '/avatar') ? '' : this.userInfo.userimg); this.$set(this.formData, 'email', this.userInfo.email); this.$set(this.formData, 'tel', this.userInfo.tel); this.$set(this.formData, 'nickname', typeof this.userInfo.nickname_original !== "undefined" ? this.userInfo.nickname_original : this.userInfo.nickname); this.$set(this.formData, 'profession', this.userInfo.profession); - this.$set(this.formData, 'birthday', this.userInfo.birthday || ''); - this.$set(this.formData, 'address', this.userInfo.address || ''); - this.$set(this.formData, 'introduction', this.userInfo.introduction || ''); + this.$set(this.formData, 'birthday', extra.birthday || ''); + this.$set(this.formData, 'address', extra.address || ''); + this.$set(this.formData, 'introduction', extra.introduction || ''); this.formData_bak = $A.cloneJSON(this.formData); this.syncPersonalTags(); }, + loadUserExtra(force = false) { + const userid = this.userInfo?.userid; + if (!userid) { + this.applyExtraInfo({}); + return; + } + const payload = force ? {userid, force: true} : userid; + this.$store.dispatch("getUserExtra", payload) + .then((data) => { + if ($A.isJson(data)) { + this.applyExtraInfo(data); + } + }) + .catch(() => { + if (!this.extraInfo || Object.keys(this.extraInfo).length === 0) { + this.applyExtraInfo({}); + } + }); + }, + + applyExtraInfo(extra) { + const info = $A.isJson(extra) ? extra : {}; + this.extraInfo = info; + this.$set(this.formData, 'birthday', info.birthday || ''); + this.$set(this.formData, 'address', info.address || ''); + this.$set(this.formData, 'introduction', info.introduction || ''); + this.syncPersonalTags(); + this.formData_bak = $A.cloneJSON(this.formData); + }, + syncPersonalTags() { - const tags = Array.isArray(this.userInfo.personal_tags) ? this.userInfo.personal_tags : []; + const extra = this.extraInfo || {}; + const tags = Array.isArray(extra.personal_tags) ? extra.personal_tags : []; this.personalTags = tags.slice(0, 10); - this.personalTagTotal = typeof this.userInfo.personal_tags_total === 'number' - ? this.userInfo.personal_tags_total + this.personalTagTotal = typeof extra.personal_tags_total === 'number' + ? extra.personal_tags_total : this.personalTags.length; }, @@ -166,7 +204,24 @@ export default { data, }).then(() => { $A.messageSuccess('修改成功'); - this.$store.dispatch('getUserInfo').catch(() => {}); + const userid = this.userInfo?.userid; + const extraPayload = { + birthday: data.birthday || '', + address: data.address || '', + introduction: data.introduction || '' + }; + if (userid) { + this.$store.dispatch('saveUserExtra', { + userid, + data: extraPayload + }); + } + this.applyExtraInfo(Object.assign({}, this.extraInfo, extraPayload)); + this.$store.dispatch('getUserInfo') + .catch(() => {}) + .finally(() => { + this.loadUserExtra(true); + }); }).catch(({msg}) => { $A.modalError(msg); }).finally(_ => { @@ -190,6 +245,10 @@ export default { onTagsUpdated({top, total}) { this.personalTags = Array.isArray(top) ? top : []; this.personalTagTotal = typeof total === 'number' ? total : this.personalTags.length; + this.extraInfo = Object.assign({}, this.extraInfo, { + personal_tags: this.personalTags, + personal_tags_total: this.personalTagTotal + }); } } } @@ -205,13 +264,18 @@ export default { .tag-pill { cursor: pointer; - padding: 5px 12px; + padding: 6px 12px; border-radius: 12px; font-size: 13px; - line-height: 1; user-select: none; background-color: #f5f5f5; color: #606266; + line-height: 14px; + height: 26px; + max-width: 160px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; &.is-recognized { color: #67c23a; } diff --git a/resources/assets/js/store/actions.js b/resources/assets/js/store/actions.js index 8387e0d8f..bab7df919 100644 --- a/resources/assets/js/store/actions.js +++ b/resources/assets/js/store/actions.js @@ -714,6 +714,64 @@ export default { }); }, + /** + * 获取会员扩展信息 + * @param state + * @param dispatch + * @param userid + * @returns {Promise} + */ + getUserExtra({state, dispatch}, userid) { + return new Promise(async (resolve, reject) => { + if (!userid) { + reject({msg: "userid missing"}); + return; + } + const cacheMap = state.cacheUserExtra || {}; + const cacheItem = cacheMap[`${userid}`]; + const now = Date.now(); + if (cacheItem && cacheItem.data && (now - cacheItem.updatedAt) < 30000) { + resolve(cacheItem.data); + return; + } + try { + const {data} = await dispatch("call", { + url: 'users/extra', + data: {userid}, + }); + state.cacheUserExtra = Object.assign({}, cacheMap, { + [`${userid}`]: { + data, + updatedAt: Date.now() + } + }); + resolve(data); + } catch (error) { + reject(error); + } + }); + }, + + /** + * 缓存会员扩展信息 + * @param state + * @param payload {userid, data} + */ + saveUserExtra({state}, payload) { + const userid = $A.runNum(payload?.userid); + if (!userid || !$A.isJson(payload?.data)) { + return; + } + const cacheMap = state.cacheUserExtra || {}; + const current = cacheMap[`${userid}`]?.data || {}; + state.cacheUserExtra = Object.assign({}, cacheMap, { + [`${userid}`]: { + data: Object.assign({}, current, payload.data), + updatedAt: Date.now() + } + }); + }, + /** * 更新会员信息 * @param state @@ -856,7 +914,7 @@ export default { } catch (_) {} } if (tempUser) { - resolve(tempUser); + resolve($A.cloneJSON(tempUser)); } else { reject(); } diff --git a/resources/assets/js/store/state.js b/resources/assets/js/store/state.js index 6a0737242..9ddb9e40e 100644 --- a/resources/assets/js/store/state.js +++ b/resources/assets/js/store/state.js @@ -90,6 +90,7 @@ export default { // User cacheUserWait: [], cacheUserBasic: [], + cacheUserExtra: {}, // 日历 cacheCalendarView: null, diff --git a/resources/assets/sass/pages/components/user-detail.scss b/resources/assets/sass/pages/components/user-detail.scss index eaafe2dc6..8b55d9250 100755 --- a/resources/assets/sass/pages/components/user-detail.scss +++ b/resources/assets/sass/pages/components/user-detail.scss @@ -163,7 +163,6 @@ display: flex; flex-wrap: wrap; gap: 8px; - justify-content: center; position: relative; .recognition-total { padding-left: 8px;