* add manager change memberName and update inform UI

* change icon and some inform ui

* change for comment

* fix for comment
This commit is contained in:
gggaaallleee
2025-03-27 10:04:29 +08:00
committed by GitHub
parent 29a10c1389
commit cb29076e5b
25 changed files with 474 additions and 189 deletions

View File

@@ -24,5 +24,6 @@ export enum SendInformTemplateCodeEnum {
RESET_PASSWORD = 'RESET_PASSWORD',
BIND_NOTIFICATION = 'BIND_NOTIFICATION',
LACK_OF_POINTS = 'LACK_OF_POINTS',
CUSTOM = 'CUSTOM'
CUSTOM = 'CUSTOM',
MANAGE_RENAME = 'MANAGE_RENAME'
}

View File

@@ -19,6 +19,19 @@ export type SendInform2User = SendInformProps & {
export type UserInformSchema = {
_id: string;
userId: string;
teamId?: string;
time: Date;
level: `${InformLevelEnum}`;
title: string;
content: string;
read: boolean;
};
export type UserInformType = {
_id: string;
userId: string;
teamId?: string;
teamName?: string;
time: Date;
level: `${InformLevelEnum}`;
title: string;

View File

@@ -17,13 +17,15 @@ export const useEditTextarea = ({
tip,
placeholder = '',
canEmpty = true,
valueRule
valueRule,
rows = 10
}: {
title: string;
tip?: string;
placeholder?: string;
canEmpty?: boolean;
valueRule?: (val: string) => string | void;
rows?: number;
}) => {
const { t } = useTranslation();
const { isOpen, onOpen, onClose } = useDisclosure();
@@ -105,7 +107,7 @@ export const useEditTextarea = ({
placeholder={placeholder}
autoFocus
maxLength={maxLength}
rows={10}
rows={rows}
bg={'myGray.50'}
/>
</ModalBody>

View File

@@ -28,6 +28,7 @@
"exchange_success": "Redemption successful",
"expiration_time": "Expiration time",
"expired": "Expired",
"general_info": "General information",
"group": "Group",
"help_chatbot": "robot assistant",
"help_document": "Help documentation",
@@ -50,7 +51,6 @@
"password_update_error": "Exception when changing password",
"password_update_success": "Password changed successfully",
"pending_usage": "To be used",
"personal_information": "My info",
"phone_label": "Phone number",
"please_bind_notification_receiving_path": "Please bind the notification receiving method first",
"purchase_extra_package": "Upgrade",
@@ -60,6 +60,7 @@
"standard_package_and_extra_resource_package": "Includes standard and extra plans",
"storage_capacity": "Storage capacity",
"team_balance": "Balance",
"team_info": "Team Information",
"token_validity_period": "Points are valid for one year",
"tokens": "integral",
"type": "type",

View File

@@ -1,4 +1,7 @@
{
"notification_detail": "notification details",
"no_notifications": "No notification yet",
"read": "Read"
"read": "Read",
"system": "official",
"team": "team"
}

View File

@@ -17,6 +17,8 @@
"create_sub_org": "Create sub-organization",
"delete": "delete",
"delete_org": "Delete organization",
"edit_member": "Edit user",
"edit_member_tip": "username",
"edit_info": "Edit information",
"edit_org_info": "Edit organization information",
"expires": "Expiration",
@@ -29,6 +31,7 @@
"has_forbidden": "Forbidden",
"has_invited": "Invited",
"ignore": "Ignore",
"invitation_copy_link": "[{{systemName}}] {{userName}} invites you to join the {{teamName}} team, link: {{url}}",
"invitation_link_auto_clean_hint": "Expired links will be automatically cleaned up after 30 days",
"invitation_link_description": "Link description",
"invitation_link_list": "Invitation link list",
@@ -58,6 +61,5 @@
"user_team_invite_member": "Invite members",
"user_team_leave_team": "Leave the team",
"user_team_leave_team_failed": "Failure to leave the team",
"waiting": "To be accepted",
"invitation_copy_link": "[{{systemName}}] {{userName}} invites you to join the {{teamName}} team, link: {{url}}"
}
"waiting": "To be accepted"
}

View File

@@ -272,7 +272,7 @@
"compliance.compliance.dataset": "Please ensure that your content strictly complies with relevant laws and regulations and avoid containing any illegal or infringing content. \nPlease be careful when uploading materials that may contain sensitive information.",
"compliance.dataset": "Please ensure that your content strictly complies with relevant laws and regulations and avoid containing any illegal or infringing content. \nPlease be careful when uploading materials that may contain sensitive information.",
"confirm_choice": "Confirm Choice",
"contact_way": "Contact information",
"contact_way": "Notification Received",
"contribute_app_template": "Contribute Template",
"core.Chat": "Chat",
"core.Max Token": "Max Token",

View File

@@ -22,10 +22,10 @@
"delete.failed": "Delete failed",
"delete.success": "Delete successfully",
"has_chosen": "Selected",
"login.Dingtalk": "DingTalk Login",
"login.error": "Login Error",
"login.password_condition": "Password can be up to 60 characters",
"login.success": "Login Successful",
"login.Dingtalk": "DingTalk Login",
"manage_team": "Manage team",
"name": "Name",
"notification.remind_owner_bind": "Please remind the creator to bind a notification account",
@@ -45,8 +45,8 @@
"password.retrieved_account": "Retrieve {{account}} Account",
"password.to_login": "Go to Login",
"password.verification_code": "Verification Code",
"permission.Manage": "Admin",
"permission.Add": "Add Permissions",
"permission.Manage": "Admin",
"permission.Manage tip": "Team admin with full permissions",
"permission.Read": "Read Only",
"permission.Read desc": "Members can only read related resources, cannot create new resources",
@@ -55,10 +55,10 @@
"permission.only_collaborators": "Collaborators Only",
"permission.team_read": "Team Read Access",
"permission.team_write": "Team Write Access",
"permission_add_tip": "After adding, you can check the permissions for them.",
"permission_des.manage": "Can create resources, invite, and delete members",
"permission_des.read": "Members can only read related resources and cannot create new resources.",
"permission_des.write": "In addition to readable resources, you can also create new resources",
"permission_add_tip": "After adding, you can check the permissions for them.",
"permissions": "Permissions",
"personal_information": "Me",
"personalization": "Personalization",
@@ -67,6 +67,7 @@
"register.register_account": "Register {{account}} Account",
"register.success": "Registration Successful",
"register.to_login": "Already have an account? Go to Login",
"search_group_org_user": "Search member/group/org name",
"search_user": "Search Username",
"sso_auth_failed": "SSO authentication failed",
"synchronization.button": "Sync Now",
@@ -82,8 +83,8 @@
"team.Team Name": "Team name",
"team.Update Team": "Update team information",
"team.add_collaborator": "Add Collaborator",
"team.add_writer": "Add writable members",
"team.add_permission": "Add permissions",
"team.add_writer": "Add writable members",
"team.avatar_and_name": "avatar",
"team.belong_to_group": "Member group",
"team.group.avatar": "Group avatar",
@@ -96,7 +97,7 @@
"team.group.group": "group",
"team.group.keep_admin": "Keep administrator rights",
"team.group.manage_member": "Managing members",
"team.group.manage_tip": "You can invite members, delete members, create groups, manage all groups, and assign permissions to groups and members",
"team.group.manage_tip": "Can manage members, create groups, manage all groups, assign permissions to groups and members",
"team.group.members": "member",
"team.group.name": "Group name",
"team.group.permission.manage": "administrator",
@@ -105,12 +106,11 @@
"team.group.role.admin": "administrator",
"team.group.role.member": "member",
"team.group.role.owner": "owner",
"search_group_org_user": "Search member/group/org name",
"team.group.set_as_admin": "Set as administrator",
"team.group.toast.can_not_delete_owner": "Owner cannot be deleted, please transfer first",
"team.group.transfer_owner": "transfer owner",
"team.org.org": "Organization",
"team.manage_collaborators": "Manage Collaborators",
"team.no_collaborators": "No Collaborators",
"team.org.org": "Organization",
"team.write_role_member": ""
}

View File

@@ -28,6 +28,7 @@
"exchange_success": "兑换成功",
"expiration_time": "到期时间",
"expired": "已过期",
"general_info": "通用信息",
"group": "组",
"help_chatbot": "机器人助手",
"help_document": "帮助文档",
@@ -48,8 +49,8 @@
"password_update_error": "修改密码异常",
"password_update_success": "修改密码成功",
"pending_usage": "待使用",
"personal_information": "个人信息",
"phone_label": "手机号",
"please_bind_contact": "请绑定联系方式",
"please_bind_notification_receiving_path": "请先绑定通知接收途径",
"purchase_extra_package": "购买额外套餐",
"reminder_create_bound_notification_account": "提醒创建者绑定通知账号",
@@ -58,6 +59,7 @@
"standard_package_and_extra_resource_package": "包含标准套餐与额外资源包",
"storage_capacity": "存储量",
"team_balance": "团队余额",
"team_info": "团队信息",
"token_validity_period": "积分有效期一年",
"tokens": "积分",
"type": "类型",
@@ -68,9 +70,8 @@
"usage_balance": "使用余额: 使用余额",
"usage_balance_notice": "由于系统升级,原“自动续费从余额扣款”模式取消,余额充值入口关闭。您的余额可用于购买积分",
"user_account": "账号",
"user_team_team_name": "团队",
"user_team_team_name": "团队",
"verification_code": "验证码",
"you_can_convert": "您可以兑换",
"yuan": "元",
"please_bind_contact": "请绑定联系方式"
}
"yuan": "元"
}

View File

@@ -1,4 +1,7 @@
{
"notification_detail": "通知详情",
"no_notifications": "暂无通知",
"read": "已读",
"no_notifications": "暂无通知"
"system": "官方",
"team": "团队"
}

View File

@@ -20,6 +20,8 @@
"delete_from_org": "移出部门",
"delete_from_team": "移出团队",
"delete_org": "删除部门",
"edit_member": "编辑用户",
"edit_member_tip": "用户名",
"edit_info": "编辑信息",
"edit_org_info": "编辑部门信息",
"expires": "有效期",
@@ -33,6 +35,7 @@
"has_forbidden": "已失效",
"has_invited": "已邀请",
"ignore": "忽略",
"invitation_copy_link": "【{{systemName}}】 {{userName}} 邀请您加入{{teamName}}团队,链接:{{url}}",
"invitation_link_auto_clean_hint": "已失效链接将在30天后自动清理",
"invitation_link_description": "链接描述",
"invitation_link_list": "链接列表",
@@ -53,6 +56,7 @@
"org_name": "部门名称",
"owner": "所有者",
"permission": "权限",
"please_bind_contact": "请绑定联系方式",
"remark": "备注",
"remove_tip": "确认将 {{username}} 移出团队?成员将被标记为“已离职”,不删除操作数据,账号下资源自动转让给团队所有者。",
"restore_tip": "确认将 {{username}} 加入团队吗?仅恢复该成员账号可用性及相关权限,无法恢复账号下资源。",
@@ -73,7 +77,5 @@
"user_team_invite_member": "邀请成员",
"user_team_leave_team": "离开团队",
"user_team_leave_team_failed": "离开团队失败",
"waiting": "待接受",
"invitation_copy_link": "【{{systemName}}】 {{userName}} 邀请您加入{{teamName}}团队,链接:{{url}}",
"please_bind_contact": "请绑定联系方式"
}
"waiting": "待接受"
}

View File

@@ -275,7 +275,7 @@
"compliance.chat": "内容由第三方 AI 生成,无法确保真实准确,仅供参考",
"compliance.dataset": "请确保您的内容严格遵守相关法律法规,避免包含任何违法或侵权的内容。请谨慎上传可能涉及敏感信息的资料。",
"confirm_choice": "确认选择",
"contact_way": "联系方式",
"contact_way": "通知接收",
"contribute_app_template": "贡献模板",
"core.Chat": "对话",
"core.Max Token": "单条数据上限",
@@ -1291,4 +1291,4 @@
"yes": "是",
"yesterday": "昨天",
"yesterday_detail_time": "昨天 {{time}}"
}
}

View File

@@ -22,10 +22,10 @@
"delete.failed": "删除失败",
"delete.success": "删除成功",
"has_chosen": "已选择",
"login.Dingtalk": "钉钉登录",
"login.error": "登录异常",
"login.password_condition": "密码最多 60 位",
"login.success": "登录成功",
"login.Dingtalk": "钉钉登录",
"manage_team": "管理团队",
"name": "名称",
"notification.remind_owner_bind": "请提醒创建者绑定通知账号",
@@ -45,20 +45,20 @@
"password.retrieved_account": "找回 {{account}} 账号",
"password.to_login": "去登录",
"password.verification_code": "验证码",
"permission.Add": "添加权限",
"permission.Manage": "管理员",
"permission.Manage tip": "团队管理员,拥有全部权限",
"permission.Read": "仅读",
"permission.Read desc": "成员仅可阅读相关资源,无法新建资源",
"permission.Add": "添加权限",
"permission.Write": "可写",
"permission.Write tip": "除了可读资源外,还可以新建新的资源",
"permission.only_collaborators": "仅协作者访问",
"permission.team_read": "团队可访问",
"permission.team_write": "团队可编辑",
"permission_add_tip": "添加后,您可为其勾选权限。",
"permission_des.manage": "可创建资源、邀请、删除成员",
"permission_des.read": "成员仅可阅读相关资源,无法新建资源",
"permission_des.write": "除了可读资源外,还可以新建新的资源",
"permission_add_tip": "添加后,您可为其勾选权限。",
"permissions": "权限",
"personal_information": "个人信息",
"personalization": "个性化",
@@ -67,6 +67,7 @@
"register.register_account": "注册 {{account}} 账号",
"register.success": "注册成功",
"register.to_login": "已有账号,去登录",
"search_group_org_user": "搜索成员/部门/群组名称",
"search_user": "搜索用户名",
"sso_auth_failed": "SSO 鉴权失败",
"synchronization.button": "立即同步",
@@ -82,8 +83,8 @@
"team.Team Name": "团队名",
"team.Update Team": "更新团队信息",
"team.add_collaborator": "添加协作者",
"team.add_writer": "添加可写成员",
"team.add_permission": "添加权限",
"team.add_writer": "添加可写成员",
"team.avatar_and_name": "头像 & 名称",
"team.belong_to_group": "所属群组",
"team.group.avatar": "群头像",
@@ -96,7 +97,7 @@
"team.group.group": "群组",
"team.group.keep_admin": "保留管理员权限",
"team.group.manage_member": "管理成员",
"team.group.manage_tip": "可以邀请成员、删除成员、创建群组、管理所有群组、为群组和成员分配权限",
"team.group.manage_tip": "可以管理成员、创建群组、管理所有群组、为群组和成员分配权限",
"team.group.members": "成员",
"team.group.name": "群组名称",
"team.group.permission.manage": "管理员",
@@ -105,12 +106,11 @@
"team.group.role.admin": "管理员",
"team.group.role.member": "成员",
"team.group.role.owner": "所有者",
"search_group_org_user": "搜索成员/部门/群组名称",
"team.group.set_as_admin": "设为管理员",
"team.group.toast.can_not_delete_owner": "不能删除所有者, 请先转让",
"team.group.transfer_owner": "转让所有者",
"team.org.org": "部门",
"team.manage_collaborators": "管理协作者",
"team.no_collaborators": "暂无协作者",
"team.org.org": "部门",
"team.write_role_member": "可写权限"
}

View File

@@ -28,6 +28,7 @@
"exchange_success": "兌換成功",
"expiration_time": "到期時間",
"expired": "已過期",
"general_info": "通用信息",
"group": "群組",
"help_chatbot": "機器人助手",
"help_document": "幫助文檔",
@@ -50,7 +51,6 @@
"password_update_error": "修改密碼異常",
"password_update_success": "修改密碼成功",
"pending_usage": "待使用",
"personal_information": "個人資訊",
"phone_label": "手機號",
"please_bind_notification_receiving_path": "請先綁定通知接收途徑",
"purchase_extra_package": "購買額外套餐",
@@ -60,6 +60,7 @@
"standard_package_and_extra_resource_package": "包含標準套餐與額外資源包",
"storage_capacity": "儲存量",
"team_balance": "團隊餘額",
"team_info": "團隊信息",
"token_validity_period": "積分有效期限一年",
"tokens": "積分",
"type": "類型",
@@ -70,8 +71,8 @@
"usage_balance": "使用餘額: 使用餘額",
"usage_balance_notice": "由於系統升級,原「自動續費從餘額扣款」模式取消,餘額儲值入口關閉。\n您的餘額可用於購買積分",
"user_account": "帳號",
"user_team_team_name": "團隊",
"user_team_team_name": "團隊",
"verification_code": "驗證碼",
"you_can_convert": "您可以兌換",
"yuan": "元"
}
}

View File

@@ -1,4 +1,7 @@
{
"notification_detail": "通知詳情",
"no_notifications": "暫無通知",
"read": "已讀"
"read": "已讀",
"system": "官方",
"team": "團隊"
}

View File

@@ -17,6 +17,8 @@
"create_sub_org": "創建子部門",
"delete": "刪除",
"delete_org": "刪除部門",
"edit_member": "編輯用戶",
"edit_member_tip": "使用者名稱",
"edit_info": "編輯訊息",
"edit_org_info": "編輯部門資訊",
"expires": "有效期",
@@ -29,6 +31,7 @@
"has_forbidden": "已失效",
"has_invited": "已邀請",
"ignore": "忽略",
"invitation_copy_link": "【{{systemName}}】 {{userName}} 邀請您加入{{teamName}}團隊,連結:{{url}}",
"invitation_link_auto_clean_hint": "已失效連結將在30天後自動清理",
"invitation_link_description": "連結描述",
"invitation_link_list": "連結列表",
@@ -58,6 +61,5 @@
"user_team_invite_member": "邀請成員",
"user_team_leave_team": "離開團隊",
"user_team_leave_team_failed": "離開團隊失敗",
"waiting": "待接受",
"invitation_copy_link": "【{{systemName}}】 {{userName}} 邀請您加入{{teamName}}團隊,連結:{{url}}"
}
"waiting": "待接受"
}

View File

@@ -271,7 +271,7 @@
"compliance.compliance.dataset": "請確保您的內容嚴格遵守相關法律法規,避免包含任何違法或侵權的內容。\n在上傳可能涉及敏感資訊的資料時請務必謹慎。",
"compliance.dataset": "請確保您的內容嚴格遵守相關法律法規,避免包含任何違法或侵權的內容。\n在上傳可能涉及敏感資訊的資料時請務必謹慎。",
"confirm_choice": "確認選擇",
"contact_way": "聯繫方式",
"contact_way": "通知接收",
"contribute_app_template": "貢獻範本",
"core.Chat": "對話",
"core.Max Token": "單筆資料上限",

View File

@@ -22,10 +22,10 @@
"delete.failed": "刪除失敗",
"delete.success": "刪除成功",
"has_chosen": "已選擇",
"login.Dingtalk": "釘釘登入",
"login.error": "登入失敗",
"login.password_condition": "密碼最多可輸入 60 個字元",
"login.success": "登入成功",
"login.Dingtalk": "釘釘登入",
"manage_team": "管理團隊",
"name": "名稱",
"notification.remind_owner_bind": "請提醒建立者綁定通知帳號",
@@ -45,20 +45,20 @@
"password.retrieved_account": "找回 {{account}} 帳號",
"password.to_login": "前往登入",
"password.verification_code": "驗證碼",
"permission.Add": "新增權限",
"permission.Manage": "管理員",
"permission.Manage tip": "團隊管理員,擁有完整權限",
"permission.Read": "唯讀",
"permission.Read desc": "成員僅能閱讀相關資源,無法建立新資源",
"permission.Add": "新增權限",
"permission.Write": "可寫入",
"permission.Write tip": "除了可讀取資源外,還可以建立新的資源",
"permission.only_collaborators": "僅協作者可存取",
"permission.team_read": "團隊可存取",
"permission.team_write": "團隊可編輯",
"permission_add_tip": "添加後,您可為其勾選權限。",
"permission_des.manage": "可建立資源、邀請及刪除成員",
"permission_des.read": "成員僅能閱讀相關資源,無法建立新資源",
"permission_des.write": "除了可讀取資源外,還可以建立新的資源",
"permission_add_tip": "添加後,您可為其勾選權限。",
"permissions": "權限",
"personal_information": "個人資料",
"personalization": "個人化",
@@ -67,6 +67,7 @@
"register.register_account": "註冊 {{account}} 帳號",
"register.success": "註冊成功",
"register.to_login": "已有帳號?前往登入",
"search_group_org_user": "搜尋成員/部門/群組名稱",
"search_user": "搜尋使用者名稱",
"sso_auth_failed": "SSO 鑑權失敗",
"synchronization.button": "立即同步",
@@ -82,8 +83,8 @@
"team.Team Name": "團隊名",
"team.Update Team": "更新團隊資訊",
"team.add_collaborator": "新增協作者",
"team.add_writer": "新增可寫入成員",
"team.add_permission": "新增權限",
"team.add_writer": "新增可寫入成員",
"team.avatar_and_name": "頭像與名稱",
"team.belong_to_group": "所屬成員群組",
"team.group.avatar": "群組頭像",
@@ -96,7 +97,7 @@
"team.group.group": "群組",
"team.group.keep_admin": "保留管理員權限",
"team.group.manage_member": "管理成員",
"team.group.manage_tip": "可以邀請成員、刪除成員、建立群組、管理所有群組,以及為群組和成員分配權限",
"team.group.manage_tip": "可以管理成員、創建群組、管理所有群組為群組和成員分配權限",
"team.group.members": "成員",
"team.group.name": "群組名稱",
"team.group.permission.manage": "管理員",
@@ -105,12 +106,11 @@
"team.group.role.admin": "管理員",
"team.group.role.member": "成員",
"team.group.role.owner": "擁有者",
"search_group_org_user": "搜尋成員/部門/群組名稱",
"team.group.set_as_admin": "設為管理員",
"team.group.toast.can_not_delete_owner": "無法刪除擁有者,請先轉移擁有權",
"team.group.transfer_owner": "轉移擁有者",
"team.org.org": "組織",
"team.manage_collaborators": "管理協作者",
"team.no_collaborators": "目前沒有協作者",
"team.org.org": "組織",
"team.write_role_member": "可寫入權限"
}

View File

@@ -5,7 +5,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import { CloseIcon } from '@chakra-ui/icons';
import { readInform } from '@/web/support/user/inform/api';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import Markdown from '@/components/Markdown';
const ImportantInform = ({
informs,
refetch
@@ -28,32 +28,61 @@ const ImportantInform = ({
{informs.map((inform) => (
<Flex
key={inform._id}
bg={'primary.015'}
py={3}
py={4}
px={5}
fontSize={'md'}
borderRadius={'lg'}
boxShadow={'4'}
borderWidth={'1px'}
backgroundColor={'white'}
borderColor={'borderColor.base'}
minW={['200px', '400px']}
alignItems={'flex-start'}
mb={3}
backdropFilter={'blur(30px)'}
>
<MyIcon name={'support/user/informLight'} w={'16px'} mr={2} />
<Box flex={'1 0 0'}>
<Box fontWeight={'bold'}>{inform.title}</Box>
<Box fontSize={'sm'}>{inform.content}</Box>
</Box>
<CloseIcon
cursor={'pointer'}
_hover={{
color: 'primary.700'
}}
w={'12px'}
onClick={() => onClickClose(inform._id)}
<MyIcon
name={'support/user/informLight'}
w={5}
h={5}
mr={2}
mt={'2px'}
color="blue.600"
/>
<Box flex={'1 0 0'}>
<Box
fontWeight="bold"
fontSize="16px"
lineHeight="24px"
letterSpacing="0.15px"
fontFamily="'PingFang SC', sans-serif"
color=" #24282C"
>
{inform.title}
</Box>
<Box
pt={1}
fontSize="14px"
lineHeight="20px"
letterSpacing="0.25px"
fontFamily="'PingFang SC', sans-serif"
fontWeight="400"
color="#24282C"
>
<Markdown source={inform?.content} />
</Box>
</Box>
<Box
cursor={'pointer'}
p={1}
pt={0}
borderRadius={'4px'}
_hover={{
backgroundColor: 'rgba(17, 24, 36, 0.05)'
}}
onClick={() => onClickClose(inform._id)}
>
<CloseIcon w={'12px'} />
</Box>
</Flex>
))}
</Box>

View File

@@ -0,0 +1,73 @@
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import Markdown from '@/components/Markdown';
import React from 'react';
import { Box, Flex } from '@chakra-ui/react';
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import MyDivider from '@fastgpt/web/components/common/MyDivider';
const NotificationDetailsModal = ({ inform, onClose }: { inform: any; onClose: () => void }) => {
const { t } = useTranslation();
const textStyles = {
title: {
color: 'grayModern.900',
fontSize: '20px',
fontWeight: 'medium',
lineHeight: 6,
letterSpacing: '0.15px'
},
time: {
color: 'grayModern.500',
fontSize: '12px',
lineHeight: 5,
letterSpacing: '0.25px'
}
};
return (
<MyModal
isOpen={!!inform}
iconSrc={'support/user/informLight'}
title={t('account_inform:notification_detail')}
onClose={onClose}
iconColor="blue.600"
maxW="680px"
maxH="80vh"
>
<Flex flexDirection="column" p={8}>
<Flex
{...textStyles.time}
fontFamily="PingFang SC"
display="flex"
justifyContent="space-between"
alignItems="center"
alignSelf="stretch"
>
<Box {...textStyles.title} fontFamily="PingFang SC">
{inform.title}
</Box>
<Box {...textStyles.time} ml={3} flex={1} fontFamily="PingFang SC">
{t(formatTimeToChatTime(inform.time) as any).replace('#', ':')}
</Box>
<MyTag
colorSchema={inform.teamId ? 'green' : 'blue'}
mr={2}
fontSize="xs"
fontWeight="medium"
showDot={false}
type="fill"
>
{inform.teamId ? t('account_inform:team') : t('account_inform:system')}
</MyTag>
</Flex>
<MyDivider my={4} />
<Box fontSize="sm" lineHeight={1.8}>
<Markdown source={inform?.content} />
</Box>
</Flex>
</MyModal>
);
};
export default React.memo(NotificationDetailsModal);

View File

@@ -17,7 +17,12 @@ import {
import { useTranslation } from 'next-i18next';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { delRemoveMember, postRestoreMember } from '@/web/support/user/team/api';
import { useEditTextarea } from '@fastgpt/web/hooks/useEditTextarea';
import {
delRemoveMember,
postRestoreMember,
putUpdateMemberNameByManager
} from '@/web/support/user/team/api';
import Tag from '@fastgpt/web/components/common/Tag';
import Icon from '@fastgpt/web/components/common/Icon';
import { useContextSelector } from 'use-context-selector';
@@ -40,6 +45,7 @@ import OrgTags from '@/components/support/user/team/OrgTags';
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
import { useState } from 'react';
import { downloadFetch } from '@/web/common/system/utils';
import { TeamMemberItemType } from '@fastgpt/global/support/user/team/type';
const InviteModal = dynamic(() => import('./Invite/InviteModal'));
const TeamTagModal = dynamic(() => import('@/components/support/user/team/TeamTagModal'));
@@ -128,6 +134,30 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
const isLoading = isUpdateInvite || isSyncing;
const { EditModal: EditMemberNameModal, onOpenModal: openEditMemberName } = useEditTextarea({
title: t('account_team:edit_member'),
tip: t('account_team:edit_member_tip'),
canEmpty: false,
rows: 1
});
const handleEditMemberName = (tmbId: string, memberName: string) => {
openEditMemberName({
defaultVal: memberName,
onSuccess: (newName: string) => {
return putUpdateMemberNameByManager(tmbId, newName).then(() => {
Promise.all([refetchGroups(), refetchMembers()]);
});
},
onError: (err) => {
toast({
title: '',
status: 'error'
});
}
});
};
return (
<>
{isLoading && <MyLoading />}
@@ -222,7 +252,9 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{t('account_team:user_name')}
</Th>
<Th bgColor="myGray.100">{t('common:contact_way')}</Th>
<Th bgColor="myGray.100">{t('account_team:org')}</Th>
<Th bgColor="myGray.100" pl={9}>
{t('account_team:org')}
</Th>
<Th bgColor="myGray.100">{t('account_team:join_update_time')}</Th>
<Th borderRightRadius="6px" bgColor="myGray.100">
{t('common:common.Action')}
@@ -262,7 +294,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
})()}
</Td>
<Td maxW={'300px'}>
<VStack gap={0}>
<VStack gap={0} alignItems="flex-start">
<Box>{format(new Date(member.createTime), 'yyyy-MM-dd HH:mm:ss')}</Box>
<Box>
{member.updateTime
@@ -276,29 +308,45 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
member.role !== TeamMemberRoleEnum.owner &&
member.tmbId !== userInfo?.team.tmbId &&
(member.status === TeamMemberStatusEnum.active ? (
<Icon
name={'common/trash'}
cursor={'pointer'}
w="1rem"
p="1"
borderRadius="sm"
_hover={{
color: 'red.600',
bgColor: 'myGray.100'
}}
onClick={() => {
openRemoveMember(
() =>
delRemoveMember(member.tmbId).then(() =>
Promise.all([refetchGroups(), refetchMembers()])
),
undefined,
t('account_team:remove_tip', {
username: member.memberName
})
)();
}}
/>
<>
<Icon
name={'edit'}
cursor={'pointer'}
w="1rem"
p="1"
borderRadius="sm"
_hover={{
color: 'blue.600',
bgColor: 'myGray.100'
}}
onClick={() =>
handleEditMemberName(member.tmbId, member.memberName)
}
/>
<Icon
name={'common/trash'}
cursor={'pointer'}
w="1rem"
p="1"
borderRadius="sm"
_hover={{
color: 'red.600',
bgColor: 'myGray.100'
}}
onClick={() => {
openRemoveMember(
() =>
delRemoveMember(member.tmbId).then(() =>
Promise.all([refetchGroups(), refetchMembers()])
),
undefined,
t('account_team:remove_tip', {
username: member.memberName
})
)();
}}
/>
</>
) : (
member.status === TeamMemberStatusEnum.forbidden && (
<Icon
@@ -331,6 +379,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
</Table>
<ConfirmRemoveMemberModal />
<ConfirmRestoreMemberModal />
<EditMemberNameModal />
</TableContainer>
</MemberScrollData>
</Box>

View File

@@ -45,6 +45,7 @@ import TeamSelector from '@/pageComponents/account/TeamSelector';
import { getWorkorderURL } from '@/web/common/workorder/api';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useMount } from 'ahooks';
import MyDivider from '@fastgpt/web/components/common/MyDivider';
const StandDetailModal = dynamic(
() => import('@/pageComponents/account/info/standardDetailModal'),
@@ -76,7 +77,7 @@ const Info = () => {
<Flex justifyContent={'center'} maxW={'1080px'}>
<Box flex={'0 0 330px'}>
<MyInfo onOpenContact={onOpenContact} />
<Box mt={9}>
<Box mt={6}>
<Other onOpenContact={onOpenContact} />
</Box>
</Box>
@@ -162,8 +163,23 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
const labelStyles: BoxProps = {
flex: '0 0 80px',
fontSize: 'sm',
color: 'myGray.900'
color: 'var(--light-general-on-surface-lowest, var(--Gray-Modern-500, #667085))',
fontFamily: '"PingFang SC"',
fontSize: '14px',
fontStyle: 'normal',
fontWeight: 400,
lineHeight: '20px',
letterSpacing: '0.25px'
};
const titleStyles: BoxProps = {
color: 'var(--light-general-on-surface, var(--Gray-Modern-900, #111824))',
fontFamily: '"PingFang SC"',
fontSize: '16px',
fontStyle: 'normal',
fontWeight: 500,
lineHeight: '24px',
letterSpacing: '0.15px'
};
const isSyncMember = feConfigs.register_method?.includes('sync');
@@ -171,27 +187,69 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
<Box>
{/* user info */}
{isPc && (
<Flex alignItems={'center'} fontSize={'md'} h={'30px'}>
<MyIcon mr={2} name={'support/user/userLight'} w={'1.25rem'} />
{t('account_info:personal_information')}
<Flex alignItems={'center'} h={'30px'} {...titleStyles}>
<MyIcon mr={2} name={'core/dataset/fileCollection'} w={'1.25rem'} />
{t('account_info:general_info')}
</Flex>
)}
<Box mt={[0, 6]} fontSize={'sm'}>
<Flex alignItems={'center'}>
<Box {...labelStyles}>{t('account_info:user_account')}&nbsp;</Box>
<Box flex={1}>{userInfo?.username}</Box>
</Flex>
{feConfigs?.isPlus && (
<Flex mt={4} alignItems={'center'}>
<Box {...labelStyles}>{t('account_info:password')}&nbsp;</Box>
<Box flex={1}>*****</Box>
<Button size={'sm'} variant={'whitePrimary'} onClick={onOpenUpdatePsw}>
{t('account_info:change')}
</Button>
</Flex>
)}
{feConfigs?.isPlus && (
<Flex mt={4} alignItems={'center'}>
<Box {...labelStyles}>{t('common:contact_way')}&nbsp;</Box>
<Box flex={1} {...(!userInfo?.contact ? { color: 'red.600' } : {})}>
{userInfo?.contact ? userInfo?.contact : t('account_info:please_bind_contact')}
</Box>
<Button size={'sm'} variant={'whitePrimary'} onClick={onOpenUpdateContact}>
{t('account_info:change')}
</Button>
</Flex>
)}
<MyDivider my={6} />
{isPc && (
<Flex alignItems={'center'} h={'30px'} {...titleStyles} mt={6}>
<MyIcon mr={2} name={'support/team/group'} w={'1.25rem'} />
{t('account_info:team_info')}
</Flex>
)}
{feConfigs.isPlus && (
<Flex mt={6} alignItems={'center'}>
<Box {...labelStyles}>{t('account_info:user_team_team_name')}&nbsp;</Box>
<Flex flex={'1 0 0'} w={0} align={'center'}>
<TeamSelector height={'28px'} w={'100%'} showManage />
</Flex>
</Flex>
)}
{isPc ? (
<Flex alignItems={'center'} cursor={'pointer'}>
<Box {...labelStyles}>{t('account_info:avatar')}:&nbsp;</Box>
<Flex mt={4} alignItems={'center'} cursor={'pointer'}>
<Box {...labelStyles}>{t('account_info:avatar')}&nbsp;</Box>
<MyTooltip label={t('account_info:select_avatar')}>
<Box
w={['44px', '56px']}
h={['44px', '56px']}
w={['22px', '32px']}
h={['22px', '32px']}
borderRadius={'50%'}
border={theme.borders.base}
overflow={'hidden'}
p={'2px'}
boxShadow={'0 0 5px rgba(0,0,0,0.1)'}
mb={2}
onClick={onOpenSelectFile}
>
<Avatar src={userInfo?.avatar} borderRadius={'50%'} w={'100%'} h={'100%'} />
@@ -228,7 +286,7 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
)}
{feConfigs?.isPlus && (
<Flex mt={[0, 4]} alignItems={'center'}>
<Box {...labelStyles}>{t('account_info:member_name')}:&nbsp;</Box>
<Box {...labelStyles}>{t('account_info:member_name')}&nbsp;</Box>
<Input
flex={'1 0 0'}
disabled={isSyncMember}
@@ -248,43 +306,10 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
/>
</Flex>
)}
<Flex alignItems={'center'} mt={6}>
<Box {...labelStyles}>{t('account_info:user_account')}:&nbsp;</Box>
<Box flex={1}>{userInfo?.username}</Box>
</Flex>
{feConfigs?.isPlus && (
<Flex mt={6} alignItems={'center'}>
<Box {...labelStyles}>{t('account_info:password')}:&nbsp;</Box>
<Box flex={1}>*****</Box>
<Button size={'sm'} variant={'whitePrimary'} onClick={onOpenUpdatePsw}>
{t('account_info:change')}
</Button>
</Flex>
)}
{feConfigs?.isPlus && (
<Flex mt={6} alignItems={'center'}>
<Box {...labelStyles}>{t('common:contact_way')}:&nbsp;</Box>
<Box flex={1} {...(!userInfo?.contact ? { color: 'red.600' } : {})}>
{userInfo?.contact ? userInfo?.contact : t('account_info:please_bind_contact')}
</Box>
<Button size={'sm'} variant={'whitePrimary'} onClick={onOpenUpdateContact}>
{t('account_info:change')}
</Button>
</Flex>
)}
{feConfigs.isPlus && (
<Flex mt={6} alignItems={'center'}>
<Box {...labelStyles}>{t('account_info:user_team_team_name')}:&nbsp;</Box>
<Flex flex={'1 0 0'} w={0} align={'center'}>
<TeamSelector height={'28px'} w={'100%'} showManage />
</Flex>
</Flex>
)}
{feConfigs?.isPlus && (userInfo?.team?.balance ?? 0) > 0 && (
<Box mt={6} whiteSpace={'nowrap'}>
<Box mt={4} whiteSpace={'nowrap'}>
<Flex alignItems={'center'}>
<Box {...labelStyles}>{t('account_info:team_balance')}:&nbsp;</Box>
<Box {...labelStyles}>{t('account_info:team_balance')}&nbsp;</Box>
<Box flex={1}>
<strong>{formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)}</strong>{' '}
{t('account_info:yuan')}
@@ -298,6 +323,8 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
</Flex>
</Box>
)}
<MyDivider my={6} />
</Box>
{isOpenConversionModal && (
<ConversionModal onClose={onCloseConversionModal} onOpenContact={onOpenContact} />
@@ -418,7 +445,16 @@ const PlanUsage = () => {
return standardPlan ? (
<Box mt={[6, 0]}>
<Flex fontSize={['md', 'lg']} h={'30px'}>
<Flex alignItems={'center'}>
<Flex
alignItems={'center'}
color="var(--light-general-on-surface, var(--Gray-Modern-900, #111824))"
fontFamily='"PingFang SC"'
fontSize="16px"
fontStyle="normal"
fontWeight={500}
lineHeight="24px"
letterSpacing="0.15px"
>
<MyIcon mr={2} name={'support/account/plans'} w={'20px'} />
{t('account_info:package_and_usage')}
</Flex>
@@ -601,7 +637,7 @@ const Other = ({ onOpenContact }: { onOpenContact: () => void }) => {
return (
<Box>
<Grid gridGap={4} mt={3}>
<Grid gridGap={4}>
{feConfigs?.docUrl && (
<Link
href={getDocPath('/docs/intro')}

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Box, Button, Flex, useTheme } from '@chakra-ui/react';
import React, { useState } from 'react';
import { Box, Flex, useTheme } from '@chakra-ui/react';
import { getInforms, readInform } from '@/web/support/user/inform/api';
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
import { usePagination } from '@fastgpt/web/hooks/usePagination';
@@ -8,11 +8,31 @@ import { useTranslation } from 'next-i18next';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import AccountContainer from '@/pageComponents/account/AccountContainer';
import { serviceSideProps } from '@/web/common/i18n/utils';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import Markdown from '@/components/Markdown';
import NotificationDetailsModal from '@/pageComponents/account/NotificationDetailsModal';
const InformTable = () => {
const { t } = useTranslation();
const theme = useTheme();
const { Loading } = useLoading();
const [selectedInform, setSelectedInform] = useState<any>(null);
const textStyles = {
title: {
color: '#111824',
fontSize: 'md',
fontWeight: 'bold',
lineHeight: 6,
letterSpacing: '0.15px'
},
time: {
color: '#667085',
fontSize: 'sm',
lineHeight: 5,
letterSpacing: '0.25px'
}
};
const {
data: informs,
@@ -28,63 +48,105 @@ const InformTable = () => {
return (
<AccountContainer>
<Flex flexDirection={'column'} py={[0, 5]} h={'100%'} position={'relative'}>
<Box px={[3, 8]} position={'relative'} flex={'1 0 0'} h={0} overflowY={'auto'}>
<Flex flexDirection="column" py={[0, 5]} h="100%" position="relative">
<Box
px={[3, 8]}
position="relative"
flex="1 0 0"
h={0}
overflowY="auto"
display="flex"
flexDirection="column"
alignItems="center"
>
{informs.map((item) => (
<Box
key={item._id}
border={theme.borders.md}
py={2}
px={4}
borderRadius={'md'}
position={'relative'}
_notLast={{ mb: 3 }}
py={5}
px={6}
maxH="168px"
maxW="800px"
minW="200px"
width="100%"
borderRadius="md"
position="relative"
_notLast={{ mb: 4 }}
_hover={{
border: '1px solid #94B5FF',
cursor: 'pointer'
}}
onClick={() => {
if (!item.read) {
readInform(item._id).then(() => getData(pageNum));
}
setSelectedInform(item);
}}
>
<Flex alignItems={'center'}>
<Box fontWeight={'bold'}>{item.title}</Box>
<Box ml={2} color={'myGray.500'} flex={'1 0 0'}>
({t(formatTimeToChatTime(item.time) as any).replace('#', ':')})
<Flex alignItems="center">
<Box {...textStyles.title}>
{item.teamId ? `${item.teamName}` : ''}
{item.title}
</Box>
{!item.read && (
<Button
variant={'whitePrimary'}
size={'xs'}
onClick={async () => {
if (!item.read) {
await readInform(item._id);
getData(pageNum);
}
}}
>
{t('account_inform:read')}
</Button>
)}
<Flex ml={3} flex={1} alignItems="center">
<Box {...textStyles.time}>
{t(formatTimeToChatTime(item.time) as any).replace('#', ':')}
</Box>
{!item.read && <Box w={2} h={2} borderRadius="full" bg="red.600" ml={3} />}
</Flex>
<MyTag
colorSchema={item.teamId ? 'green' : 'blue'}
mr={2}
fontSize="xs"
fontWeight="medium"
showDot={false}
type="fill"
>
{item.teamId ? t('account_inform:team') : t('account_inform:system')}
</MyTag>
</Flex>
<Box mt={2} fontSize={'sm'} color={'myGray.600'} whiteSpace={'pre-wrap'}>
{item.content}
<Box
mt={2}
fontSize="sm"
fontWeight={400}
color="#485264"
overflow="hidden"
maxHeight={24}
sx={{
lineHeight: '16px',
'& h1, & h2, & h3, & h4, & h5, & h6': {
my: '0 !important',
py: 0.5,
display: 'block',
lineHeight: 'normal'
},
'& p': {
my: 0
}
}}
noOfLines={6}
>
<Markdown source={item.content} />
</Box>
{!item.read && (
<>
<Box
w={'5px'}
h={'5px'}
borderRadius={'10px'}
bg={'red.600'}
position={'absolute'}
top={'8px'}
left={'8px'}
/>
</>
)}
</Box>
))}
{!isLoading && informs.length === 0 && (
<EmptyTip text={t('account_inform:no_notifications')}></EmptyTip>
<EmptyTip text={t('account_inform:no_notifications')} />
)}
</Box>
{selectedInform && (
<NotificationDetailsModal
inform={selectedInform}
onClose={() => setSelectedInform(null)}
/>
)}
{total > pageSize && (
<Flex w={'100%'} mt={4} px={[3, 8]} justifyContent={'flex-end'}>
<Flex w="100%" mt={4} px={[3, 8]} justifyContent="flex-end">
<Pagination />
</Flex>
)}

View File

@@ -1,15 +1,15 @@
import { GET, POST } from '@/web/common/api/request';
import type { UserInformSchema } from '@fastgpt/global/support/user/inform/type';
import type { UserInformType } from '@fastgpt/global/support/user/inform/type';
import type { SystemMsgModalValueType } from '@fastgpt/service/support/user/inform/type';
import type { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
export const getInforms = (data: PaginationProps) =>
POST<PaginationResponse<UserInformSchema>>(`/proApi/support/user/inform/list`, data);
POST<PaginationResponse<UserInformType>>(`/proApi/support/user/inform/list`, data);
export const getUnreadCount = () =>
GET<{
unReadCount: number;
importantInforms: UserInformSchema[];
importantInforms: UserInformType[];
}>(`/proApi/support/user/inform/countUnread`);
export const readInform = (id: string) => GET(`/proApi/support/user/inform/read`, { id });

View File

@@ -40,6 +40,8 @@ export const getTeamMembers = (props: PaginationProps<{ withLeaved?: boolean }>)
// export const postInviteTeamMember = (data: InviteMemberProps) =>
// POST<InviteMemberResponse>(`/proApi/support/user/team/member/invite`, data);
export const putUpdateMemberNameByManager = (tmbId: string, name: string) =>
PUT(`/proApi/support/user/team/member/updateNameByManager`, { tmbId, name });
export const putUpdateMemberName = (name: string) =>
PUT(`/proApi/support/user/team/member/updateName`, { name });