diff --git a/pages/settings/org/profile/[org_id].tsx b/pages/settings/org/profile/[org_id].tsx
index 7fc4574e..08984494 100644
--- a/pages/settings/org/profile/[org_id].tsx
+++ b/pages/settings/org/profile/[org_id].tsx
@@ -1,4 +1,4 @@
-import { Text, Box, Heading, Image, Center, Button, Flex, VStack, Divider, useToast, FormControl, FormLabel, FormHelperText, Input, FormErrorMessage, HStack, Wrap, useMediaQuery, Avatar, Textarea, } from "@chakra-ui/react"
+import { Text, Box, Heading, Image, Center, Button, Flex, VStack, Divider, useToast, FormControl, FormLabel, FormHelperText, Input, FormErrorMessage, HStack, Wrap, useMediaQuery, Avatar, Textarea, Modal, ModalOverlay, ModalContent, ModalBody, useDisclosure, Alert, AlertIcon, } from "@chakra-ui/react"
import Card from "components/card"
import Nav from "layouts/nav/nav"
import PageContainer from "layouts/page-container"
@@ -13,12 +13,15 @@ import Tags from "components/tags/tags"
var validator = require('validator');
const UserProfilePage = () => {
- const [user, setUser] = useState(null)
+ const { isOpen, onOpen, onClose } = useDisclosure()
+ const [org, setOrg] = useState(null)
+ const [newOnwer, setNewOnwer] = useState('')
+
const [isLargerThan1280] = useMediaQuery("(min-width: 768px)")
const router = useRouter()
useEffect(() => {
if (router.query.org_id) {
- requestApi.get(`/user/info/${router.query.org_id}`).then(res => setUser(res.data))
+ requestApi.get(`/user/info/${router.query.org_id}`).then(res => setOrg(res.data))
}
}, [router.query.org_id])
@@ -27,7 +30,7 @@ const UserProfilePage = () => {
const submitUser = async (values, _) => {
await requestApi.post(`/org/update`, values)
- setUser(values)
+ setOrg(values)
toast({
description: "更新成功",
status: "success",
@@ -97,15 +100,26 @@ const UserProfilePage = () => {
return error
}
+ const transferOrg = async () => {
+ await requestApi.post(`/org/transfer`,{orgID: router.query.org_id, ownerID: newOnwer})
+ toast({
+ description: "转移成功",
+ status: "success",
+ duration: 2000,
+ isClosable: true,
+ })
+ router.push('/settings/orgs')
+ }
+
const Layout = isLargerThan1280 ? HStack : VStack
return (
<>
-
- {user &&
+
+ {org &&
{(props) => (
@@ -115,10 +129,15 @@ const UserProfilePage = () => {
基本信息
+
+ Username
+
+
+
{({ field, form }) => (
- 用户昵称
+ 组织名称
{form.errors.nickname}
@@ -141,7 +160,7 @@ const UserProfilePage = () => {
头像设置
{form.errors.avatar}
- {user.avatar && }
+ {org.avatar && }
)}
@@ -152,7 +171,7 @@ const UserProfilePage = () => {
封面图片
{form.errors.cover}
- {user.cover && }
+ {org.cover && }
)}
@@ -192,7 +211,7 @@ const UserProfilePage = () => {
{({ field, form }) => (
组织技术栈
- form.values.skills = v} size="lg"/>
+ form.values.skills = v} size="lg" />
)}
@@ -277,10 +296,18 @@ const UserProfilePage = () => {
variant="outline"
type="submit"
_focus={null}
-
+
>
更新信息
-
+
+
)}
@@ -288,6 +315,23 @@ const UserProfilePage = () => {
}
+
+
+
+ {
+
+ 转移组织
+ 你必须是超级管理员才能完成此操作
+ setNewOnwer(e.currentTarget.value)} />
+
+
+ 注意:该操作无法逆转,一旦转移,你将变为普通成员
+
+
+
+
+ }
+
>
)
}
diff --git a/pages/settings/profile.tsx b/pages/settings/profile.tsx
index 8d56a3d9..bf6ceee9 100644
--- a/pages/settings/profile.tsx
+++ b/pages/settings/profile.tsx
@@ -64,6 +64,16 @@ const UserProfilePage = () => {
基本信息
+
+ ID
+
+
+
+
+ Username
+
+
+
{({ field, form }) => (
diff --git a/server/internal/api/org.go b/server/internal/api/org.go
index a4dfae8c..0eb7a4e7 100644
--- a/server/internal/api/org.go
+++ b/server/internal/api/org.go
@@ -110,7 +110,7 @@ func GenOrgSecret(c *gin.Context) {
func GetOrgSecret(c *gin.Context) {
orgID := c.Param("id")
currentUser := user.CurrentUser(c)
- if !org.IsOrgAdmin(currentUser.ID, orgID) {
+ if !org.UserInOrg(currentUser.ID, orgID) {
c.JSON(http.StatusForbidden, common.RespError(e.NoPermission))
return
}
@@ -209,3 +209,40 @@ func UpdateOrgMember(c *gin.Context) {
c.JSON(http.StatusOK, common.RespSuccess(nil))
}
+
+type TransferReq struct {
+ OrgID string `json:"orgID"`
+ OwnerID string `json:"ownerID"`
+}
+
+func TransferOrg(c *gin.Context) {
+ req := &TransferReq{}
+ c.Bind(&req)
+
+ // 检查当前用户是否是组织的超级管理员
+ currentUser := user.CurrentUser(c)
+ currentRole, err := org.GetMemberRole(req.OrgID, currentUser.ID)
+ if err != nil {
+ c.JSON(http.StatusBadRequest, common.RespError(e.BadRequest))
+ return
+ }
+
+ if currentRole != models.ROLE_SUPER_ADMIN {
+ c.JSON(http.StatusForbidden, common.RespError("只有超级管理员才能转移组织"))
+ return
+ }
+
+ // 检查目标用户是否是组织成员
+ if !org.UserInOrg(req.OwnerID, req.OrgID) {
+ c.JSON(http.StatusForbidden, common.RespError("目标用户不是组织成员"))
+ return
+ }
+
+ err0 := org.Transfer(req.OrgID, currentUser.ID, req.OwnerID)
+ if err0 != nil {
+ c.JSON(err0.Status, common.RespError(err0.Message))
+ return
+ }
+
+ c.JSON(http.StatusOK, common.RespSuccess(nil))
+}
diff --git a/server/internal/org/org.go b/server/internal/org/org.go
index 4336ca07..71561ceb 100644
--- a/server/internal/org/org.go
+++ b/server/internal/org/org.go
@@ -171,3 +171,28 @@ func GetMemberRole(orgID string, memberID string) (models.RoleType, error) {
return role, nil
}
+
+func Transfer(orgID, currentOwner, newOwner string) *e.Error {
+ tx, err := db.Conn.Begin()
+ if err != nil {
+ logger.Warn("start sql transaction error", "error", err)
+ return e.New(http.StatusInternalServerError, e.Internal)
+ }
+
+ _, err = tx.Exec("UPDATE org_member SET role=? WHERE org_id=? and user_id=?", models.ROLE_SUPER_ADMIN, orgID, newOwner)
+ if err != nil {
+ logger.Warn("transfer org error", "error", err)
+ return e.New(http.StatusInternalServerError, e.Internal)
+ }
+
+ _, err = tx.Exec("UPDATE org_member SET role=? WHERE org_id=? and user_id=?", models.ROLE_NORMAL, orgID, currentOwner)
+ if err != nil {
+ logger.Warn("transfer org error", "error", err)
+ tx.Rollback()
+ return e.New(http.StatusInternalServerError, e.Internal)
+ }
+
+ tx.Commit()
+
+ return nil
+}
diff --git a/server/internal/server.go b/server/internal/server.go
index a55cccb6..a0e589ab 100644
--- a/server/internal/server.go
+++ b/server/internal/server.go
@@ -112,6 +112,7 @@ func (s *Server) Start() error {
r.GET("/org/secret/:id", IsLogin(), api.GetOrgSecret)
r.POST("/org/join", IsLogin(), api.JoinOrg)
r.POST("/org/member/role", IsLogin(), api.UpdateOrgMember)
+ r.POST("/org/transfer", api.TransferOrg)
// admin apis
r.POST("/admin/user", IsLogin(), api.AdminSubmitUser)
r.GET("/admin/user/all", IsLogin(), api.AdminGetUsers)
diff --git a/src/data/links.tsx b/src/data/links.tsx
index 955ee877..0678d26c 100644
--- a/src/data/links.tsx
+++ b/src/data/links.tsx
@@ -102,7 +102,7 @@ export const settingLinks: Route[] = [{
export function orgSettingLinks(orgID) {
return [{
- title: '组织信息',
+ title: '组织设置',
path: `${ReserveUrls.Settings}/org/profile/${orgID}`,
icon: ,
disabled: false