feat:增加个人中心:50% 支持右侧的社交绑定

pull/76/MERGE
YunaiV 2025-04-20 18:52:21 +08:00
parent d030a73beb
commit a6dcd8c200
5 changed files with 190 additions and 11 deletions

View File

@ -1,6 +1,7 @@
import { requestClient } from '#/api/request';
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace SystemSocialUserApi {
/** 社交用户信息 */
export interface SystemSocialUser {
@ -17,6 +18,19 @@ export namespace SystemSocialUserApi {
createTime?: Date;
updateTime?: Date;
}
/** 社交绑定请求 */
export interface SocialUserBindReqVO {
type: number;
code: string;
state: string;
}
/** 取消社交绑定请求 */
export interface SocialUserUnbindReqVO {
type: number;
openid: string;
}
}
/** 查询社交用户列表 */
@ -30,3 +44,18 @@ export function getSocialUserPage(params: PageParam) {
export function getSocialUser(id: number) {
return requestClient.get<SystemSocialUserApi.SystemSocialUser>(`/system/social-user/get?id=${id}`);
}
/** 社交绑定,使用 code 授权码 */
export function socialBind(data: SystemSocialUserApi.SocialUserBindReqVO) {
return requestClient.post<boolean>('/system/social-user/bind', data);
}
/** 取消社交绑定 */
export function socialUnbind(data: SystemSocialUserApi.SocialUserUnbindReqVO) {
return requestClient.delete<boolean>('/system/social-user/unbind', { data });
}
/** 获得绑定社交用户列表 */
export function getBindSocialUserList() {
return requestClient.get<SystemSocialUserApi.SystemSocialUser[]>('/system/social-user/get-bind-list');
}

View File

@ -1,12 +1,6 @@
import { requestClient } from '#/api/request';
export namespace SystemUserProfileApi {
/** 社交用户信息 */
export interface SocialUser {
type: number;
openid: string;
}
/** 用户个人中心信息 */
export interface UserProfileRespVO {
id: number;
@ -22,7 +16,6 @@ export namespace SystemUserProfileApi {
roles: any[];
dept: any;
posts: any[];
socialUsers: SocialUser[];
}
/** 更新密码请求 */

View File

@ -6,13 +6,15 @@ import { Page } from '@vben/common-ui';
import ProfileUser from './modules/profile-user.vue';
import BaseInfo from './modules/base-info.vue';
import ResetPwd from './modules/reset-pwd.vue';
import UserSocial from './modules/user-social.vue';
import { onMounted, ref } from 'vue';
import { getUserProfile } from '#/api/system/user/profile';
import { useAuthStore } from '#/store';
const authStore = useAuthStore();
const activeName = ref('basicInfo');
// const activeName = ref('basicInfo');
const activeName = ref('userSocial');
/** 加载个人信息 */
const profile = ref<SystemUserProfileApi.UserProfileRespVO>();
@ -51,7 +53,7 @@ onMounted(loadProfile);
<ResetPwd />
</Tabs.TabPane>
<Tabs.TabPane key="userSocial" tab="社交绑定">
<!-- <UserSocial :profile="profile" /> -->
<UserSocial />
</Tabs.TabPane>
</Tabs>
</Card>

View File

@ -0,0 +1,153 @@
<script setup lang="tsx">
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemSocialUserApi } from '#/api/system/social/user';
import { Button, Card, Image, message, Modal } from 'ant-design-vue';
import { computed, ref } from 'vue';
import { $t } from '#/locales';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getBindSocialUserList, socialUnbind } from '#/api/system/social/user';
import { DICT_TYPE, getDictLabel } from '#/utils/dict';
import { SystemUserSocialTypeEnum } from '#/utils/constants';
/** 已经绑定的平台 */
const bindList = ref<SystemSocialUserApi.SystemSocialUser[]>([]);
const allBindList = computed<any[]>(() => {
return Object.values(SystemUserSocialTypeEnum).map((social) => {
const socialUser = bindList.value.find(item => item.type === social.type);
return {
...social,
socialUser,
};
});
});
function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'type',
title: '绑定平台',
minWidth: 100,
cellRender: { name: 'CellDict', props: { type: DICT_TYPE.SYSTEM_SOCIAL_TYPE } },
},
{
field: 'openid',
title: '标识',
minWidth: 180,
},
{
field: 'nickname',
title: '昵称',
minWidth: 180,
},
{
field: 'operation',
title: '操作',
minWidth: 80,
align: 'center',
fixed: 'right',
slots: {
default: ({ row }: { row: SystemSocialUserApi.SystemSocialUser }) => {
return (
<Button onClick={() => onUnbind(row)} type="link">解绑</Button>
);
},
},
},
];
}
const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: {
columns: useGridColumns(),
minHeight: 0,
keepSource: true,
proxyConfig: {
ajax: {
query: async () => {
bindList.value = await getBindSocialUserList();
return bindList.value;
},
},
},
rowConfig: {
keyField: 'id',
},
pagerConfig: {
enabled: false,
},
toolbarConfig: {
enabled: false,
},
} as VxeTableGridOptions<SystemSocialUserApi.SystemSocialUser>,
});
/** 解绑账号 */
function onUnbind(row: SystemSocialUserApi.SystemSocialUser) {
Modal.confirm({
type: 'warning',
title: '提示',
content: `确定解绑[${getDictLabel(DICT_TYPE.SYSTEM_SOCIAL_TYPE, row.type)}]平台的[${row.openid}]账号吗?`,
async onOk() {
await socialUnbind({ type: row.type, openid: row.openid });
//
message.success({
content: $t('ui.actionMessage.operationSuccess'),
key: 'action_process_msg',
});
await gridApi.reload();
},
});
}
/** 绑定账号 */
function onBind(bind: any) {
alert('待实现!');
}
</script>
<template>
<div class="flex flex-col">
<Grid />
<div class="pb-3">
<div class="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-3 gap-2 px-2">
<Card v-for="item in allBindList" :key="item.type" class="!mb-2">
<div class="flex w-full items-center gap-4">
<Image
:src="item.img"
:width="40"
:height="40"
:alt="item.title"
:preview="false"
/>
<div class="flex flex-1 items-center justify-between">
<div class="flex flex-col">
<h4 class="mb-[4px] text-[14px] text-black/85 dark:text-white/85">
{{ getDictLabel(DICT_TYPE.SYSTEM_SOCIAL_TYPE, item.type) }}
</h4>
<span class="text-black/45 dark:text-white/45">
<template v-if="item.socialUser">
{{ item.socialUser?.nickname || item.socialUser?.openid }}
</template>
<template v-else>
绑定{{ getDictLabel(DICT_TYPE.SYSTEM_SOCIAL_TYPE, item.type) }}账号
</template>
</span>
</div>
<Button
:disabled="item.socialUser"
size="small"
type="link"
@click="onBind(item)"
>
{{ item.socialUser ? '已绑定' : '绑定' }}
</Button>
</div>
</div>
</Card>
</div>
</div>
</div>
</template>

View File

@ -182,7 +182,9 @@ export function useGridFormSchema(): VbenFormSchema[] {
/** 列表的字段 */
const tenantPackageList = await getTenantPackageList();
export function useGridColumns<T = SystemTenantApi.SystemTenant>(onActionClick: OnActionClickFn<T>): VxeTableGridOptions['columns'] {
export function useGridColumns<T = SystemTenantApi.SystemTenant>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
return [
{
field: 'id',