feat(@vben/web-antd): 重构个人中心用户资料与社交绑定页面
- 用户资料页改为卡片式布局,头像居中展示角色标签 - 社交绑定页替换表格为卡片列表,支持已绑定详情展开 - 新增微信小程序社交类型枚举,小程序端绑定入口置灰提示 - 头像上传兼容 server/client 两种模式的返回值 - 社交绑定列表增加类型安全(SocialBindItem interface) - 隐藏暂不支持的钉钉和企业微信绑定入口 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@ import { IconifyIcon } from '@vben/icons';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { Descriptions, DescriptionsItem, Tooltip } from 'ant-design-vue';
|
||||
import { Divider, Tag, Tooltip } from 'ant-design-vue';
|
||||
|
||||
import { updateUserProfile } from '#/api/system/user/profile';
|
||||
import { CropperAvatar } from '#/components/cropper';
|
||||
@@ -36,113 +36,155 @@ async function handelUpload({
|
||||
const { httpRequest } = useUpload();
|
||||
// 将 Blob 转换为 File
|
||||
const fileObj = new File([file], filename, { type: file.type });
|
||||
const avatar = await httpRequest(fileObj);
|
||||
const result = await httpRequest(fileObj);
|
||||
// 2. 更新用户头像
|
||||
await updateUserProfile({ avatar });
|
||||
const avatarUrl = typeof result === 'string' ? result : result?.url;
|
||||
if (avatarUrl) {
|
||||
await updateUserProfile({ avatar: avatarUrl });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="profile">
|
||||
<div class="flex flex-col items-center">
|
||||
<div v-if="profile" class="profile-user">
|
||||
<!-- 头像和基本信息 -->
|
||||
<div class="flex flex-col items-center pb-4">
|
||||
<Tooltip title="点击上传头像">
|
||||
<CropperAvatar
|
||||
:show-btn="false"
|
||||
:upload-api="handelUpload"
|
||||
:value="avatar"
|
||||
:width="120"
|
||||
:width="100"
|
||||
@change="emit('success')"
|
||||
/>
|
||||
</Tooltip>
|
||||
<h3 class="mt-3 text-lg font-semibold">
|
||||
{{ profile.nickname || profile.username }}
|
||||
</h3>
|
||||
<div class="mt-1 flex flex-wrap justify-center gap-1">
|
||||
<Tag
|
||||
v-for="role in profile.roles"
|
||||
:key="role.id"
|
||||
color="blue"
|
||||
>
|
||||
{{ role.name }}
|
||||
</Tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-8">
|
||||
<Descriptions :column="2">
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ant-design:user-outlined" class="mr-1" />
|
||||
用户账号
|
||||
</div>
|
||||
</template>
|
||||
{{ profile.username }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon
|
||||
icon="ant-design:user-switch-outlined"
|
||||
class="mr-1"
|
||||
/>
|
||||
所属角色
|
||||
</div>
|
||||
</template>
|
||||
{{ profile.roles.map((role) => role.name).join(',') }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ant-design:phone-outlined" class="mr-1" />
|
||||
手机号码
|
||||
</div>
|
||||
</template>
|
||||
{{ profile.mobile }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ant-design:mail-outlined" class="mr-1" />
|
||||
用户邮箱
|
||||
</div>
|
||||
</template>
|
||||
{{ profile.email }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ant-design:team-outlined" class="mr-1" />
|
||||
所属部门
|
||||
</div>
|
||||
</template>
|
||||
{{ profile.dept?.name }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon
|
||||
icon="ant-design:usergroup-add-outlined"
|
||||
class="mr-1"
|
||||
/>
|
||||
所属岗位
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<Divider class="!my-3" />
|
||||
|
||||
<!-- 详细信息列表 -->
|
||||
<div class="space-y-3 px-2">
|
||||
<div class="profile-item">
|
||||
<div class="profile-item-label">
|
||||
<IconifyIcon icon="ant-design:user-outlined" class="mr-2 text-base" />
|
||||
<span>用户账号</span>
|
||||
</div>
|
||||
<span class="profile-item-value">{{ profile.username }}</span>
|
||||
</div>
|
||||
|
||||
<div class="profile-item">
|
||||
<div class="profile-item-label">
|
||||
<IconifyIcon icon="ant-design:phone-outlined" class="mr-2 text-base" />
|
||||
<span>手机号码</span>
|
||||
</div>
|
||||
<span class="profile-item-value">{{ profile.mobile || '-' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="profile-item">
|
||||
<div class="profile-item-label">
|
||||
<IconifyIcon icon="ant-design:mail-outlined" class="mr-2 text-base" />
|
||||
<span>用户邮箱</span>
|
||||
</div>
|
||||
<span class="profile-item-value">{{ profile.email || '-' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="profile-item">
|
||||
<div class="profile-item-label">
|
||||
<IconifyIcon icon="ant-design:team-outlined" class="mr-2 text-base" />
|
||||
<span>所属部门</span>
|
||||
</div>
|
||||
<span class="profile-item-value">{{ profile.dept?.name || '-' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="profile-item">
|
||||
<div class="profile-item-label">
|
||||
<IconifyIcon
|
||||
icon="ant-design:usergroup-add-outlined"
|
||||
class="mr-2 text-base"
|
||||
/>
|
||||
<span>所属岗位</span>
|
||||
</div>
|
||||
<span class="profile-item-value">
|
||||
{{
|
||||
profile.posts && profile.posts.length > 0
|
||||
? profile.posts.map((post) => post.name).join(',')
|
||||
: '-'
|
||||
}}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon
|
||||
icon="ant-design:clock-circle-outlined"
|
||||
class="mr-1"
|
||||
/>
|
||||
创建时间
|
||||
</div>
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="profile-item">
|
||||
<div class="profile-item-label">
|
||||
<IconifyIcon
|
||||
icon="ant-design:clock-circle-outlined"
|
||||
class="mr-2 text-base"
|
||||
/>
|
||||
<span>创建时间</span>
|
||||
</div>
|
||||
<span class="profile-item-value">
|
||||
{{ formatDateTime(profile.createTime) }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ant-design:login-outlined" class="mr-1" />
|
||||
登录时间
|
||||
</div>
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="profile-item">
|
||||
<div class="profile-item-label">
|
||||
<IconifyIcon icon="ant-design:login-outlined" class="mr-2 text-base" />
|
||||
<span>登录时间</span>
|
||||
</div>
|
||||
<span class="profile-item-value">
|
||||
{{ formatDateTime(profile.loginDate) }}
|
||||
</DescriptionsItem>
|
||||
</Descriptions>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.profile-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 4px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.dark .profile-item {
|
||||
border-bottom-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.profile-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.profile-item-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dark .profile-item-label {
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
}
|
||||
|
||||
.profile-item-value {
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dark .profile-item-value {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="tsx">
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
<script setup lang="ts">
|
||||
import type { SystemSocialUserApi } from '#/api/system/social/user';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
@@ -8,125 +7,80 @@ import { useRoute } from 'vue-router';
|
||||
import { confirm } from '@vben/common-ui';
|
||||
import { DICT_TYPE, SystemUserSocialTypeEnum } from '@vben/constants';
|
||||
import { getDictLabel } from '@vben/hooks';
|
||||
import { getUrlValue } from '@vben/utils';
|
||||
import { $t } from '@vben/locales';
|
||||
import { formatDateTime, getUrlValue } from '@vben/utils';
|
||||
|
||||
import { Button, Card, Image, message } from 'ant-design-vue';
|
||||
import { Avatar, Button, Image, message, Tag, Tooltip } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { socialAuthRedirect } from '#/api/core/auth';
|
||||
import {
|
||||
getBindSocialUserList,
|
||||
socialBind,
|
||||
socialUnbind,
|
||||
} from '#/api/system/social/user';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:activeName', v: string): void;
|
||||
}>();
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
/** 已经绑定的平台 */
|
||||
const bindList = ref<SystemSocialUserApi.SocialUser[]>([]);
|
||||
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.SocialUser }) => {
|
||||
return (
|
||||
<Button onClick={() => onUnbind(row)} type="link">
|
||||
解绑
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
/** 暂不支持钉钉和企业微信,后续开放时移除此过滤 */
|
||||
const HIDDEN_SOCIAL_TYPES = [
|
||||
SystemUserSocialTypeEnum.DINGTALK.type,
|
||||
SystemUserSocialTypeEnum.WECHAT_ENTERPRISE.type,
|
||||
];
|
||||
|
||||
interface SocialBindItem {
|
||||
title: string;
|
||||
type: number;
|
||||
source: string;
|
||||
img: string;
|
||||
socialUser?: SystemSocialUserApi.SocialUser;
|
||||
}
|
||||
|
||||
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.SocialUser>,
|
||||
const allBindList = computed<SocialBindItem[]>(() => {
|
||||
return Object.values(SystemUserSocialTypeEnum)
|
||||
.filter((social) => !HIDDEN_SOCIAL_TYPES.includes(social.type))
|
||||
.map((social) => {
|
||||
const socialUser = bindList.value.find(
|
||||
(item) => item.type === social.type,
|
||||
);
|
||||
return {
|
||||
...social,
|
||||
socialUser,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
/** 加载绑定列表 */
|
||||
async function loadBindList() {
|
||||
bindList.value = await getBindSocialUserList();
|
||||
}
|
||||
|
||||
/** 解绑账号 */
|
||||
function onUnbind(row: SystemSocialUserApi.SocialUser) {
|
||||
function onUnbind(item: SocialBindItem) {
|
||||
const socialUser = item.socialUser!;
|
||||
confirm({
|
||||
content: `确定解绑[${getDictLabel(DICT_TYPE.SYSTEM_SOCIAL_TYPE, row.type)}]平台的[${row.openid}]账号吗?`,
|
||||
content: `确定解绑[${getDictLabel(DICT_TYPE.SYSTEM_SOCIAL_TYPE, socialUser.type)}]平台的[${socialUser.nickname || socialUser.openid}]账号吗?`,
|
||||
}).then(async () => {
|
||||
await socialUnbind({ type: row.type, openid: row.openid });
|
||||
// 提示成功
|
||||
await socialUnbind({ type: socialUser.type, openid: socialUser.openid });
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
await gridApi.reload();
|
||||
await loadBindList();
|
||||
});
|
||||
}
|
||||
|
||||
/** 绑定账号(跳转授权页面) */
|
||||
async function onBind(bind: any) {
|
||||
async function onBind(bind: SocialBindItem) {
|
||||
const type = bind.type;
|
||||
if (type <= 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 计算 redirectUri
|
||||
// tricky: type 需要先 encode 一次,否则钉钉回调会丢失。配合 getUrlValue() 使用
|
||||
const redirectUri = `${location.origin}/profile?${encodeURIComponent(`type=${type}`)}`;
|
||||
|
||||
// 进行跳转
|
||||
window.location.href = await socialAuthRedirect(type, redirectUri);
|
||||
} catch (error) {
|
||||
console.error('社交绑定处理失败:', error);
|
||||
@@ -135,7 +89,6 @@ async function onBind(bind: any) {
|
||||
|
||||
/** 监听路由变化,处理社交绑定回调 */
|
||||
async function bindSocial() {
|
||||
// 社交绑定
|
||||
const type = Number(getUrlValue('type'));
|
||||
const code = route.query.code as string;
|
||||
const state = route.query.state as string;
|
||||
@@ -143,64 +96,138 @@ async function bindSocial() {
|
||||
return;
|
||||
}
|
||||
await socialBind({ type, code, state });
|
||||
// 提示成功
|
||||
message.success('绑定成功');
|
||||
emit('update:activeName', 'userSocial');
|
||||
await gridApi.reload();
|
||||
// 清理 URL 参数,避免刷新重复触发
|
||||
await loadBindList();
|
||||
window.history.replaceState({}, '', location.pathname);
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
bindSocial();
|
||||
onMounted(async () => {
|
||||
await loadBindList();
|
||||
await bindSocial();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col">
|
||||
<Grid />
|
||||
<div class="space-y-4 py-2">
|
||||
<div
|
||||
v-for="item in allBindList"
|
||||
:key="item.type"
|
||||
class="rounded-lg border border-solid"
|
||||
:class="
|
||||
item.socialUser
|
||||
? 'border-blue-200 dark:border-blue-800'
|
||||
: 'border-gray-200 dark:border-gray-700'
|
||||
"
|
||||
>
|
||||
<!-- 主行:图标 + 平台名 + 状态 + 操作 -->
|
||||
<div class="flex items-center gap-4 px-4 py-3">
|
||||
<!-- 平台图标 -->
|
||||
<div class="flex h-10 w-10 shrink-0 items-center justify-center">
|
||||
<Image
|
||||
:src="item.img"
|
||||
:width="36"
|
||||
:height="36"
|
||||
:alt="item.title"
|
||||
:preview="false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="pb-3">
|
||||
<!-- 平台名称 + 状态 -->
|
||||
<div class="flex min-w-0 flex-1 flex-col gap-0.5">
|
||||
<div class="flex items-center gap-2">
|
||||
<span
|
||||
class="text-sm font-medium text-black/85 dark:text-white/85"
|
||||
>
|
||||
{{ getDictLabel(DICT_TYPE.SYSTEM_SOCIAL_TYPE, item.type) }}
|
||||
</span>
|
||||
<Tag
|
||||
v-if="item.socialUser"
|
||||
color="blue"
|
||||
:bordered="false"
|
||||
class="!mr-0 !text-xs"
|
||||
>
|
||||
已绑定
|
||||
</Tag>
|
||||
<Tag v-else :bordered="false" class="!mr-0 !text-xs">未绑定</Tag>
|
||||
</div>
|
||||
<span class="text-xs text-black/45 dark:text-white/45">
|
||||
<template v-if="!item.socialUser">
|
||||
绑定后可使用该平台快速登录
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ item.socialUser.nickname || item.socialUser.openid }}
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="shrink-0">
|
||||
<template v-if="item.socialUser">
|
||||
<Button size="small" danger @click="onUnbind(item)">解绑</Button>
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
item.type === SystemUserSocialTypeEnum.WECHAT_MINI_APP.type
|
||||
"
|
||||
>
|
||||
<Tooltip title="请在微信小程序「我的」页面中完成绑定">
|
||||
<Button size="small" disabled>小程序端绑定</Button>
|
||||
</Tooltip>
|
||||
</template>
|
||||
<template v-else>
|
||||
<Button size="small" type="primary" ghost @click="onBind(item)">
|
||||
绑定
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已绑定:展示详细信息 -->
|
||||
<div
|
||||
class="grid grid-cols-1 gap-2 px-2 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-3"
|
||||
v-if="item.socialUser"
|
||||
class="border-t border-solid border-gray-100 bg-gray-50/50 px-4 py-3 dark:border-gray-700 dark:bg-white/[0.02]"
|
||||
>
|
||||
<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-1 text-sm 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
|
||||
class="grid grid-cols-1 gap-x-6 gap-y-2 text-xs sm:grid-cols-2 lg:grid-cols-3"
|
||||
>
|
||||
<!-- 头像 + 昵称 -->
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-black/45 dark:text-white/45">昵称:</span>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<Avatar
|
||||
v-if="item.socialUser.avatar"
|
||||
:src="item.socialUser.avatar"
|
||||
:size="20"
|
||||
/>
|
||||
<span class="text-black/85 dark:text-white/85">
|
||||
{{ item.socialUser.nickname || '-' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<!-- OpenID -->
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="shrink-0 text-black/45 dark:text-white/45">
|
||||
标识:
|
||||
</span>
|
||||
<span
|
||||
class="truncate text-black/85 dark:text-white/85"
|
||||
:title="item.socialUser.openid"
|
||||
>
|
||||
{{ item.socialUser.openid }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- 绑定时间 -->
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="shrink-0 text-black/45 dark:text-white/45">
|
||||
绑定时间:
|
||||
</span>
|
||||
<span class="text-black/85 dark:text-white/85">
|
||||
{{ formatDateTime(item.socialUser.createTime) || '-' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -56,4 +56,10 @@ export const SystemUserSocialTypeEnum = {
|
||||
source: 'wechat_enterprise',
|
||||
img: 'https://s1.ax1x.com/2022/05/22/OzMrzn.png',
|
||||
},
|
||||
WECHAT_MINI_APP: {
|
||||
title: '微信小程序',
|
||||
type: 34,
|
||||
source: 'wechat_mini_app',
|
||||
img: 'https://res.wx.qq.com/a/wx_fed/assets/res/NTI4MWU5.ico',
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user