diff --git a/shared/index.tsx b/shared/index.tsx index 34158902..e1af94d0 100644 --- a/shared/index.tsx +++ b/shared/index.tsx @@ -102,15 +102,22 @@ export { GroupPanelType, createGroup, createGroupInviteCode, + getAllGroupInviteCode, getGroupBasicInfo, quitGroup, applyGroupInvite, + deleteGroupInvite, modifyGroupField, createGroupPanel, modifyGroupPanel, deleteGroupPanel, } from './model/group'; -export type { GroupPanel, GroupInfo, GroupBasicInfo } from './model/group'; +export type { + GroupPanel, + GroupInfo, + GroupBasicInfo, + GroupInvite, +} from './model/group'; export { recallMessage, deleteMessage, @@ -177,6 +184,7 @@ export { shouldShowMessageTime, getMessageTimeDiff, formatShortTime, + formatFullTime, datetimeToNow, datetimeFromNow, } from './utils/date-helper'; diff --git a/shared/model/group.ts b/shared/model/group.ts index 9e16bf2f..2bee5f30 100644 --- a/shared/model/group.ts +++ b/shared/model/group.ts @@ -125,6 +125,25 @@ export async function createGroupInviteCode( return data; } +/** + * 获取群组所有邀请码 + * @param groupId 群组ID + */ +export async function getAllGroupInviteCode( + groupId: string +): Promise { + const { data } = await request.get( + '/api/group/invite/getAllGroupInviteCode', + { + params: { + groupId, + }, + } + ); + + return data; +} + /** * 根据邀请码查找邀请信息 * @param inviteCode 邀请码 @@ -151,6 +170,19 @@ export async function applyGroupInvite(inviteCode: string): Promise { }); } +/** + * 删除群组邀请 + */ +export async function deleteGroupInvite( + groupId: string, + inviteId: string +): Promise { + await request.post('/api/group/invite/deleteInvite', { + groupId, + inviteId, + }); +} + /** * 创建群组面板 */ diff --git a/shared/utils/date-helper.ts b/shared/utils/date-helper.ts index bea15b45..b4492fac 100644 --- a/shared/utils/date-helper.ts +++ b/shared/utils/date-helper.ts @@ -53,6 +53,13 @@ export function formatShortTime(date: dayjs.ConfigType): string { return dayjs(date).format('HH:mm'); } +/** + * 格式化为 小时:分钟 + */ +export function formatFullTime(date: dayjs.ConfigType): string { + return dayjs(date).format('YYYY-MM-DD HH:mm:ss'); +} + /** * 返回当前实例到现在的相对时间。 * @example diff --git a/web/src/components/modals/GroupDetail/Invite.tsx b/web/src/components/modals/GroupDetail/Invite.tsx new file mode 100644 index 00000000..896f4c6c --- /dev/null +++ b/web/src/components/modals/GroupDetail/Invite.tsx @@ -0,0 +1,100 @@ +import { LoadingSpinner } from '@/plugin/component'; +import React, { useCallback, useMemo } from 'react'; +import { + getAllGroupInviteCode, + t, + GroupInvite as GroupInviteType, + datetimeFromNow, + formatFullTime, + deleteGroupInvite, + useAsyncRefresh, +} from 'tailchat-shared'; +import { Button, Table, Tooltip } from 'antd'; +import type { ColumnType } from 'antd/lib/table'; +import { UserName } from '@/components/UserName'; +import { Loading } from '@/components/Loading'; +import { openReconfirmModalP } from '@/components/Modal'; + +export const GroupInvite: React.FC<{ + groupId: string; +}> = React.memo((props) => { + const groupId = props.groupId; + const { loading, value, refresh } = useAsyncRefresh(async () => { + const list = await getAllGroupInviteCode(groupId); + + return list; + }, [groupId]); + + const handleDeleteInvite = useCallback( + async (inviteId: string) => { + if (await openReconfirmModalP()) { + await deleteGroupInvite(groupId, inviteId); + await refresh(); + } + }, + [groupId, refresh] + ); + + const columns: ColumnType[] = useMemo( + () => [ + { + title: t('邀请码'), + dataIndex: 'code', + }, + { + title: t('创建时间'), + dataIndex: 'createdAt', + render: (date) => ( + + {datetimeFromNow(date)} + + ), + }, + { + title: t('过期时间'), + dataIndex: 'expiredAt', + render: (date) => ( + + {datetimeFromNow(date)} + + ), + }, + { + title: t('创建者'), + dataIndex: 'creator', + render: (userId) => , + }, + { + title: t('操作'), + dataIndex: '_id', + render: (id: string) => { + return ( +
+ +
+ ); + }, + }, + ], + [handleDeleteInvite] + ); + + return ( + + + + ); +}); +GroupInvite.displayName = 'GroupInvite'; diff --git a/web/src/components/modals/GroupDetail/index.tsx b/web/src/components/modals/GroupDetail/index.tsx index 4875c902..fd149fe8 100644 --- a/web/src/components/modals/GroupDetail/index.tsx +++ b/web/src/components/modals/GroupDetail/index.tsx @@ -8,6 +8,7 @@ import { GroupIdContextProvider } from '@/context/GroupIdContext'; import { pluginCustomPanel } from '@/plugin/common'; import React, { useCallback, useMemo } from 'react'; import { t } from 'tailchat-shared'; +import { GroupInvite } from './Invite'; import { GroupPanel } from './Panel'; import { GroupRole } from './Role'; import { GroupSummary } from './Summary'; @@ -44,6 +45,11 @@ export const GroupDetail: React.FC = React.memo((props) => { title: t('面板'), content: , }, + { + type: 'item', + title: t('邀请码'), + content: , + }, { type: 'item', title: t('身份组'), diff --git a/web/src/styles/antd/dark.less b/web/src/styles/antd/dark.less index 2675c4cb..57125239 100644 --- a/web/src/styles/antd/dark.less +++ b/web/src/styles/antd/dark.less @@ -301,4 +301,53 @@ background-color: #111b26; } } + + // 分页器 + .ant-pagination { + color: rgba(255, 255, 255, 0.85); + + .ant-pagination-prev,.ant-pagination-next { + .ant-pagination-item-link { + background-color: transparent; + border: 1px solid rgba(255, 255, 255, 0.2); + color: rgba(255, 255, 255, 0.85); + + &:hover { + border-color: #177ddc; + color: #177ddc; + } + } + } + + + .ant-pagination-disabled { + .ant-pagination-item-link { + border: 1px solid rgba(255, 255, 255, 0.2); + color: rgba(255, 255, 255, 0.3); + } + + &:hover .ant-pagination-item-link { + border: 1px solid rgba(255, 255, 255, 0.2); + color: rgba(255, 255, 255, 0.3); + } + } + + .ant-pagination-item { + background-color: transparent; + border: 1px solid rgba(255, 255, 255, 0.2); + + &:hover { + border-color: #177ddc; + color: #177ddc; + } + + > a { + color: rgba(255, 255, 255, 0.85); + } + } + + .ant-pagination-item-active { + border-color: #177ddc; + } + } } diff --git a/web/src/styles/antd/overwrite.less b/web/src/styles/antd/overwrite.less index fc64b8d9..5f7628ea 100644 --- a/web/src/styles/antd/overwrite.less +++ b/web/src/styles/antd/overwrite.less @@ -51,3 +51,9 @@ } } } + +.ant-pagination { + .ant-pagination-item-link > .anticon { + vertical-align: text-top; + } +}