diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 1207de77d..33f70c9d1 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); } @@ -832,16 +829,76 @@ 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|Number[]} [userid] 会员ID(多个格式:jsonArray,一次最多50个,不传默认为当前登录会员) + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object[]} data 返回数据 + */ + public function extra() + { + $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(); + } + // + $userid = Request::input('userid'); + $array = Base::json2array($userid); + if (empty($array) && $userid) { + $array[] = $userid; + } + if (empty($array) && $viewer) { + $array[] = $viewer->userid; + } + if (empty($array)) { + return Base::retError('参数错误'); + } + if (count($array) > 50) { + return Base::retError('一次最多只能获取50条数据'); + } + // + $userids = array_values(array_unique(array_filter(array_map(function ($id) { + $id = intval($id); + return $id > 0 ? $id : null; + }, $array)))); + if (empty($userids)) { + return Base::retError('参数错误'); + } + // + $fields = ['userid', 'birthday', 'address', 'introduction']; + $retArray = []; + foreach ($userids as $id) { + $user = User::whereUserid($id)->select($fields)->first(); + if (empty($user)) { + continue; + } + $extended = Arr::only($user->toArray(), $fields); + $tagMeta = UserTag::listWithMeta($id, $viewer); + $extended['personal_tags'] = $tagMeta['top']; + $extended['personal_tags_total'] = $tagMeta['total']; + $retArray[] = $extended; + } + return Base::retSuccess('success', $retArray); + } + /** * @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..c59e4e5c0 100755 --- a/resources/assets/js/pages/manage/components/UserDetail.vue +++ b/resources/assets/js/pages/manage/components/UserDetail.vue @@ -136,6 +136,7 @@ export default { userid: 0, }, showModal: false, + extraLoading: 0, tagModalVisible: false, commonDialog: { userid: null, @@ -203,6 +204,7 @@ export default { .then((user) => { this.userData = user; this.ensureTagDefaults(); + this.loadUserExtra(userid); this.showModal = true; this.loadCommonDialogCount(); }) @@ -414,6 +416,38 @@ export default { } }); }, + + loadUserExtra(userid) { + const targetId = Number(userid); + if (!targetId) { + return; + } + this.extraLoading += 1; + this.$store + .dispatch("getUserExtra", { + userid: targetId, + checkAuth: false, + }) + .then((extra) => { + if (extra) { + this.applyExtraData(extra, targetId); + } + }) + .catch((error) => { + console.warn(error); + }) + .finally(() => { + this.extraLoading = Math.max(0, this.extraLoading - 1); + }); + }, + + applyExtraData(extra, targetId) { + if (!extra || this.userData.userid !== targetId) { + return; + } + this.userData = Object.assign({}, this.userData, extra); + this.ensureTagDefaults(); + }, }, }; - \ No newline at end of file + diff --git a/resources/assets/js/pages/manage/setting/personal.vue b/resources/assets/js/pages/manage/setting/personal.vue index dd6ca90e2..b9a9bfd4a 100644 --- a/resources/assets/js/pages/manage/setting/personal.vue +++ b/resources/assets/js/pages/manage/setting/personal.vue @@ -116,10 +116,13 @@ export default { tagModalVisible: false, personalTags: [], personalTagTotal: 0, + extraLoading: 0, + extraLoaded: false, } }, mounted() { this.initData(); + this.fetchExtraInfo(); }, computed: { ...mapState(['userInfo', 'formOptions']), @@ -131,6 +134,8 @@ export default { watch: { userInfo() { this.initData(); + this.extraLoaded = false; + this.fetchExtraInfo(); } }, methods: { @@ -147,11 +152,12 @@ export default { this.syncPersonalTags(); }, - syncPersonalTags() { - const tags = Array.isArray(this.userInfo.personal_tags) ? this.userInfo.personal_tags : []; + syncPersonalTags(source = null) { + const target = source || this.userInfo; + const tags = Array.isArray(target.personal_tags) ? target.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 target.personal_tags_total === 'number' + ? target.personal_tags_total : this.personalTags.length; }, @@ -190,6 +196,39 @@ export default { onTagsUpdated({top, total}) { this.personalTags = Array.isArray(top) ? top : []; this.personalTagTotal = typeof total === 'number' ? total : this.personalTags.length; + }, + + fetchExtraInfo() { + const userid = $A.runNum(this.userInfo.userid); + if (userid <= 0) { + return; + } + if (this.extraLoaded || this.extraLoading > 0) { + return; + } + this.extraLoading++; + this.$store + .dispatch("getUserExtra", {userid}) + .then((extra) => { + if (extra) { + this.applyExtraInfo(extra); + this.extraLoaded = true; + } + }) + .catch((error) => { + console.warn(error); + }) + .finally(() => { + this.extraLoading = Math.max(0, this.extraLoading - 1); + }); + }, + + applyExtraInfo(extra) { + this.$set(this.formData, 'birthday', extra?.birthday || ''); + this.$set(this.formData, 'address', extra?.address || ''); + this.$set(this.formData, 'introduction', extra?.introduction || ''); + this.syncPersonalTags(extra); + this.formData_bak = $A.cloneJSON(this.formData); } } } diff --git a/resources/assets/js/store/actions.js b/resources/assets/js/store/actions.js index 8387e0d8f..fe0fb924e 100644 --- a/resources/assets/js/store/actions.js +++ b/resources/assets/js/store/actions.js @@ -863,6 +863,71 @@ export default { }) }, + /** + * 获取会员拓展信息(生日、地址、简介、标签等) + * @param state + * @param dispatch + * @param commit + * @param payload {userid|{userid, force, checkAuth}} + * @returns {Promise} + */ + getUserExtra({state, dispatch, commit}, payload) { + return new Promise(async (resolve, reject) => { + const options = (payload && typeof payload === 'object' && !Array.isArray(payload)) + ? payload + : {userid: payload}; + let {userid, force = false, checkAuth = false} = options; + const original = userid; + const ids = (Array.isArray(userid) ? userid : [userid]) + .map(id => Number(id)) + .filter(id => Number.isFinite(id) && id > 0); + if (ids.length === 0) { + resolve(Array.isArray(original) ? [] : null); + return; + } + const cachedBefore = ids + .map(id => state.userExtraCache[String(id)]) + .filter(item => typeof item === 'object' && item !== null); + const missing = ids.filter(id => force || !state.userExtraCache[String(id)]); + if (missing.length > 0) { + try { + const {data} = await dispatch("call", { + url: 'users/extra', + data: {userid: missing}, + checkAuth + }); + const extraList = Array.isArray(data) ? data : []; + extraList.forEach(item => { + const id = Number(item?.userid); + if (!Number.isFinite(id) || id <= 0) { + return; + } + const normalized = Object.assign({}, item, {userid: id}); + commit('user/extra/save', {userid: id, data: normalized}); + const existing = state.cacheUserBasic.find(entry => entry.userid == id); + if (existing) { + dispatch("saveUserBasic", Object.assign({}, existing, normalized)); + } + }); + } catch (error) { + console.warn(error); + if (cachedBefore.length === 0) { + reject(error); + return; + } + } + } + const results = ids + .map(id => state.userExtraCache[String(id)]) + .filter(item => typeof item === 'object' && item !== null); + if (Array.isArray(original)) { + resolve(results); + } else { + resolve(results[0] || null); + } + }); + }, + /** * 保存用户基础信息 * @param commit @@ -1099,6 +1164,7 @@ export default { // Reset auth exception flag after successful login flow state.ajaxAuthException = null + state.userExtraCache = {} resolve() }); diff --git a/resources/assets/js/store/mutations.js b/resources/assets/js/store/mutations.js index decdd2347..47cee9009 100644 --- a/resources/assets/js/store/mutations.js +++ b/resources/assets/js/store/mutations.js @@ -30,6 +30,27 @@ export default { $A.IDBSave("cacheUserBasic", state.cacheUserBasic, 600) }, + 'user/extra/save': function(state, {userid, data}) { + const id = Number(userid); + if (!Number.isFinite(id) || id <= 0 || typeof data !== 'object' || data === null) { + return; + } + const key = String(id); + const cache = Object.assign({}, state.userExtraCache); + cache[key] = Object.assign({}, data, {userid: id}); + state.userExtraCache = cache; + }, + + 'user/extra/clear': function(state, userid) { + if (typeof userid === 'number' || typeof userid === 'string') { + const cache = Object.assign({}, state.userExtraCache); + delete cache[String(userid)]; + state.userExtraCache = cache; + } else { + state.userExtraCache = {}; + } + }, + // 共同群聊 'common/dialog/count/save': function(state, {userid, total, updatedAt = Date.now()}) { if (!userid) { diff --git a/resources/assets/js/store/state.js b/resources/assets/js/store/state.js index 6a0737242..7325de45f 100644 --- a/resources/assets/js/store/state.js +++ b/resources/assets/js/store/state.js @@ -90,6 +90,7 @@ export default { // User cacheUserWait: [], cacheUserBasic: [], + userExtraCache: {}, // 日历 cacheCalendarView: null,