Merge pull request #1 from Dot-Liu/main

APIPark-1.0-beta完成
This commit is contained in:
Dot.L
2024-08-16 20:56:44 +08:00
committed by GitHub
111 changed files with 1255 additions and 833 deletions
+63
View File
@@ -0,0 +1,63 @@
name: "Bug Report"
description: Report a bug to help improve the project.
title: "bug: "
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to report this bug!
_The more information you share, the faster we can identify and fix the bug._
Prior to opening the issue, please make sure that you:
- Use English to communicate.
- Search the [open issues](https://github.com/APIParkLab/APIPark/issues) and [discussion forum](https://github.com/APIParkLab/APIPark/discussions) to avoid duplicating the issue.
- type: textarea
id: current-behavior
attributes:
label: Current Behavior
description: Describe the issue you are facing.
placeholder: |
What is the issue with the current behavior?
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected Behavior
description: Describe what you expected to happen.
placeholder: |
What did you expect to happen instead?
validations:
required: false
- type: textarea
id: error
attributes:
label: Error Logs
description: Paste the error logs if any.
validations:
required: false
- type: textarea
id: steps
attributes:
label: Steps to Reproduce
description: Share the steps you took so that we can reproduce the issue. Reports without proper steps details will likely be closed.
placeholder: |
1. Run apinto via the Docker image.
2. Create a Route with the Admin API.
3. Try configuring ...
4. ...
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment
description: Share your environment details. Reports without proper environment details will likely be closed.
value: |
- APINTO Dashboard version (run `apinto dashboard version`):
- Operating system (run `uname -a`):
validations:
required: true
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: APIPark Discussion Forum
url: https://github.com/APIParkLab/APIPark/discussions
about: Please ask and answer questions here.
+33
View File
@@ -0,0 +1,33 @@
name: "Documentation Issue"
description: Issues related to documentation.
title: "docs: "
labels: [doc]
body:
- type: markdown
attributes:
value: |
_The more information you share, the faster we can help you._
Prior to opening the issue, please make sure that you:
- Use English to communicate.
- Search the [open issues](https://github.com/APIParkLab/APIPark/issues) and [discussion forum](https://github.com/APIParkLab/APIPark/discussions) to avoid duplicating the issue.
- type: textarea
id: current-state
attributes:
label: Current State
description: Describe the current state of the documentation.
placeholder: |
The documentation for the API in this page (url) is missing ...
validations:
required: true
- type: textarea
id: desired-state
attributes:
label: Desired State
description: Describe the desired state the documentation should be in.
placeholder: |
There should be line mentioning how the API behaves when ...
validations:
required: true
@@ -0,0 +1,23 @@
name: "Feature Request"
description: Suggest an enhancement to APINTO.
title: "feat: As a user, I want to ..., so that ..."
body:
- type: markdown
attributes:
value: |
_The more information you share, the faster we can help you._
Prior to opening the issue, please make sure that you:
- Use English to communicate.
- Search the [open issues](https://github.com/APIParkLab/APIPark/issues) and [discussion forum](https://github.com/APIParkLab/APIPark/discussions) to avoid duplicating the issue.
- type: textarea
id: description
attributes:
label: Description
description: Describe the feature you would like to see.
placeholder: |
As a user, I want to ..., so that...
validations:
required: true
+31
View File
@@ -0,0 +1,31 @@
name: "Request Help"
description: Stuck? Ask for help!
title: "help request: "
body:
- type: markdown
attributes:
value: |
_The more information you share, the faster we can help you._
Prior to opening the issue, please make sure that you:
- Use English to communicate.
- Search the [open issues](https://github.com/APIParkLab/APIPark/issues) and [discussion forum](https://github.com/APIParkLab/APIPark/discussions) to avoid duplicating the issue.
- type: textarea
id: description
attributes:
label: Description
description: Describe the issue you are facing and what you need help with.
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment
description: Share your environment details. Reports without proper environment details will likely be closed.
value: |
- APIPark version (run `apinto dashboard version`):
- Operating system (run `uname -a`):
validations:
required: true
+86
View File
@@ -0,0 +1,86 @@
name: release
#触发机制,当创建tag时
on:
release:
types:
- published
jobs:
frontend-builder:
name: frontend-builder
runs-on: ubuntu-latest
steps:
- name: SetOutput
id: vars
run: echo "tag=${GITHUB_REF#refs/*/v}" >> $GITHUB_OUTPUT
- name: Checkout #Checkout代码
uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3.0.0
with:
node-version: '18.12'
- name: Pnpm install and build
run: |
npm install -g pnpm
pnpm install --registry https://registry.npmmirror.com --dir ./frontend
echo "Build frontend..."
cd ./frontend && pnpm run build
- name: upload frontend release
uses: actions/upload-artifact@v2
with:
name: frontend-package
path: frontend/dist
release:
needs: [frontend-builder]
name: release
runs-on: ubuntu-latest
steps:
- name: SetOutput #处理Tag字符串并存进outputs
id: vars
run: |
echo "tag=${GITHUB_REF#refs/*/v}" >> $GITHUB_OUTPUT
- name: Checkout #Checkout代码
uses: actions/checkout@v3
- name: download frontend release
uses: actions/download-artifact@v2
with:
name: frontend-package
path: frontend/dist
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.21.1'
- name: Go tidy
run: |
go mod tidy
echo "GOVERSION=$(go version)" >> $GITHUB_ENV
- name: Create archives on Release #创建各种系统架构下的二进制包并上传至release assets
uses: goreleaser/goreleaser-action@v3.1.0
with:
version: 1.9.2
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
docker-push:
needs: [frontend-builder]
name: docker-push
runs-on: ubuntu-latest
steps:
- name: SetOutput
id: vars
run: echo "tag=${GITHUB_REF#refs/*/v}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v3
- name: download frontend release
uses: actions/download-artifact@v2
with:
name: frontend-package
path: frontend/dist
- name: Login Docker #登录docker
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: build
run: cd scripts && ./docker_publish.sh ${{ secrets.DOCKER_USERNAME }} "backend"
+2
View File
@@ -1,3 +1,5 @@
*.DS_Store
/.idea/
/config.yml
/build/
/apipark
+2 -2
View File
@@ -16,8 +16,8 @@ type IDynamicModuleController interface {
List(ctx *gin.Context, module string, keyword string, cluster string, page string, pageSize string) ([]map[string]interface{}, *dynamic_module_dto.PluginInfo, int64, error)
Render(ctx *gin.Context, module string) (*dynamic_module_dto.PluginBasic, map[string]interface{}, error)
ModuleDrivers(ctx *gin.Context, group string) ([]*dynamic_module_dto.ModuleDriver, error)
Online(ctx *gin.Context, module string, id string, partitionInput *dynamic_module_dto.ClusterInput) error
Offline(ctx *gin.Context, module string, id string, partitionInput *dynamic_module_dto.ClusterInput) error
Online(ctx *gin.Context, module string, id string) error
Offline(ctx *gin.Context, module string, id string) error
//PartitionStatuses(ctx *gin.Context, module string, keyword string, page string, pageSize string) (map[string]map[string]string, error)
//PartitionStatus(ctx *gin.Context, module string, id string) (*dynamic_module_dto.OnlineInfo, error)
}
+4 -4
View File
@@ -15,12 +15,12 @@ type imlDynamicModuleController struct {
module dynamic_module.IDynamicModuleModule `autowired:""`
}
func (i *imlDynamicModuleController) Online(ctx *gin.Context, module string, id string, partitionInput *dynamic_module_dto.ClusterInput) error {
return i.module.Online(ctx, module, id, partitionInput)
func (i *imlDynamicModuleController) Online(ctx *gin.Context, module string, id string) error {
return i.module.Online(ctx, module, id)
}
func (i *imlDynamicModuleController) Offline(ctx *gin.Context, module string, id string, partitionInput *dynamic_module_dto.ClusterInput) error {
return i.module.Offline(ctx, module, id, partitionInput)
func (i *imlDynamicModuleController) Offline(ctx *gin.Context, module string, id string) error {
return i.module.Offline(ctx, module, id)
}
//func (i *imlDynamicModuleController) PartitionStatuses(ctx *gin.Context, module string, keyword string, page string, pageSize string) (map[string]map[string]string, error) {
+7 -3
View File
@@ -14,6 +14,10 @@ type imlTeamController struct {
module my_team.ITeamModule `autowired:""`
}
func (c *imlTeamController) SimpleTeams(ctx *gin.Context, keyword string) ([]*team_dto.SimpleTeam, error) {
return c.module.SimpleTeams(ctx, keyword)
}
func (c *imlTeamController) UpdateMemberRole(ctx *gin.Context, id string, input *team_dto.UpdateMemberRole) error {
return c.module.UpdateMemberRole(ctx, id, input)
}
@@ -23,7 +27,7 @@ func (c *imlTeamController) GetTeam(ctx *gin.Context, id string) (*team_dto.Team
}
func (c *imlTeamController) Search(ctx *gin.Context, keyword string) ([]*team_dto.Item, error) {
return c.module.Search(ctx, keyword)
}
@@ -31,8 +35,8 @@ func (c *imlTeamController) EditTeam(ctx *gin.Context, id string, team *team_dto
return c.module.Edit(ctx, id, team)
}
func (c *imlTeamController) SimpleTeams(ctx *gin.Context, keyword string) ([]*team_dto.SimpleTeam, error) {
return c.module.SimpleTeams(ctx, keyword)
func (c *imlTeamController) MySimpleTeams(ctx *gin.Context, keyword string) ([]*team_dto.SimpleTeam, error) {
return c.module.MySimpleTeams(ctx, keyword)
}
func (c *imlTeamController) AddMember(ctx *gin.Context, id string, users *team_dto.UserIDs) error {
+2 -1
View File
@@ -2,7 +2,7 @@ package my_team
import (
"reflect"
team_dto "github.com/APIParkLab/APIPark/module/my-team/dto"
"github.com/eolinker/go-common/autowire"
"github.com/gin-gonic/gin"
@@ -13,6 +13,7 @@ type ITeamController interface {
GetTeam(ctx *gin.Context, id string) (*team_dto.Team, error)
Search(ctx *gin.Context, keyword string) ([]*team_dto.Item, error)
EditTeam(ctx *gin.Context, id string, team *team_dto.EditTeam) (*team_dto.Team, error)
MySimpleTeams(ctx *gin.Context, keyword string) ([]*team_dto.SimpleTeam, error)
SimpleTeams(ctx *gin.Context, keyword string) ([]*team_dto.SimpleTeam, error)
AddMember(ctx *gin.Context, id string, users *team_dto.UserIDs) error
RemoveMember(ctx *gin.Context, id string, uuid string) error
+1
View File
@@ -29,6 +29,7 @@
"@vitejs/plugin-react": "^4.2.0",
"autoprefixer": "^10.4.16",
"dayjs": "^1.11.10",
"dompurify": "^3.1.6",
"js-base64": "^3.7.5",
"moment": "^2.29.4",
"postcss": "^8.4.31",
@@ -28,6 +28,6 @@
"@businessEntry/*": ["./src/*"],
},
},
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TransferTable.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/Navigation.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/GroupTree.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../common/src/components/aoplatform/ScrollableSection.tsx", "../common/src/utils/postcat.tsx", "../common/src/utils/curl.ts", "../common/src/components/aoplatform/ResetPsw.tsx", "../common/src/components/aoplatform/SubscribeApprovalModalContent.tsx", "../common/src/components/aoplatform/InsidePageForHub.tsx", "src/components/aoplatform/RenderRoutes.tsx", "../common/src/components/aoplatform/PublishApprovalModalContent.tsx", "../common/src/components/aoplatform/InsidePage.tsx", "../common/src/const/type.ts", "../common/src/components/aoplatform/intelligent-plugin", "../common/src/const/domain"],
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TransferTable.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/Navigation.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/GroupTree.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../common/src/components/aoplatform/ScrollableSection.tsx", "../common/src/utils/postcat.tsx", "../common/src/utils/curl.ts", "../common/src/components/aoplatform/ResetPsw.tsx", "../common/src/components/aoplatform/SubscribeApprovalModalContent.tsx", "src/components/aoplatform/RenderRoutes.tsx", "../common/src/components/aoplatform/PublishApprovalModalContent.tsx", "../common/src/components/aoplatform/InsidePage.tsx", "../common/src/const/type.ts", "../common/src/components/aoplatform/intelligent-plugin", "../common/src/const/domain"],
"references": [{ "path": "./tsconfig.node.json" }]
}
@@ -1,17 +0,0 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_652_44370)">
<path d="M26.9998 0.501787H27.0086L27.0174 0.501479C27.3465 0.489919 27.6745 0.546231 27.981 0.66691C28.2875 0.78759 28.5658 0.970053 28.7987 1.20294C29.0316 1.43583 29.214 1.71417 29.3347 2.02062C29.4554 2.32707 29.5117 2.65508 29.5001 2.98424L29.4998 2.99301V3.00179V27.0018V27.0106L29.5001 27.0193C29.5117 27.3485 29.4554 27.6765 29.3347 27.983C29.214 28.2894 29.0316 28.5677 28.7987 28.8006C28.5658 29.0335 28.2875 29.216 27.981 29.3367C27.6745 29.4573 27.3465 29.5137 27.0174 29.5021L27.0086 29.5018H26.9998H2.99983H2.99106L2.98228 29.5021C2.65313 29.5137 2.32512 29.4573 2.01867 29.3367C1.71221 29.216 1.43388 29.0335 1.20099 28.8006C0.9681 28.5677 0.785636 28.2894 0.664957 27.983C0.544278 27.6765 0.487966 27.3485 0.499526 27.0193L0.499834 27.0106V27.0018V3.00179V2.99301L0.499526 2.98424C0.487966 2.65508 0.544278 2.32707 0.664957 2.02062C0.785636 1.71417 0.9681 1.43583 1.20099 1.20294C1.43388 0.970053 1.71221 0.787589 2.01867 0.66691C2.32512 0.546231 2.65313 0.489919 2.98228 0.501479L2.99106 0.501787H2.99983H26.9998Z" fill="#E8EFFE" stroke="#E8EFFE"/>
<rect x="12.2227" y="23.6289" width="5.53122" height="4.14832" fill="#F9D6C2"/>
<ellipse cx="22.248" cy="17.0625" rx="1.0371" ry="1.72847" fill="#FFE6D8"/>
<ellipse cx="7.72851" cy="17.0625" rx="1.0371" ry="1.72847" fill="#FFE6D8"/>
<ellipse cx="14.9882" cy="16.7166" rx="7.60543" ry="8.29663" fill="#FFE6D8"/>
<path d="M10 11.5C8.5 11.5 8.30468 13.7205 8.07421 15.3337L7.72851 10.494C7.26757 10.494 6.3457 10.0792 6.3457 8.41985C6.3457 6.76052 7.03711 6.34569 7.38281 6.34569C9.11131 6.23046 13.1214 6 15.3339 6C18.0995 6 19.1366 6 20.1738 7.38277C21.0034 8.48899 20.289 9.91786 19.8281 10.494C16.832 10.494 12.3662 11.5 10 11.5Z" fill="#333333" stroke="#333333" stroke-width="0.691394"/>
<path d="M21.9023 9.11089C23.2851 9.94055 22.709 13.144 22.248 14.642L19.1367 9.8028C19.252 9.57234 20.5195 8.28123 21.9023 9.11089Z" fill="#333333" stroke="#333333" stroke-width="0.691394"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.9891 27.7778C16.5165 27.7778 17.7547 26.8492 17.7547 25.7036C17.7547 25.536 17.7282 25.3729 17.6781 25.2168C21.3287 25.7883 23.9774 27.4926 23.9774 29.5063C23.9774 31.9882 19.9533 34.0003 14.9892 34.0003C10.0251 34.0003 6.00098 31.9882 6.00098 29.5063C6.00098 27.4926 8.64969 25.7883 12.3001 25.2168C12.2501 25.373 12.2235 25.536 12.2235 25.7036C12.2235 26.8492 13.4617 27.7778 14.9891 27.7778Z" fill="#1861F2"/>
</g>
<defs>
<clipPath id="clip0_652_44370">
<rect width="30" height="30" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -2,9 +2,10 @@ import {
ConfigProvider,
Dropdown,
MenuProps,
App} from 'antd';
App,
Button} from 'antd';
import Logo from '@common/assets/layout-logo.png';
import AvatarPic from '@common/assets/avatar_default.svg'
import AvatarPic from '@common/assets/default-avatar.png'
import { routerKeyMap, TOTAL_MENU_ITEMS } from "./Navigation";
import {Outlet, useLocation, useNavigate} from "react-router-dom";
import {useEffect, useMemo, useRef, useState} from "react";
@@ -19,6 +20,8 @@ import { ResetPsw, ResetPswHandle } from './ResetPsw.tsx';
import { BasicResponse, STATUS_CODE } from '@common/const/const.ts';
import { UserInfoType, UserProfileHandle } from '@common/const/type.ts';
import { useFetch } from '@common/hooks/http.ts';
import { QuestionCircleOutlined } from '@ant-design/icons';
import { Icon } from '@iconify/react/dist/iconify.js';
const themeToken = {
bgLayout:'#17163E;',
@@ -122,13 +125,19 @@ const themeToken = {
}
const items: MenuProps['items'] = [
{
key: '2',
label: (
<Button key="changePsw" type="text" className="border-none p-0 flex items-center bg-transparent " onClick={()=>navigator('/userProfile/changepsw')}>
</Button>)
},
{
key: '3',
label: (
<a className="block px-btnbase leading-[32px]" target="_blank" rel="noopener noreferrer" onClick={logOut}>
退
</a>
),
<Button key="logout" type="text" className="border-none p-0 flex items-center bg-transparent " onClick={logOut}>
退
</Button>)
},
];
@@ -210,19 +219,19 @@ const themeToken = {
);
},
}}
// actionsRender={(props) => {
// if (props.isMobile) return [];
// if (typeof window === 'undefined') return [];
// return [
// <Button className="mr-[20px]">
// <span className='flex items-center'><QuestionCircleOutlined className="mr-[4px]" />帮助文档</span>
// </Button>
// ];
// }}
actionsRender={(props) => {
if (props.isMobile) return [];
if (typeof window === 'undefined') return [];
return [
<Button className=" text-[#ffffffb3] hover:text-[#fff] border-none" type="default" ghost onClick={()=>{window.open('https://docs.apipark.com','_blank')}}>
<span className='flex items-center gap-[8px]'> <Icon icon="ic:baseline-help" width="14" height="14"/></span>
</Button>
];
}}
headerTitleRender={() => (
<div className="w-[192px] flex items-center">
<img
className="h-[20px] cursor-pointer"
className="h-[20px] cursor-pointer "
src={Logo}
onClick={()=> navigator(mainPage)}
/>
@@ -259,7 +268,7 @@ const themeToken = {
collapsed={false}
collapsedButtonRender={false}
>
<div className={`w-full h-calc-100vh-minus-navbar px-[40px] pt-[30px] ${currentUrl.startsWith('/role/list') ? 'overflow-auto' : 'overflow-hidden' }`}>
<div className={`w-full h-calc-100vh-minus-navbar pl-PAGE_INSIDE_X pt-PAGE_INSIDE_T ${currentUrl.startsWith('/role/list') ? 'overflow-auto' : 'overflow-hidden' }`}>
<Outlet />
</div>
</ProLayout>
@@ -34,8 +34,11 @@ export function DrawerWithFooter(props:DrawerWithFooterProps){
width="60%"
destroyOnClose={true}
maskClosable={false}
classNames={
{footer:'text-right'}
}
footer={
<Space >
<Space className="flex flex-row-reverse" style={{}}>
{showOkBtn && <WithPermission access={submitAccess}>
<Button onClick={handlerSubmit} type="primary" loading={submitLoading} disabled={submitDisabled}>
{okBtnTitle}
@@ -17,9 +17,14 @@ class InsidePageProps {
onBtnClick?:()=>void
backUrl?:string = '/'
btnAccess?:string
showBorder?:boolean = true
className?:string = ''
contentClassName?:string=''
/** 整个页面滚动 */
scrollPage?:boolean = true
}
const InsidePage:FC<InsidePageProps> = ({showBanner=true,pageTitle,tagList,showBtn,btnTitle,btnAccess,description,children,onBtnClick,backUrl})=>{
const InsidePage:FC<InsidePageProps> = ({showBanner=true,pageTitle,tagList,showBtn,btnTitle,btnAccess,description,children,onBtnClick,backUrl,showBorder=true,className='',contentClassName='',scrollPage=true})=>{
const navigate = useNavigate();
const goBack = () => {
@@ -27,27 +32,29 @@ const InsidePage:FC<InsidePageProps> = ({showBanner=true,pageTitle,tagList,showB
};
return (
// <div className="h-full flex flex-col flex-1 overflow-hidden bg-[#f7f8fa]">
<div className="h-full flex flex-col flex-1 overflow-hidden ">
{ showBanner && <div className=" mx-[4px] border-[0px] border-b-[1px] border-solid border-BORDER">
{backUrl &&<div className="text-[18px] leading-[25px] pb-[12px]">
<Button type="text" onClick={goBack}><ArrowLeftOutlined className="max-h-[14px]" /></Button>
</div>}
<div className="flex justify-between">
<div className="flex items-center">
<p className="text-theme text-[26px] pr-[10px]">{pageTitle}</p>
{tagList && tagList?.length > 0 && tagList?.map((tag)=>{
return ( <Tag className="" key={tag.label as string} bordered={false} >{tag.label}</Tag>)
})}
<div className={`h-full flex flex-col flex-1 overflow-hidden ${className}`}>
{ showBanner && <div className={`border-[0px] mr-PAGE_INSIDE_X ${showBorder ? 'border-b-[1px] border-solid border-BORDER' : ''}`}>
<div className="mb-[30px]">
{backUrl &&<div className="text-[18px] leading-[25px] mb-[12px]">
<Button type="text" onClick={goBack}><ArrowLeftOutlined className="max-h-[14px]" /></Button>
</div>}
<div className="flex justify-between mb-[20px] items-center ">
<div className="flex items-center gap-TAG_LEFT ">
<p className="text-theme text-[26px] ">{pageTitle}</p>
{tagList && tagList?.length > 0 && tagList?.map((tag)=>{
return ( <Tag key={tag.label as string} bordered={false} >{tag.label}</Tag>)
})}
</div>
{showBtn && <WithPermission access={btnAccess}><Button type="primary" onClick={()=> {
onBtnClick&&onBtnClick()
}}>{btnTitle}</Button></WithPermission>}
</div>
{showBtn && <WithPermission access={btnAccess}><Button type="primary" onClick={()=> {
onBtnClick&&onBtnClick()
}}>{btnTitle}</Button></WithPermission>}
<p >
{description}
</p>
</div>
<p className="mb-[30px]">
{description}
</p>
</div>}
<div className="h-full overflow-y-hidden">{children}</div>
<div className={`h-full ${scrollPage ? 'overflow-hidden' : 'overflow-auto'} ${contentClassName || ''}`}>{children}</div>
</div>
)
}
@@ -1,54 +0,0 @@
import { Button, Tag } from "antd"
import {useNavigate} from "react-router-dom";
import WithPermission from "@common/components/aoplatform/WithPermission";
import { FC, ReactNode } from "react";
import { ArrowLeftOutlined } from "@ant-design/icons";
class InsidePageProps {
showBanner?:boolean = true
pageTitle:string = ''
tagList?:Array<{label:string|ReactNode}> = []
children:React.ReactNode
showBtn?:boolean = false
btnTitle?:string = ''
description?:string = ''
onBtnClick?:()=>void
backUrl:string = '/'
btnAccess?:string
}
const InsidePageForHub:FC<InsidePageProps> = ({showBanner=true,pageTitle,tagList,showBtn,btnTitle,btnAccess,description,children,onBtnClick,backUrl})=>{
const navigate = useNavigate();
const goBack = () => {
navigate(backUrl);
};
return (
<div className="h-full flex flex-col flex-1 overflow-hidden max-w-[1500px] m-auto">
{ showBanner && <div className="p-btnbase mx-[4px]">
<div className="text-[18px] leading-[25px] pb-[12px]">
<Button type="text" onClick={goBack}><ArrowLeftOutlined className="max-h-[14px]" /></Button>
</div>
<div className="flex justify-between">
<div className="">
<span className="text-[26px] text-theme">{pageTitle}</span>
{tagList && tagList?.length > 0 && tagList?.map((tag)=>{
return ( <Tag key={tag.label as string} bordered={false}>{tag.label}</Tag>)
})}
</div>
{showBtn && <WithPermission access={btnAccess}><Button type="primary" onClick={()=> {
onBtnClick&&onBtnClick()
}}>{btnTitle}</Button></WithPermission>}
</div>
<p className="mb-[30px]">
{description}
</p>
</div>}
<div className="h-full overflow-y-hidden">{children}</div>
</div>
)
}
export default InsidePageForHub
@@ -4,7 +4,6 @@
}
:global .eo_page_list .ant-pro-table-list-toolbar-container{
padding:10px 20px 10px 10px !important;
.ant-pro-table-list-toolbar-right{
justify-content: flex-start;
@@ -77,8 +77,9 @@ const PageList = <T extends Record<string, unknown>>(props: React.PropsWithChild
const handleResize = () => {
if (parentRef.current && !noScroll) {
const res = parentRef.current.getBoundingClientRect();
const height = res.height - ((noTop ? 0 : 52) + 40 + (showPagination && !dragSortKey ? 52 : 0) +( besidesTableHeight ?? 0)); // 减去顶部按钮、底部分页、表头高度
setTableWidth(minTableWidth > res.width ? minTableWidth : undefined);
const height = res.height - ((noTop ? 0 : 59) + 54 + (showPagination && !dragSortKey ? 52 : 0) +( besidesTableHeight ?? 0) + 1); // 减去顶部按钮、底部分页、表头高度
setTableWidth(minTableWidth - 5> res.width ? minTableWidth : undefined);
console.log(minTableWidth,res.width )
height && setTableHeight(minVirtualHeight === undefined ? height : (height > minVirtualHeight ? height : minVirtualHeight));
}
};
@@ -112,11 +113,11 @@ const PageList = <T extends Record<string, unknown>>(props: React.PropsWithChild
const newColumns = useMemo(()=>{
let width:number = 0
const res = columns?.map(
(x)=>{
(x, index)=>{
width += Number(x.width ?? ((x.filters || x.sorter) ? 120 : 100))
x.copyable = x.copyable === false? false: true
const sorter = localStorage.getItem(`${id}_sorter`)
const filters = localStorage.getItem(`${id}_filters`)
x.copyable = x.copyable ?? (index === 0 || x.dataIndex === 'id' || x.dataIndex === 'email')
if(sorter && x.sorter){
const sorterObj = JSON.parse(sorter)
const xName = Array.isArray(x.dataIndex) ? x.dataIndex.join(','):x.dataIndex
@@ -137,7 +138,7 @@ const PageList = <T extends Record<string, unknown>>(props: React.PropsWithChild
return (
<>{
tableTitle ? <span className={`text-[30px] leading-[42px] my-mbase pl-[20px] ${tableTitleClass}`}>{tableTitle}</span> : (
addNewBtnTitle ? <WithPermission access={addNewBtnAccess} ><Button type="primary" className={`mr-btnbase ${addNewBtnWrapperClass}`} onClick={onAddNewBtnClick}>{addNewBtnTitle}</Button></WithPermission> : undefined
addNewBtnTitle ? <WithPermission access={addNewBtnAccess} ><Button type="primary" className={`mr-btnrbase my-btnbase ${addNewBtnWrapperClass}`} onClick={onAddNewBtnClick}>{addNewBtnTitle}</Button></WithPermission> : undefined
)
}
@@ -191,7 +192,7 @@ const PageList = <T extends Record<string, unknown>>(props: React.PropsWithChild
</Dropdown>):null,
]}
toolbar={{
actions:[...[beforeSearchNode],...[searchPlaceholder?<Input className="" onChange={ onSearchWordChange ? (e) => debounce(onSearchWordChange, 100)(e) : undefined } onPressEnter={()=>manualReloadTable ? manualReloadTable():actionRef.current?.reload?.()} allowClear placeholder={searchPlaceholder} prefix={<SearchOutlined className="cursor-pointer" onClick={()=>{actionRef.current?.reload?.()}}/>}/>:null]],
actions:[...[beforeSearchNode],...[searchPlaceholder?<Input className="my-btnbase ml-btnbase" onChange={ onSearchWordChange ? (e) => debounce(onSearchWordChange, 100)(e) : undefined } onPressEnter={()=>manualReloadTable ? manualReloadTable():actionRef.current?.reload?.()} allowClear placeholder={searchPlaceholder} prefix={<SearchOutlined className="cursor-pointer" onClick={()=>{actionRef.current?.reload?.()}}/>}/>:null]],
}}
options={{
reload: false,
@@ -26,19 +26,16 @@ const apiColumns = [
{
title:'API 名称',
dataIndex:'name',
copyable: true,
ellipsis:true
},
{
title:'请求方式',
dataIndex:'method',
copyable: true,
ellipsis:true
},
{
title:'路径',
dataIndex:'path',
copyable: true,
ellipsis:true
},
{
@@ -77,7 +74,6 @@ const upstreamColumns = [
title:'地址',
dataIndex:'addr',
render:(text:string[])=>(<>{text.join(',')}</>),
copyable: true,
ellipsis:true
},
{
@@ -265,7 +261,7 @@ export const PublishApprovalModalContent = forwardRef<PublishApprovalModalHandle
>
<Input.TextArea className="w-INPUT_NORMAL" disabled={type !== 'add' && type !== 'publish'} placeholder="请输入" />
</Form.Item>
{/*
{type !== 'add' && type !== 'publish' && <Form.Item
label="审批意见"
name="opinion"
@@ -277,7 +273,7 @@ export const PublishApprovalModalContent = forwardRef<PublishApprovalModalHandle
errors: [], // 设置为空数组来移除错误信息
},
]);}}/>
</Form.Item>}
</Form.Item>} */}
{['error','done'].indexOf(data.status) !== -1 && data.clusterPublishStatus &&data.clusterPublishStatus.length > 0 && <> <Row className="text-left h-[32px] mb-8px]" span={3}><span>线</span></Row>
<Row span={24} className="mb-mbase">
@@ -73,7 +73,7 @@ export const ResetPsw = forwardRef<ResetPswHandle,ResetPswProps>((props,ref)=>{
layout='vertical'
form={form}
scrollToFirstError
className="mx-auto mt-mbase "
className="mx-auto mt-mbase ml-mbase"
name="resetPsw"
// labelCol={{ span: 8 }}
// wrapperCol={{ span: 10}}
@@ -70,7 +70,7 @@ const UserAvatar: FC = () => {
{
key: '3',
label: (
<a className="block px-btnbase leading-[32px]" target="_blank" rel="noopener noreferrer" onClick={logOut}>
<a className="block leading-[32px]" target="_blank" rel="noopener noreferrer" onClick={logOut}>
退
</a>
),
@@ -9,9 +9,10 @@ type WithPermissionProps = {
tooltip?:string
children:ReactElement
disabled?:boolean
showDisabled?:boolean
}
// 权限控制的高阶组件
const WithPermission = ({access, tooltip, children,disabled}:WithPermissionProps) => {
const WithPermission = ({access, tooltip, children,disabled, showDisabled = true}:WithPermissionProps) => {
const [editAccess, setEditAccess] = useState<boolean>(access ? false:true)
const {accessData,checkPermission} = useGlobalContext()
@@ -24,16 +25,21 @@ const WithPermission = ({access, tooltip, children,disabled}:WithPermissionProps
useEffect(()=>{
// 先判断权限,无论权限是否为true,如果disabled为true时则必须为ture
access && setEditAccess(lastAccess)
disabled && setEditAccess(false)
console.log('editAccess',editAccess, children,children?.type?.displayName,showDisabled, children?.type?.displayName !== 'Button' && showDisabled)
},[lastAccess,disabled])
return (
<>
{editAccess ? cloneElement(children):
<Tooltip title={tooltip ?? "暂无操作权限,请联系管理员分配。"}>
{ cloneElement(children, {disabled:true})}
</Tooltip>
}
{editAccess && !disabled && cloneElement(children)}
{editAccess && disabled && <Tooltip title={tooltip}>
{ cloneElement(children, {disabled:true})}
</Tooltip>}
{!editAccess && (children?.type?.displayName !== 'Button' && showDisabled ) && <Tooltip title={tooltip ?? "暂无操作权限,请联系管理员分配。"}>
{ cloneElement(children, {disabled:true})}
</Tooltip>}
</>
);
}
@@ -46,6 +46,7 @@ import {createSchemaField, FormProvider, RecursionField, useField, useForm} from
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {useFetch} from "@common/hooks/http.ts";
import {App} from "antd";
import { config } from "process";
@@ -218,8 +219,8 @@ export const IntelligentPluginConfig = forwardRef<IntelligentPluginConfigHandle
placeholder: '请输入描述',
}
},
container: {
type: 'void',
config: {
type: 'object',
'x-component': 'DynamicRender',
'x-component-props': {
schema: JSON.stringify(renderSchema),
@@ -130,10 +130,9 @@ export default function IntelligentPluginList(){
message.destroy();
if(res.code === STATUS_CODE.SUCCESS){
getConfig(res.data)
setColumns(res.data.basic.fields.map((field:DynamicTableField)=>({
setColumns(res.data.basic.fields.map((field:DynamicTableField, index:number)=>({
title:field.title,
dataIndex:field.name,
copyable: true,
fixed:field.name === 'title' ? 'left' : undefined,
ellipsis:true,
width:field.name === 'title' ? 150 : undefined,
@@ -227,7 +226,7 @@ export default function IntelligentPluginList(){
const openDrawer = async (type:'add'|'edit', entity?:DynamicTableItem)=>{
switch (type){
case 'add':
setCurDetail({driver:driverOptions[0].value || '',config:{'c3ebd745-f7d5-45cd-8d3e-e0e43099d20e':{scopes:[]},'550e2537-8436-48e4-ab84-f9f58faf1b18':{scopes:[]}}})
setCurDetail({driver:driverOptions[0].value || '',config:{}})
break;
case 'edit':{
setDrawerLoading(true)
@@ -237,9 +236,6 @@ export default function IntelligentPluginList(){
const {code, data, msg } = res
if(code === STATUS_CODE.SUCCESS){
if(data.info.config){
for (const tab in data.info.config) {
data.info.config[tab]._apinto_show = true
}
}
setCurDetail(data.info)
}else{
@@ -276,7 +272,7 @@ export default function IntelligentPluginList(){
return;}
case 'delete':
title='删除'
content=<span><span className="text-status_fail"></span></span>
content=<span><span className="text-status_fail"></span></span>
break;
}
@@ -19,25 +19,21 @@ export const SUBSCRIBE_APPROVAL_TABLE_COLUMN : ProColumns<ApprovalTableListItem>
{
title: '申请方-应用',
dataIndex: ['application','name'],
copyable: true,
ellipsis:true
},
{
title: '申请服务',
dataIndex: ['service','name'],
copyable: true,
ellipsis:true
},
{
title: '服务所属系统',
dataIndex: ['service','name'],
copyable: true,
ellipsis:true
},
{
title: '服务所属团队',
dataIndex: ['team','name'],
copyable: true,
ellipsis:true
},
{
@@ -74,7 +70,6 @@ export const SUBSCRIBE_APPROVAL_INNER_TODO_TABLE_COLUMN : ProColumns<SubscribeAp
title: '申请时间',
dataIndex: 'applyTime',
// sorter: true,
copyable: true,
ellipsis:true,
width:182,
fixed:'left',
@@ -85,7 +80,6 @@ export const SUBSCRIBE_APPROVAL_INNER_TODO_TABLE_COLUMN : ProColumns<SubscribeAp
{
title: '申请方-应用',
dataIndex: ['application','name'],
copyable: true,
ellipsis:true
},
{
@@ -101,7 +95,6 @@ export const SUBSCRIBE_APPROVAL_INNER_TODO_TABLE_COLUMN : ProColumns<SubscribeAp
{
title: '申请服务',
dataIndex: ['service','name'],
copyable: true,
ellipsis:true
},
];
@@ -112,7 +105,6 @@ export const SUBSCRIBE_APPROVAL_INNER_DONE_TABLE_COLUMN : ProColumns<SubscribeAp
title: '申请时间',
dataIndex: 'applyTime',
// sorter: true,
copyable: true,
ellipsis:true,
width:182,
fixed:'left',
@@ -123,7 +115,6 @@ export const SUBSCRIBE_APPROVAL_INNER_DONE_TABLE_COLUMN : ProColumns<SubscribeAp
{
title: '申请方-应用',
dataIndex: ['application','name'],
copyable: true,
ellipsis:true
},
{
@@ -139,7 +130,6 @@ export const SUBSCRIBE_APPROVAL_INNER_DONE_TABLE_COLUMN : ProColumns<SubscribeAp
{
title: '申请服务',
dataIndex: ['service','name'],
copyable: true,
ellipsis:true
},
{
@@ -235,7 +225,6 @@ export const PUBLISH_APPROVAL_VERSION_INNER_TABLE_COLUMN : ProColumns<PublishTab
{
title: '发布版本',
dataIndex: 'version',
copyable: true,
ellipsis:true,
width:160,
fixed:'left'
@@ -243,7 +232,6 @@ export const PUBLISH_APPROVAL_VERSION_INNER_TABLE_COLUMN : ProColumns<PublishTab
{
title: '版本说明',
dataIndex: 'remark',
copyable: true,
ellipsis:true
},
{
@@ -287,7 +275,6 @@ export const PUBLISH_APPROVAL_RECORD_INNER_TABLE_COLUMN : ProColumns<PublishTabl
{
title: '申请时间',
dataIndex: 'applyTime',
copyable: true,
ellipsis:true,
width:182,
fixed:'left',
@@ -295,20 +282,17 @@ export const PUBLISH_APPROVAL_RECORD_INNER_TABLE_COLUMN : ProColumns<PublishTabl
{
title: '审核时间',
dataIndex: 'approveTime',
copyable: true,
ellipsis:true,
width:182,
},
{
title: '版本号',
dataIndex: 'version',
copyable: true,
ellipsis:true
},
{
title: '版本说明',
dataIndex: 'remark',
copyable: true,
ellipsis:true
},
{
@@ -357,7 +341,6 @@ export const PUBLISH_APPROVAL_TABLE_COLUMN : ProColumns<ApprovalTableListItem>[]
{
title: '申请时间',
dataIndex: 'applyTime',
copyable: true,
ellipsis:true,
width:182,
fixed:'left',
@@ -368,13 +351,11 @@ export const PUBLISH_APPROVAL_TABLE_COLUMN : ProColumns<ApprovalTableListItem>[]
{
title: '申请系统',
dataIndex: ['service','name'],
copyable: true,
ellipsis:true
},
{
title: '所属团队',
dataIndex: ['team','name'],
copyable: true,
ellipsis:true
},
{
@@ -109,17 +109,22 @@ export const GlobalProvider: FC<{children:ReactNode}> = ({ children }) => {
const [pluginAccessDictionary, setPluginAccessDictionary] = useState<{[k:string]:string}>({})
const [teamDataFlushed, setTeamDataFlushed] = useState<boolean>(false)
const [accessInit, setAccessInit] = useState<boolean>(false)
let getGlobalAccessPromise: Promise<BasicResponse<{ access:string[] }>> | null = null
const getGlobalAccessData = ()=>{
fetchData<BasicResponse<{ access:string[]}>>('profile/permission/system',{method:'GET'},).then(response=>{
getGlobalAccessPromise = new Promise((resolve, reject) => fetchData<BasicResponse<{ access:string[]}>>('profile/permission/system',{method:'GET'},).then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
setAccessInit(true)
setAccessData(prevData => new Map(prevData).set('system', data.access))
resolve(data.response)
}else{
message.error(msg || '操作失败')
reject(data.msg || '操作失败')
}
})
)
return getGlobalAccessData
}
const getTeamAccessData = (teamId:string)=>{
@@ -149,7 +154,13 @@ export const GlobalProvider: FC<{children:ReactNode}> = ({ children }) => {
setPluginAccessDictionary({})
}
const checkPermission = (access:keyof typeof PERMISSION_DEFINITION[0] | Array<keyof typeof PERMISSION_DEFINITION[0]>)=>{
const checkPermission = async (access:keyof typeof PERMISSION_DEFINITION[0] | Array<keyof typeof PERMISSION_DEFINITION[0]>)=>{
if( !accessInit && getGlobalAccessPromise){
await getGlobalAccessPromise
}
if( !accessInit && !getGlobalAccessPromise){
await getGlobalAccessData()
}
let revs = false;
if (Array.isArray(access)) {
revs = access.some(item => checkAccess(item, accessData));
@@ -75,6 +75,10 @@ module.exports = {
DEFAULT_BORDER_RADIUS: 'var(--border-radius)',
TREE_TITLE:'var(--small-padding) var(--LAYOUT_PADDING);',
'navbar-height': 'var(--layout-header-height)',
TAG_LEFT:'10px',
PAGE_INSIDE_X:'40px',
PAGE_INSIDE_T:'30px',
PAGE_INSIDE_B:'20px',
},
borderColor: {
'color-base': 'var(--border-color)'
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

+1 -1
View File
@@ -80,7 +80,7 @@
.apipark-layout-layout{
.apipark-layout-layout-bg-list{
background:#17163E;
background-image: radial-gradient(circle farthest-corner at 450px 350px, #050eb7, #17163e 500px);
}
.ant-layout-header.apipark-layout-layout-header{
backdrop-filter: unset !important;
@@ -379,6 +379,16 @@ const PUBLIC_ROUTES:RouteConfig[] = [
key:uuidv4()
}]
},
{
path:'userProfile/*',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/UserProfile.tsx')),
key:uuidv4(),
children:[{
path:'changepsw',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/ChangePsw.tsx')),
key:uuidv4()
}]
}
]
},
@@ -7,7 +7,6 @@ export const MEMBER_TABLE_COLUMNS: ProColumns<MemberTableListItem>[] = [
{
title: '用户名',
dataIndex: 'name',
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -18,13 +17,11 @@ export const MEMBER_TABLE_COLUMNS: ProColumns<MemberTableListItem>[] = [
{
title: '邮箱',
dataIndex: 'email',
copyable: true,
ellipsis:true,
},
{
title: '部门',
dataIndex: 'department',
copyable: true,
ellipsis:true,
filterMode:'tree',
renderText:(_,entity:MemberTableListItem)=>(entity.department?.map(x=>x.name).join('') || '-'),
@@ -36,7 +33,6 @@ export const MEMBER_TABLE_COLUMNS: ProColumns<MemberTableListItem>[] = [
{
title: '角色',
dataIndex: 'roles',
copyable: true,
ellipsis:true,
width:200
},
@@ -8,7 +8,6 @@ export const PARTITION_CERT_TABLE_COLUMNS: ProColumns<PartitionCertTableListItem
{
title: '证书',
dataIndex: 'name',
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -22,17 +21,16 @@ export const PARTITION_CERT_TABLE_COLUMNS: ProColumns<PartitionCertTableListItem
renderText:(_,entity) =>(
entity.domains.join(',')
),
copyable: true,
ellipsis:true
},
{
title: '证书有效期',
title: '过期日期',
ellipsis: true,
dataIndex: 'notAfter',
copyable: true,
width:320,
renderText: (value:string,entity:PartitionCertTableListItem) => {
return `${entity.notBefore} - ${entity.notAfter}`
width:100,
renderText: (value: string) => value ? value.split(' ')?.[0] : '-',
sorter: (a,b)=> {
return a.notAfter.localeCompare(b.notAfter)
},
},
{
@@ -60,7 +58,6 @@ export const PARTITION_CLUSTER_TABLE_COLUMNS : ProColumns<PartitionClusterTableL
{
title: '集群名称',
dataIndex: 'name',
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -72,7 +69,6 @@ export const PARTITION_CLUSTER_TABLE_COLUMNS : ProColumns<PartitionClusterTableL
title: '集群 ID',
dataIndex: 'id',
width: 140,
copyable: true,
ellipsis:true
},
{
@@ -90,7 +86,6 @@ export const PARTITION_CLUSTER_TABLE_COLUMNS : ProColumns<PartitionClusterTableL
{
title: '描述',
dataIndex: 'description',
copyable: true,
ellipsis:true
}
];
@@ -100,7 +95,6 @@ export const PARTITION_CLUSTER_NODE_COLUMNS: ProColumns<PartitionClusterNodeTabl
{
title: '节点名称',
dataIndex: 'name',
copyable: true,
ellipsis:true,
fixed:'left',
sorter: (a,b)=> {
@@ -159,7 +153,6 @@ export const PARTITION_LIST_COLUMNS: ProColumns<PartitionTableListItem>[] = [
{
title: '环境名称',
dataIndex: 'name',
copyable: true,
ellipsis:true,
fixed:'left',
sorter: (a,b)=> {
@@ -169,7 +162,6 @@ export const PARTITION_LIST_COLUMNS: ProColumns<PartitionTableListItem>[] = [
{
title: 'ID',
dataIndex: 'id',
copyable: true,
ellipsis:true,
width:140,
},
@@ -86,9 +86,15 @@ export type NodeModalFieldType = {
export type NodeModalHandle = {
save:()=>Promise<boolean|string>
save:()=>void
}
export type NodeModalPropsType = {
changeStatus:(status:ClusterPageShowStatus)=>void
getClusterInfo:()=>void
status:ClusterPageShowStatus
}
export type ClusterPageShowStatus = 'view'|'preview'|'edit'
export type PartitionTableListItem = {
id:string;
name: string;
@@ -3,7 +3,6 @@ export const ROLE_TABLE_COLUMNS = [
{
title: '角色名称',
dataIndex: 'name',
copyable: true,
ellipsis:true,
fixed:'left',
sorter: (a,b)=> {
@@ -74,7 +74,6 @@ export const SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
{
title: '服务名称',
dataIndex: 'name',
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -86,13 +85,11 @@ export const SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
title: '服务 ID',
dataIndex: 'id',
width: 140,
copyable: true,
ellipsis:true,
},
{
title: '所属团队',
dataIndex: ['team','name'],
copyable: true,
ellipsis:true,
// filters: true,
// onFilter: true,
@@ -134,7 +131,6 @@ export const SYSTEM_SUBSERVICE_TABLE_COLUMNS: ProColumns<SystemSubServiceTableLi
{
title: '服务名称',
dataIndex: ['service','name'],
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -146,7 +142,6 @@ export const SYSTEM_SUBSERVICE_TABLE_COLUMNS: ProColumns<SystemSubServiceTableLi
title: '服务 ID',
dataIndex: ['service','name'],
width: 140,
copyable: true,
ellipsis:true
},
{
@@ -170,13 +165,11 @@ export const SYSTEM_SUBSERVICE_TABLE_COLUMNS: ProColumns<SystemSubServiceTableLi
{
title: '所属服务',
dataIndex: ['project','name'],
copyable: true,
ellipsis:true
},
{
title: '所属团队',
dataIndex: ['team','name'],
copyable: true,
ellipsis:true
},
{
@@ -217,7 +210,6 @@ export const SYSTEM_SUBSCRIBER_TABLE_COLUMNS: ProColumns<SystemSubscriberTableLi
{
title: '服务名称',
dataIndex: ['service','name'],
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -229,19 +221,16 @@ export const SYSTEM_SUBSCRIBER_TABLE_COLUMNS: ProColumns<SystemSubscriberTableLi
title: '服务 ID',
dataIndex: 'id',
width: 140,
copyable: true,
ellipsis:true
},
{
title: '订阅方',
dataIndex: ['subscriber','name'],
copyable: true,
ellipsis:true
},
{
title: '所属团队',
dataIndex: ['team','name'],
copyable: true,
ellipsis:true
},
// {
@@ -307,7 +296,6 @@ export const SYSTEM_MEMBER_TABLE_COLUMN: ProColumns<SystemMemberTableListItem>[]
{
title: '用户名',
dataIndex: ['user','name'],
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -318,13 +306,11 @@ export const SYSTEM_MEMBER_TABLE_COLUMN: ProColumns<SystemMemberTableListItem>[]
{
title: '邮箱',
dataIndex: 'email',
copyable: true,
ellipsis:true
},
{
title: '角色',
dataIndex: ['roles','name'],
copyable: true,
ellipsis:true
}
@@ -374,7 +360,6 @@ export const SYSTEM_API_TABLE_COLUMNS: ProColumns<SystemApiTableListItem>[] = [
{
title: '名称',
dataIndex: 'name',
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -401,7 +386,6 @@ export const SYSTEM_API_TABLE_COLUMNS: ProColumns<SystemApiTableListItem>[] = [
{
title: 'URL',
dataIndex: 'requestPath',
copyable: true,
ellipsis:true
},
{
@@ -429,7 +413,6 @@ export const SYSTEM_UPSTREAM_TABLE_COLUMNS: ProColumns<SystemUpstreamTableListIt
{
title: '名称',
dataIndex: 'name',
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -441,7 +424,6 @@ export const SYSTEM_UPSTREAM_TABLE_COLUMNS: ProColumns<SystemUpstreamTableListIt
title: '上游 ID',
dataIndex: 'id',
width: 140,
copyable: true,
ellipsis:true
},
{
@@ -583,7 +565,6 @@ export const SYSTEM_MYSERVICE_API_TABLE_COLUMNS: ProColumns<ServiceApiTableListI
{
title: '名称',
dataIndex: 'name',
copyable: true,
width:160,
fixed:'left',
ellipsis:true
@@ -596,13 +577,11 @@ export const SYSTEM_MYSERVICE_API_TABLE_COLUMNS: ProColumns<ServiceApiTableListI
{
title: '请求路径',
dataIndex: 'path',
copyable: true,
ellipsis:true
},
{
title: '描述',
dataIndex: 'description',
copyable: true,
ellipsis:true
}
];
@@ -625,7 +604,6 @@ export const SYSTEM_AUTHORITY_TABLE_COLUMNS: ProColumns<SystemAuthorityTableList
{
title: '名称',
dataIndex: 'name',
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -702,7 +680,6 @@ export const SYSTEM_MYSERVICE_TABLE_COLUMNS: ProColumns<MyServiceTableListItem>[
{
title: '服务名称',
dataIndex: 'name',
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -714,7 +691,6 @@ export const SYSTEM_MYSERVICE_TABLE_COLUMNS: ProColumns<MyServiceTableListItem>[
title: '服务ID',
dataIndex: 'id',
width: 140,
copyable: true,
ellipsis:true
},
{
@@ -796,7 +772,6 @@ export const SYSTEM_UPSTREAM_GLOBAL_CONFIG_TABLE_COLUMNS: ProColumns<GlobalNodeI
},
],
},
copyable: true,
ellipsis:true
},
{
@@ -928,7 +903,7 @@ const APP_MODE = import.meta.env.VITE_APP_MODE;
export const SYSTEM_PAGE_MENU_ITEMS: MenuProps['items'] = [
getItem('内部数据服务', 'assets', null,
getItem('服务', 'assets', null,
[
getItem(<Link to="./api">API</Link>, 'api',undefined,undefined,undefined,'team.service.api.view'),
getItem(<Link to="./upstream"></Link>, 'upstream',undefined,undefined,undefined,'team.service.upstream.view'),
@@ -936,7 +911,7 @@ const APP_MODE = import.meta.env.VITE_APP_MODE;
getItem(<Link to="./publish"></Link>, 'publish',undefined,undefined,undefined,'team.service.release.view'),
],
'group'),
getItem('提供服务', 'provideSer', null,
getItem('订阅管理', 'provideSer', null,
[
getItem(<Link to="./approval"></Link>, 'approval',undefined,undefined,undefined,'team.service.subscription.view'),
getItem(<Link to="./subscriber"></Link>, 'subscriber',undefined,undefined,undefined,'team.service.subscription.view'),
@@ -56,8 +56,7 @@ export type SystemSubscriberTableListItem = {
};
export type SystemSubscriberConfigFieldType = {
service:string
subscriber:string
application:string
applier:string
};
@@ -12,7 +12,6 @@ export const TEAM_TABLE_COLUMNS: ProColumns<TeamTableListItem>[] = [
{
title: '名称',
dataIndex: 'name',
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -24,13 +23,11 @@ export const TEAM_TABLE_COLUMNS: ProColumns<TeamTableListItem>[] = [
title: 'ID',
dataIndex: 'id',
width: 140,
copyable: true,
ellipsis:true
},
{
title: '描述',
dataIndex: 'description',
copyable: true,
ellipsis:true
},
{
@@ -67,7 +64,6 @@ export const TEAM_SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
{
title: '服务名称',
dataIndex: 'name',
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -79,13 +75,11 @@ export const TEAM_SYSTEM_TABLE_COLUMNS: ProColumns<SystemTableListItem>[] = [
title: '服务 ID',
dataIndex: 'id',
width: 140,
copyable: true,
ellipsis:true
},
{
title: '所属团队',
dataIndex: ['team','name'],
copyable: true,
ellipsis:true
},
{
@@ -128,7 +122,6 @@ export const TEAM_MEMBER_TABLE_COLUMNS: ProColumns<TeamMemberTableListItem>[] =
{
title: '姓名',
dataIndex: ['user','name'],
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -139,7 +132,6 @@ export const TEAM_MEMBER_TABLE_COLUMNS: ProColumns<TeamMemberTableListItem>[] =
{
title: '团队角色',
dataIndex: 'roles',
copyable: true,
ellipsis:true,
},
{
@@ -8,7 +8,6 @@ export const USER_LIST_COLUMNS: ProColumns<MemberItem>[]= [
{
title: '用户名',
dataIndex: 'name',
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -19,7 +18,6 @@ export const USER_LIST_COLUMNS: ProColumns<MemberItem>[]= [
{
title: '邮箱',
dataIndex: 'email',
copyable: true,
ellipsis:true,
},
{
+40 -14
View File
@@ -727,13 +727,22 @@ p{
padding-left:0 !important;
padding-top:0 !important;
}
.pr-PAGE_INSIDE_X{
padding-right: 0 !important;
}
}
.ant-drawer-footer{
padding:16px 20px !important
}
}
.ant-modal-body .pr-PAGE_INSIDE_X{
padding-right: 0 !important;
}
.g6-tooltip {
padding: 10px 6px;
color: #444;
@@ -754,12 +763,22 @@ p{
height:10px !important;
}
/* 生产环境无法获取到下列样式,先写在这里 */
.eo_page_list .ant-pro-card{
margin-top: -1px;
margin-bottom: -1px;
}
.eo_page_list .ant-pro-card .ant-pro-card-body{
padding:0 !important;
}
.eo_page_list .ant-pro-table-list-toolbar-container{
padding:12px 20px 12px 12px !important;
/* padding:12px 20px 12px 12px !important; */
padding-block:0px !important;
flex-direction: row-reverse;
.ant-pro-table-list-toolbar-left{
justify-content: flex-end !important;
}
.ant-pro-table-list-toolbar-right{
flex:unset !important;
@@ -836,9 +855,10 @@ p{
}
}
/* .ant-dropdown .ant-dropdown-menu{
border-radius: 10px;
padding: 10px;
.ant-dropdown .ant-dropdown-menu{
/* border-radius: 10px;
padding: 10px; */
li.ant-dropdown-menu-item{
padding:0 !important;
>button.ant-btn{
@@ -848,13 +868,14 @@ p{
}
>span.ant-dropdown-menu-title-content{
padding:0px !important;
min-width: 80px;
/* padding:0px 12px !important;
/* min-width: 80px; */
/* padding:0px 12px !important; */
height:32px !important;
line-height: 32px !important;
.ant-btn{
width:100%;
padding:0px 12px !important;
justify-content:flex-start;
}
.ant-btn-default:not(:disabled):not(.ant-btn-disabled):hover{
color:var(--text-color) !important;
@@ -862,7 +883,7 @@ p{
}
}
} */
}
.ant-dropdown-button{
.ant-btn-link{
color:var(--text-color) !important;
@@ -1161,11 +1182,6 @@ p{
padding-bottom:60px !important;
}
/* .padding-top-20 .virtuoso-grid-list{
padding-bottom:60px !important;
padding:20px 40px 40px !important;
} */
.ant-form-item-control-input-content{
.ant-pro-card-body{
padding-bottom: 0px !important;
@@ -1198,4 +1214,14 @@ p{
/* .eo_page_list.role_table .ant-pro-table-list-toolbar-container{
padding-left:0 !important;
padding-right:0 !important;
} */
} */
div.preview-document{
p{
margin-block-start: 1em;
margin-block-end: 1em;}
}
.ant-table-wrapper .ant-table-thead th.ant-table-column-has-sorters{
transition:none !important;
}
@@ -27,7 +27,6 @@ const AUDIT_LOG_COLUMNS_CONFIG: ProColumns<AuditLogTableListItem, 'multipleSelec
dataIndex: 'operateTime',
valueType: 'dateTimeRange',
order:1,
copyable: true,
ellipsis:true,
fixed:'left',
width:182
@@ -72,7 +72,7 @@ const LogSettings = ()=>{
mode="inline"
items={menuItems}
/>
<div className={`w-full flex flex-1 flex-col h-full overflow-auto bg-MAIN_BG pt-btnbase pl-btnbase`}>
<div className={`w-full flex flex-1 flex-col h-full overflow-auto bg-MAIN_BG pt-btnbase pl-btnbase pr-PAGE_INSIDE_X pb-PAGE_INSIDE_B overflow-x-hidden`}>
<Outlet context={{accessPrefix:'system.devops.log_configuration'}}/>
</div>
</div>
@@ -232,7 +232,7 @@ const MemberList = ()=>{
return !checkAccess(permission, accessData);
};
const openModal = (type:'addMember'|'editMember'|'removeFromDep'|'addToDep'|'blocked'|'activate'|'delete',entity?:MemberTableListItem)=>{
const openModal = (type:'addMember'|'editMember'|'addToDep'|'delete',entity?:MemberTableListItem)=>{
let title:string = ''
let content:string|React.ReactNode = ''
switch (type){
@@ -244,10 +244,6 @@ const MemberList = ()=>{
title='编辑成员信息'
content=<MemberDropdownModal topGroupId={topGroupId} ref={EditMemberRef} type={type} entity={entity} />
break;
case 'removeFromDep':
title='移出当前部门'
content=<span><span className="text-status_fail"></span></span>
break;
case 'addToDep':
title='加入部门'
content=<AddToDepartment ref={AddToDepRef} selectedUserIds={selectedRowKeys as string[]} />
@@ -256,14 +252,6 @@ const MemberList = ()=>{
title='删除'
content=<span><span className="text-status_fail"></span></span>
break;
case 'blocked':
title='禁用成员'
content=<span><span className="text-status_fail"></span></span>
break;
case 'activate':
title='启用成员'
content=<span><span className="text-status_fail"></span></span>
break;
}
modal.confirm({
@@ -274,18 +262,9 @@ const MemberList = ()=>{
case 'addMember':
return AddMemberRef.current?.save().then((res)=>{if(res === true) {refreshGroup && refreshGroup();manualReloadTable()}})
case 'editMember':
//console.log('addChild')
return EditMemberRef.current?.save().then((res)=>{if(res === true){refreshGroup && refreshGroup();manualReloadTable()}})
case 'removeFromDep':
//console.log('addChild')
return handleMemberAction('removeFromDep').then((res)=>{if(res === true){refreshGroup && refreshGroup();manualReloadTable()}})
case 'addToDep':
//console.log('addToDep')
return AddToDepRef.current?.save().then((res)=>{if(res === true) {refreshGroup && refreshGroup();manualReloadTable()}})
case 'activate':
return handleMemberAction('activate').then((res)=>{if(res === true){refreshGroup && refreshGroup();manualReloadTable()}})
case 'blocked':
return handleMemberAction('blocked').then((res)=>{if(res === true){refreshGroup && refreshGroup();manualReloadTable()}})
case 'delete':
return handleMemberAction('delete').then((res)=>{if(res === true){refreshGroup && refreshGroup();manualReloadTable()}})
}
@@ -376,7 +355,7 @@ const MemberList = ()=>{
return (
<>
<PageList
<PageList
id="global_member"
ref={pageListRef}
columns={[...columns, ...operation]}
@@ -400,11 +379,11 @@ const MemberList = ()=>{
onRowClick={handleRowClick}
tableClickAccess="system.organization.member.edit"
afterNewBtn={[
memberGroupId &&<WithPermission key="removeFromDepPermission" access="system.organization.member.edit"><Button className="mr-btnbase" disabled={selectedRowKeys.length === 0} key="removeFromDep" onClick={()=>openModal('removeFromDep')}></Button></WithPermission>,
memberGroupId &&<WithPermission key="addToDepPermission" access="system.organization.member.edit"><Button className="mr-btnbase" disabled={selectedRowKeys.length === 0} key="addToDep" onClick={()=>openModal('addToDep')}></Button></WithPermission>,
memberGroupId !== 'disable' &&<WithPermission key="blockedPermission" access="system.organization.member.block"><Button className="mr-btnbase" disabled={selectedRowKeys.length === 0 || memberGroupId === 'unknown'} key="blocked" onClick={()=>openModal('blocked')}></Button></WithPermission>,
<WithPermission key="activatePermission" access="system.organization.member.block"><Button className="mr-btnbase" disabled={selectedRowKeys.length === 0} key="activate" onClick={()=>openModal('activate')}></Button></WithPermission>,
<WithPermission key="deletePermission" access="system.organization.member.delete"><Button className="mr-btnbase" disabled={selectedRowKeys.length === 0} key="delete" onClick={()=>openModal('delete')}></Button></WithPermission>,
selectedRowKeys.length > 0 && memberGroupId &&<WithPermission key="removeFromDepPermission" access="system.organization.member.edit"><Button className="mr-btnbase" key="removeFromDep" onClick={()=>handleMemberAction('removeFromDep').then((res)=>{if(res === true){refreshGroup && refreshGroup();manualReloadTable()}})}></Button></WithPermission>,
selectedRowKeys.length > 0 && memberGroupId &&<WithPermission key="addToDepPermission" access="system.organization.member.edit"><Button className="mr-btnbase" key="addToDep" onClick={()=>openModal('addToDep')}></Button></WithPermission>,
selectedRowKeys.length > 0 && memberGroupId !== 'disable' &&<WithPermission key="blockedPermission" access="system.organization.member.block"><Button className="mr-btnbase" key="blocked" onClick={()=>handleMemberAction('blocked').then((res)=>{if(res === true){refreshGroup && refreshGroup();manualReloadTable()}})}></Button></WithPermission>,
selectedRowKeys.length > 0 && <WithPermission key="activatePermission" access="system.organization.member.block"><Button className="mr-btnbase" key="activate" onClick={()=>handleMemberAction('activate').then((res)=>{if(res === true){refreshGroup && refreshGroup();manualReloadTable()}})}></Button></WithPermission>,
selectedRowKeys.length > 0 &&<WithPermission key="deletePermission" access="system.organization.member.delete"><Button className="mr-btnbase" key="delete" onClick={()=>openModal('delete')}></Button></WithPermission>,
]}
onSearchWordChange={(e) => {
setSearchWord(e.target.value)
@@ -17,6 +17,7 @@ import { checkAccess } from "@common/utils/permission.ts";
import { RenameDepModal } from "./Modal/RenameDepModal.tsx";
import { AddDepModal } from "./Modal/AddDepModal.tsx";
import { EditMemberModal } from "./Modal/EditMember.tsx";
import InsidePage from "@common/components/aoplatform/InsidePage.tsx";
const MemberPage = ()=>{
const [searchWord, setSearchWord] = useState<string>('')
@@ -244,56 +245,41 @@ const MemberPage = ()=>{
},[memberGroupId])
return (
<div className="flex flex-1 h-full flex-col ">
<div className="pb-[30px] border-0 border-b-[1px] border-solid border-BORDER">
<p className="text-theme text-[26px] mb-[20px]"></p>
<p></p>
</div>
<div className="flex flex-1">
<div className="w-[200px] border-0 border-solid border-r-[1px] border-r-BORDER">
<div className="px-btnbase pb-[0px]">
<Input className=" my-btnybase" onChange={(e) => debounce(onSearchWordChange, 100)(e.target.value)}
allowClear placeholder="搜索部门"
prefix={<SearchOutlined className="cursor-pointer"/>}/>
</div>
<div className="h-[calc(100%-52px)] overflow-auto">
<div className="h-[calc(100%-30px)] overflow-y-auto pl-[5px] pr-[10px]">
<Tree
showLine
switcherIcon={<DownOutlined />}
blockNode={true}
treeData={treeData}
selectedKeys={[selectedDepartmentId]}
expandedKeys={expandedKeys}
onExpand={(expandedKeys:Key[])=>{setExpandedKeys(expandedKeys)}}
onSelect={(selectedKeys,selectedRow) => {
if(selectedKeys.length > 0 ){
setSelectedDepartmentIds((selectedRow.node as unknown).departmentIds || [])
navigate(`/member/list${selectedKeys[0] === '-1'? '' : `/${selectedKeys[0]}`}`)
}
}}
/>
{/* <DirectoryTree
icon={<></>}
<InsidePage
pageTitle='成员'
description="设置成员和对应的角色,成员只能够看到权限范围内的功能和数据。"
>
<div className="flex flex-1 h-full w-full">
<div className="w-[200px] border-0 border-solid border-r-[1px] border-r-BORDER">
<div className="px-btnbase pb-[0px]">
<Input className=" my-btnybase" onChange={(e) => debounce(onSearchWordChange, 100)(e.target.value)}
allowClear placeholder="搜索部门"
prefix={<SearchOutlined className="cursor-pointer"/>}/>
</div>
<div className="h-[calc(100%-52px)] overflow-auto">
<div className="h-[calc(100%-30px)] overflow-y-auto pl-[5px] pr-[10px]">
<Tree
showLine
switcherIcon={<DownOutlined />}
blockNode={true}
treeData={treeData}
selectedKeys={[selectedDepartmentId]}
expandedKeys={expandedKeys}
onExpand={(expandedKeys:string[])=>{setExpandedKeys(expandedKeys)}}
onExpand={(expandedKeys:Key[])=>{setExpandedKeys(expandedKeys)}}
onSelect={(selectedKeys,selectedRow) => {
if(selectedKeys.length > 0 ){
setSelectedDepartmentIds((selectedRow.node as unknown).departmentIds || [])
navigate(`/member/list${selectedKeys[0] === '-1' ? '' : `/${selectedKeys[0]}`}`)
navigate(`/member/list${selectedKeys[0] === '-1'? '' : `/${selectedKeys[0]}`}`)
}
}}
/> */}
/>
</div>
</div>
</div>
<div className="flex-1 p-btnbase pr-PAGE_INSIDE_X overflow-x-hidden">
<Outlet context={{refreshMemberCount, selectedDepartmentIds,refreshGroup:()=>getDepartmentList()}}/>
</div>
</div>
<div className="w-[calc(100%-200px)] pl-btnbase pt-btnbase">
<Outlet context={{refreshMemberCount, selectedDepartmentIds,refreshGroup:()=>getDepartmentList()}}/>
</div>
</div>
</div>);
</InsidePage>);
}
export default MemberPage;
@@ -16,6 +16,7 @@ import TableBtnWithPermission from "@common/components/aoplatform/TableBtnWithPe
import { useGlobalContext } from "@common/contexts/GlobalStateContext.tsx";
import { checkAccess } from "@common/utils/permission.ts";
import { PERMISSION_DEFINITION } from "@common/const/permissions.ts";
import InsidePage from "@common/components/aoplatform/InsidePage.tsx";
const CertConfigModal = forwardRef<PartitionCertConfigHandle,PartitionCertConfigProps>((props, ref) => {
const { message } = App.useApp()
@@ -282,11 +283,12 @@ const PartitionInsideCert:FC = ()=>{
},[memberValueEnum])
return (
<div className="flex flex-col flex-1 h-full">
<div className="pb-[30px] pt-0">
<p className="text-theme text-[26px] mb-[20px]"></p>
<p> API SSL </p>
</div>
<InsidePage
pageTitle='证书'
description="通过为 API 服务配置和管理 SSL 证书,企业可以加密数据传输,防止敏感信息被窃取或篡改。"
showBorder={false}
contentClassName="pr-PAGE_INSIDE_X pb-PAGE_INSIDE_B"
>
<PageList
id="global_partition_cert"
ref={pageListRef}
@@ -299,7 +301,7 @@ const PartitionInsideCert:FC = ()=>{
onRowClick={(row:PartitionCertTableListItem)=>openModal('edit',row)}
tableClickAccess="system.devops.ssl_certificate.edit"
/>
</div>
</InsidePage>
)
}
@@ -1,33 +1,30 @@
import { FC, useEffect, useRef, useState} from "react";
import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx";
import {App, Button, Col, Collapse, Empty, Row, Spin, Tag} from "antd";
import {App, Button, Card, Col, Row, Spin, Tag} from "antd";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {useFetch} from "@common/hooks/http.ts";
import { NodeModalHandle, PartitionClusterNodeTableListItem } from "../../const/partitions/types.ts";
import { ClusterPageShowStatus, NodeModalHandle, PartitionClusterNodeTableListItem } from "../../const/partitions/types.ts";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { useGlobalContext } from "@common/contexts/GlobalStateContext.tsx";
import { ClusterNodeModal } from "./PartitionInsideClusterNode.tsx";
import { DownOutlined, LoadingOutlined, UpOutlined } from "@ant-design/icons";
import { checkAccess } from "@common/utils/permission.ts";
import { LoadingOutlined } from "@ant-design/icons";
import InsidePage from "@common/components/aoplatform/InsidePage.tsx";
const PartitionInsideCluster:FC = ()=> {
const {setBreadcrumb} = useBreadcrumb()
const {modal, message} = App.useApp()
const {fetchData} = useFetch()
const [nodesList, setNodesList] = useState<PartitionClusterNodeTableListItem[]>()
const [nodeData, setNodeData] = useState<PartitionClusterNodeTableListItem>()
const [loading, setLoading] = useState<boolean>(false)
const {accessData} = useGlobalContext()
const [activeKey, setActiveKey] = useState<string[]>([])
const editNodeRef = useRef<NodeModalHandle>(null)
const [showStatus, setShowStatus] = useState<ClusterPageShowStatus>('view')
const getPartitionClusterInfo = () => {
setNodesList([])
setLoading(true)
return fetchData<BasicResponse<{ nodes:PartitionClusterNodeTableListItem[] }>>('cluster/nodes', {method: 'GET',eoTransformKeys:['manager_address','service_address','peer_address']}).then(response => {
const {code, data, msg} = response
if (code === STATUS_CODE.SUCCESS) {
setNodesList(data.nodes)
setActiveKey(data.nodes.map((x:PartitionClusterNodeTableListItem)=>x.id))
data.nodes && data.nodes.length > 0 && setNodeData(data.nodes[0])
setShowStatus('view')
} else {
message.error(msg || '操作失败')
}
@@ -38,37 +35,6 @@ const PartitionInsideCluster:FC = ()=> {
})
}
const openModal = async (type:'editNode')=>{
let title:string = ''
let content:string|React.ReactNode = ''
switch(type){
case 'editNode': {
title = '重置配置'
content = <ClusterNodeModal ref={editNodeRef} />
}
break;
}
modal.confirm({
title,
content,
onOk:()=> {
switch (type){
case 'editNode':
return editNodeRef.current?.save().then((res:boolean)=>{if(res === true) getPartitionClusterInfo(); return false})
}
},
width:type === 'editNode' ? 900 : 600,
okText:'确认',
okButtonProps:{
disabled:!checkAccess('system.devops.cluster.edit', accessData)
},
cancelText:'取消',
closable:true,
icon:<></>,
})
}
useEffect(() => {
setBreadcrumb([
@@ -77,41 +43,53 @@ const PartitionInsideCluster:FC = ()=> {
getPartitionClusterInfo()
}, []);
const setClusterBtn = ()=>{
return (<>
{showStatus === 'view' && <WithPermission access="system.devops.cluster.edit" key="changeClusterConfig">
<Button type="primary" onClick={() => setShowStatus('edit')}></Button>
</WithPermission> }</>
)
}
return (
<>
<div className=" overflow-auto h-full">
<div className="pb-[30px] pt-0">
<p className="text-theme text-[26px] mb-[20px]"></p>
<p>访 API API </p>
<InsidePage
pageTitle='集群'
description="设置访问 API 的集群,让 API 在分布式环境中稳定运行,并且能够根据业务需求进行灵活扩展和优化。"
showBorder={false}
scrollPage={true}
>
<div className="flex flex-col h-full overflow-auto pb-PAGE_INSIDE_B pr-PAGE_INSIDE_X">
<Spin wrapperClassName=" h-full flex-1" indicator={<LoadingOutlined style={{ fontSize: 24 }} spin/>} spinning={loading}>
<div className="h-full overflow-auto">
<Card
classNames={{
body: 'overflow-auto',
}}
className="overflow-hidden w-full max-h-full flex flex-col justify-between"
title={<div><span className="text-MAIN_TEXT my-btnybase mr-btnbase" > APIPark Node</span>
{!loading && <Tag color={nodeData && nodeData.status === 1 ?'#87d068' : '#f50'}>
{ !nodeData && '未配置'}
{ nodeData?.status === 1 && '正常' }
{ nodeData?.status === 0 && '异常'}
</Tag>}</div>}
extra={setClusterBtn()}>
{showStatus === 'view'&& nodeData && ClusterConfigPreview(nodeData) }
{showStatus !== 'view' && <ClusterNodeModal ref={editNodeRef} status={showStatus} changeStatus={setShowStatus} getClusterInfo={getPartitionClusterInfo} />}
</Card>
</div>
</Spin>
</div>
<div className="pb-btnbase"> <WithPermission access="system.devops.cluster.edit" key="changeClusterConfig"><Button type="primary" onClick={() => openModal('editNode')}></Button></WithPermission></div>
<Spin wrapperClassName=" h-[calc(100%-44px)] flex-1 overflow-auto" indicator={<LoadingOutlined style={{ fontSize: 24 }} spin/>} spinning={loading}>
<div className="h-full overflow-auto ">
{nodesList && nodesList.length > 0 ?
<Collapse className={` p-[0px] mb-btnybase`}
expandIcon={({isActive})=>(isActive? <UpOutlined className="w-[23px] text-MAIN_TEXT hover:text-MAIN_HOVER_TEXT"/>:<DownOutlined className="w-[23px] text-MAIN_TEXT hover:text-MAIN_HOVER_TEXT"/> )}
items={nodesList?.map(x=>{
return {
label:<div ><Tag color={x.status === 1 ? '#87d068' : '#f50'}>{x.status === 1 ? '正常' : '异常'}</Tag><span className="text-MAIN_TEXT my-btnybase mr-btnbase" id={`${x.id}`}>{x.managerAddress.join(',')}</span></div>,
key:x.id,
children:<div className="p-btnbase">
<Row className="mb-[4px]"><Col className="font-bold text-right pr-[4px]" span="3"></Col><Col>{x.managerAddress.map(m=>(<p className="leading-[22px]">{m}</p>))}</Col></Row>
<Row className="mb-[4px]"><Col className="font-bold text-right pr-[4px]" span="3"></Col><Col>{x.serviceAddress.map(m=>(<p className="leading-[22px]">{m}</p>))}</Col></Row>
<Row className="mb-[4px]"><Col className="font-bold text-right pr-[4px]" span="3"></Col><Col><p className="leading-[22px]">{x.peerAddress}</p></Col></Row>
</div>
}
})}
activeKey={activeKey}
onChange={(val)=>{setActiveKey(val as string[])}}
/>:<Empty className="mt-[10%]" image={Empty.PRESENTED_IMAGE_SIMPLE}/>
}
</div>
</Spin>
</div>
</InsidePage>
</>
)
}
export function ClusterConfigPreview (x:PartitionClusterNodeTableListItem){
return <div className="flex flex-col gap-[4px] ">
<Row className=""><Col className="font-bold text-right pr-[4px]"></Col><Col>{x.managerAddress.map(m=>(<p className="leading-[22px]">{m}</p>))}</Col></Row>
<Row className=""><Col className="font-bold text-right pr-[4px]"></Col><Col>{x.serviceAddress.map(m=>(<p className="leading-[22px]">{m}</p>))}</Col></Row>
<Row className=""><Col className="font-bold text-right pr-[4px]"></Col><Col><p className="leading-[22px]">{x.peerAddress}</p></Col></Row>
</div>}
export default PartitionInsideCluster
@@ -4,45 +4,54 @@ import {App, Button, Form, Input, Table} from "antd";
import {useFetch} from "@common/hooks/http.ts";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import { NODE_MODAL_COLUMNS } from "../../const/partitions/const.tsx";
import { NodeModalHandle, PartitionClusterNodeModalTableListItem, PartitionClusterNodeTableListItem, NodeModalFieldType } from "../../const/partitions/types.ts";
import { NodeModalHandle, PartitionClusterNodeModalTableListItem, PartitionClusterNodeTableListItem, NodeModalFieldType, NodeModalPropsType } from "../../const/partitions/types.ts";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { ClusterConfigPreview } from "./PartitionInsideCluster.tsx";
import { set, values } from "lodash-es";
export const ClusterNodeModal = forwardRef<NodeModalHandle>((_,ref)=>{
export const ClusterNodeModal = forwardRef<NodeModalHandle, NodeModalPropsType>((props,ref)=>{
const { message } = App.useApp()
const {changeStatus,getClusterInfo, status} = props
const [form] = Form.useForm();
const [dataSource,setDataSource] = useState<PartitionClusterNodeModalTableListItem[]>([])
const {fetchData} = useFetch()
const [addressError, setAddressError] = useState<'' | 'error'>('')
const test = ()=>{
setDataSource([])
return new Promise((resolve, reject)=>{
form.validateFields().then((value)=> {
form.validateFields().then((value)=> {
if(!value.address) {
setAddressError('error')
return
}
fetchData<BasicResponse<{ nodes: PartitionClusterNodeTableListItem[] }>>('cluster/check', {method: 'POST', eoBody: (value),eoTransformKeys:['manager_address','service_address','peer_address']}).then(response => {
const {code,data, msg} = response
if (code === STATUS_CODE.SUCCESS) {
message.success(msg || '操作成功')
setDataSource(data.nodes)
changeStatus('preview')
} else {
message.error(msg || '操作失败')
message.error(msg || '无法连接集群,请检查集群地址是否正确或防火墙配置')
setAddressError('error')
}
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=>{
console.warn(errorInfo)
})
})}
const save:()=>Promise<boolean | string> = ()=>{
return new Promise((resolve, reject)=>{
const save = ()=>{
form.validateFields().then(()=> {
fetchData<BasicResponse<null>>('cluster/reset',{method:'PUT' ,eoBody:({managerAddress:form.getFieldValue('address')}), eoTransformKeys:['managerAddress']}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
resolve(true)
getClusterInfo()
}else{
message.error(msg || '操作失败')
reject(msg || '操作失败')
}
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=>
console.warn(errorInfo))
})
}
@@ -61,32 +70,25 @@ export const ClusterNodeModal = forwardRef<NodeModalHandle>((_,ref)=>{
className="mx-auto "
autoComplete="off"
name="partitionInsideClusterNode"
>
<div className="flex items-end justify-between bg-[#fafafa] p-[10px] border-[1px] border-solid border-[#f2f2f2] rounded-[10px] gap-btnbase ">
{status === 'edit' ?
<Form.Item<NodeModalFieldType>
label="集群地址"
name="address"
className="p-0 bg-transparent rounded-none border-none flex-1"
rules={[{ required: true, message: '必填项' }]}
className="mb-0"
validateStatus={addressError}
help={addressError ? form.getFieldValue('address')? '无法连接集群,请检查集群地址是否正确或防火墙配置' : '必填项' : ''}
>
<Input placeholder="请输入" onPressEnter={()=>test()}/>
</Form.Item>
<div className="">
<Button type='primary' className="mb-[10px]" onClick={()=>test()}></Button>
</div>
</div>
{
dataSource.length > 0 &&
<Table
className="mt-btnbase"
bordered={true}
columns={NODE_MODAL_COLUMNS}
size="small"
rowKey="id"
dataSource={dataSource}
pagination={false}
/>
}
<Input placeholder="请输入" onPressEnter={()=>test()} onChange={(e)=>setAddressError(e.target?.value ? '' : 'error')}/>
</Form.Item> : dataSource && ClusterConfigPreview(dataSource?.[0] as unknown as PartitionClusterNodeTableListItem)}
<div className="flex gap-btnbase mt-[20px]">
{ status === 'edit' && <WithPermission access="system.devops.cluster.edit"><Button type="primary" onClick={test}></Button></WithPermission>}
{ status === 'preview' && <WithPermission access="system.devops.cluster.edit"><Button type="primary" onClick={save}></Button></WithPermission>}
<Button type="default" onClick={()=>{changeStatus(status === 'edit' ? 'view' :'edit'); form.resetFields()}}></Button>
</div>
</Form>
</WithPermission>
)
@@ -152,9 +152,7 @@ const RoleConfig = ()=>{
const newPermits = generateNewPermit(data.permits)
generateDependenciesMap(newPermits)
setPermissionTemplate(newPermits)
console.log(newPermits)
}else{
console.log(message)
message.error(msg || '获取权限模板失败')
}
})
@@ -196,7 +194,7 @@ const RoleConfig = ()=>{
}).catch((errInfo)=>Promise.reject(errInfo))
};
return (<div className="h-full flex flex-col overflow-hidden">
return (<div className="h-full flex flex-col overflow-hidden ">
<div className="text-[18px] leading-[25px] pb-[12px]">
<Button className="flex items-center" type="text" onClick={()=>navigateTo(-1)}><ArrowLeftOutlined className="max-h-[14px]" /><span></span></Button>
</div>
@@ -214,7 +212,7 @@ const RoleConfig = ()=>{
>
<div className="flex flex-col h-full">
<Form.Item
className=" m-btnbase"
className=" m-btnbase mr-PAGE_INSIDE_X"
name="name"
rules={[{ required: true, message: '必填项',whitespace:true }]}
>
@@ -222,7 +220,7 @@ const RoleConfig = ()=>{
</Form.Item>
<Form.Item
name="permits"
className="m-btnbase flex-1 overflow-auto"
className="m-btnbase mr-0 flex-1 overflow-auto pr-PAGE_INSIDE_X"
>
<PermissionCollapse permissionTemplate={permissionTemplate!} dependenciesMap={dependenciesMap} />
</Form.Item>
@@ -1,4 +1,4 @@
import { App, Divider} from "antd";
import { App} from "antd";
import PageList from "@common/components/aoplatform/PageList.tsx";
import { useEffect, useRef,} from "react";
import {ActionType, ProColumns} from "@ant-design/pro-components";
@@ -12,6 +12,7 @@ import { useGlobalContext } from "@common/contexts/GlobalStateContext.tsx";
import { checkAccess } from "@common/utils/permission.ts";
import { useNavigate } from "react-router-dom";
import { RoleTableListItem } from "@core/const/role/type.ts";
import InsidePage from "@common/components/aoplatform/InsidePage.tsx";
const RoleList = ()=>{
@@ -116,46 +117,50 @@ const RoleList = ()=>{
}, []);
return (<>
<div className="flex flex-col pb-[20px]">
<div className="pb-[30px] ">
<p className="text-theme text-[26px] mb-[20px]"></p>
<p></p>
</div>
<h3 className="p "></h3>
<PageList
id="global_role"
tableClass="role_table mb-btnrbase"
ref={pageListRef}
columns={[...ROLE_TABLE_COLUMNS as ProColumns<RoleTableListItem, "text">[], ...operation('system')]}
request={()=>getRoleList('system')}
addNewBtnTitle="添加角色"
showPagination={false}
onAddNewBtnClick={() => {
navigateTo(`/role/system/config`)
}}
noScroll={true}
addNewBtnAccess="system.organization.role.system.add"
onRowClick={(row:RoleTableListItem)=> navigateTo(`/role/system/config/${row.id}`)}
tableClickAccess="system.organization.role.system.edit"
/>
<h3 className=" pt-btnbase "></h3>
<PageList
id="global_role"
ref={pageListRef}
tableClass="role_table "
columns={[...ROLE_TABLE_COLUMNS as ProColumns<RoleTableListItem, "text">[], ...operation('team')]}
request={()=>getRoleList('team')}
showPagination={false}
addNewBtnTitle="添加角色"
onAddNewBtnClick={() => {
navigateTo(`/role/team/config`)
}}
noScroll={true}
addNewBtnAccess="system.organization.role.team.add"
onRowClick={(row:RoleTableListItem)=> navigateTo(`/role/team/config/${row.id}`)}
tableClickAccess="system.organization.role.team.edit"
/>
</div>
<InsidePage
className="pb-PAGE_INSIDE_B overflow-y-auto"
pageTitle='角色'
description="设置角色的权限范围。"
showBorder={false}
scrollPage={false}
>
<div className="pr-PAGE_INSIDE_X">
<h3 className="mt-0"></h3>
<PageList
id="global_role"
tableClass="role_table mb-btnrbase"
ref={pageListRef}
columns={[...ROLE_TABLE_COLUMNS as ProColumns<RoleTableListItem, "text">[], ...operation('system')]}
request={()=>getRoleList('system')}
addNewBtnTitle="添加角色"
showPagination={false}
onAddNewBtnClick={() => {
navigateTo(`/role/system/config`)
}}
noScroll={true}
addNewBtnAccess="system.organization.role.system.add"
onRowClick={(row:RoleTableListItem)=> navigateTo(`/role/system/config/${row.id}`)}
tableClickAccess="system.organization.role.system.edit"
/>
<h3 className=" pt-btnbase "></h3>
<PageList
id="global_role"
ref={pageListRef}
tableClass="role_table "
columns={[...ROLE_TABLE_COLUMNS as ProColumns<RoleTableListItem, "text">[], ...operation('team')]}
request={()=>getRoleList('team')}
showPagination={false}
addNewBtnTitle="添加角色"
onAddNewBtnClick={() => {
navigateTo(`/role/team/config`)
}}
noScroll={true}
addNewBtnAccess="system.organization.role.team.add"
onRowClick={(row:RoleTableListItem)=> navigateTo(`/role/team/config/${row.id}`)}
tableClickAccess="system.organization.role.team.edit"
/>
</div>
</InsidePage>
</>)
}
export default RoleList;
@@ -5,7 +5,7 @@ import { PERMISSION_DEFINITION } from "@common/const/permissions";
import { useFetch } from "@common/hooks/http";
import { checkAccess } from "@common/utils/permission";
import { CategorizesType, ServiceHubCategoryConfigHandle } from "@market/const/serviceHub/type";
import { App, Button, Spin, TagType, Tree, TreeDataNode, TreeProps } from "antd";
import { App, Button, Spin, Tree, TreeDataNode, TreeProps } from "antd";
import { DataNode } from "antd/es/tree";
import { Key, useEffect, useMemo, useRef, useState } from "react";
import { ServiceHubCategoryConfig } from "./ServiceHubCategoryConfig";
@@ -14,6 +14,8 @@ import { useBreadcrumb } from "@common/contexts/BreadcrumbContext";
import { LoadingOutlined } from "@ant-design/icons";
import { cloneDeep } from "lodash-es";
import { Icon } from "@iconify/react/dist/iconify.js";
import InsidePage from "@common/components/aoplatform/InsidePage";
import { EntityItem } from "@common/const/type";
export default function ServiceCategory(){
const [gData, setGData] = useState<CategorizesType[]>([]);
@@ -227,7 +229,7 @@ export default function ServiceCategory(){
const getCategoryList = ()=>{
setLoading(true)
fetchData<BasicResponse<{ catalogues:CategorizesType[],tags:TagType[]}>>('catalogues',{method:'GET'}).then(response=>{
fetchData<BasicResponse<{ catalogues:CategorizesType[],tags:EntityItem[]}>>('catalogues',{method:'GET'}).then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
setGData(data.catalogues)
@@ -246,12 +248,14 @@ export default function ServiceCategory(){
},[])
return (
<div className=" mx-auto h-full">
<div className="pb-[30px] pt-0">
<p className="text-theme text-[26px] mb-[20px]"></p>
<p>便API</p>
</div>
<div className="max-h-[calc(100%-75px)] border border-solid border-BORDER p-[20px] rounded-[10px]">
<InsidePage
pageTitle='服务分类管理'
description="设置服务可选择的分类,方便团队成员快速找到API。"
showBorder={false}
contentClassName="pr-PAGE_INSIDE_X"
scrollPage={false}
>
<div className="border border-solid border-BORDER p-[20px] rounded-[10px] ">
<Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} spinning={loading} className=''>
<Tree
showIcon
@@ -267,6 +271,6 @@ export default function ServiceCategory(){
</WithPermission>
</Spin>
</div>
</div>
</InsidePage>
)
}
@@ -1,6 +1,6 @@
import {forwardRef, useEffect, useImperativeHandle, useState} from "react";
import {App, Button, Divider, Form, Input, Radio, Row, Select, TagType, TreeSelect, Upload} from "antd";
import {App, Button, Form, Input, Radio, Row, Select, TreeSelect, Upload} from "antd";
import { Link, useNavigate, useParams} from "react-router-dom";
import {RouterParams} from "@core/components/aoplatform/RenderRoutes.tsx";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
@@ -20,6 +20,7 @@ import { getImgBase64 } from "@common/utils/dataTransfer.ts";
import { CategorizesType } from "@market/const/serviceHub/type.ts";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { Icon } from "@iconify/react/dist/iconify.js";
import { useGlobalContext } from "@common/contexts/GlobalStateContext.tsx";
const MAX_SIZE = 2 * 1024; // 1KB
@@ -38,6 +39,7 @@ const SystemConfig = forwardRef<SystemConfigHandle>((_,ref) => {
const [tagOptionList, setTagOptionList] = useState<DefaultOptionType[]>([])
const [serviceClassifyOptionList, setServiceClassifyOptionList] = useState<DefaultOptionType[]>()
const [uploadLoading, setUploadLoading] = useState<boolean>(false)
const {checkPermission} = useGlobalContext()
useImperativeHandle(ref, () => ({
save:onFinish
@@ -93,11 +95,11 @@ const SystemConfig = forwardRef<SystemConfigHandle>((_,ref) => {
const getTagAndServiceClassifyList = ()=>{
setTagOptionList([])
setServiceClassifyOptionList([])
fetchData<BasicResponse<{ catalogues:CategorizesType[],tags:TagType[]}>>('catalogues',{method:'GET'}).then(response=>{
fetchData<BasicResponse<{ catalogues:CategorizesType[],tags:EntityItem[]}>>('catalogues',{method:'GET'}).then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
setTagOptionList(data.tags?.map((x:TagType)=>{return {
label:x.name, value:x.name
setTagOptionList(data.tags?.map((x:EntityItem)=>{return {
label:x.name, value:x.id
}})||[])
setServiceClassifyOptionList(data.catalogues)
@@ -127,19 +129,6 @@ const SystemConfig = forwardRef<SystemConfigHandle>((_,ref) => {
}
]
})
console.log({
...data.service,
team:data.service.team.id,
catalogue:data.service.catalogue?.id,
logoFile:[
{
uid: '-1', // 文件唯一标识
name: 'image.png', // 文件名
status: 'done', // 状态有:uploading, done, error, removed
url: data.service?.logo || '', // 图片 Base64 数据
}
]
})
setImageBase64(data.service.logo)
setShowClassify(data.service.serviceType === 'public')
},0)
@@ -170,7 +159,8 @@ const SystemConfig = forwardRef<SystemConfigHandle>((_,ref) => {
const getTeamOptionList = ()=>{
setTeamOptionList([])
fetchData<BasicResponse<{ teams: SimpleTeamItem[] }>>('simple/teams/mine',{method:'GET',eoTransformKeys:['available_partitions']}).then(response=>{
fetchData<BasicResponse<{ teams: SimpleTeamItem[] }>>(!checkPermission('system.workspace.team.view_all') ?'simple/teams/mine' :'simple/teams',{method:'GET',eoTransformKeys:[]}).then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
setTeamOptionList(data.teams?.map((x:MemberItem)=>{return {...x,
@@ -202,7 +192,7 @@ const SystemConfig = forwardRef<SystemConfigHandle>((_,ref) => {
getSystemInfo();
setBreadcrumb([
{
title: <Link to={`/service/list`}></Link>
title: <Link to={`/service/list`}></Link>
},
{
title: '设置'
@@ -238,14 +228,13 @@ const SystemConfig = forwardRef<SystemConfigHandle>((_,ref) => {
return (
<>
<div className={`h-full min-w-[570px]`}>
<WithPermission access={onEdit ? 'team.service.service.edit' :''}>
<Form
layout='vertical'
labelAlign='left'
scrollToFirstError
form={form}
className="mx-auto pb-[20px] "
className="w-full pr-PAGE_INSIDE_X "
name="systemConfig"
onFinish={onFinish}
autoComplete="off"
@@ -262,7 +251,6 @@ const SystemConfig = forwardRef<SystemConfigHandle>((_,ref) => {
<Form.Item<SystemConfigFieldType>
label="服务ID"
name="id"
extra="服务IDsys_id)可用于检索服务或日志"
rules={[{ required: true, message: '必填项' ,whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" disabled={onEdit} placeholder="请输入服务ID"/>
@@ -271,7 +259,7 @@ const SystemConfig = forwardRef<SystemConfigHandle>((_,ref) => {
<Form.Item<SystemConfigFieldType>
label="API 调用前缀"
name="prefix"
extra="选填,作为服务内所有服务的API的前缀,比如host/{sys_name}/{service_name}/{api_path},一旦保存无法修改"
extra="选填,作为服务内所有API的前缀,比如host/{service_name}/{api_path},一旦保存无法修改"
rules={[
{
validator: validateUrlSlash,
@@ -376,18 +364,19 @@ const SystemConfig = forwardRef<SystemConfigHandle>((_,ref) => {
</Row></>}
</div>
{onEdit && <>
<div className="bg-[rgb(255_120_117_/_5%)] rounded-[10px] mt-[50px] p-btnrbase pb-0">
<p className="text-left"><span className="font-bold"></span></p>
<div className="text-left">
<WithPermission access="team.service.service.delete">
<Button className="m-auto mt-[16px] mb-[20px]" type="default" danger={true} onClick={deleteSystemModal}></Button>
</WithPermission>
<WithPermission access="team.service.service.delete" showDisabled={false}>
<div className="bg-[rgb(255_120_117_/_5%)] rounded-[10px] mt-[50px] p-btnrbase pb-0">
<p className="text-left"><span className="font-bold"></span></p>
<div className="text-left">
<WithPermission access="team.service.service.delete">
<Button className="m-auto mt-[16px] mb-[20px]" type="default" danger={true} onClick={deleteSystemModal}></Button>
</WithPermission>
</div>
</div>
</div>
</WithPermission>
</>}
</Form>
</WithPermission>
</div>
</>
)
})
@@ -67,7 +67,7 @@ const ServiceInsideDocument = ()=>{
}, []);
return (
<div className="flex flex-col h-full border-[1px] rounded-[10px] border-BORDER border-solid">
<div className="flex flex-col h-full border-[1px] rounded-[10px] border-BORDER border-solid mr-PAGE_INSIDE_X">
<Editor
tinymceScriptSrc={'/tinymce/tinymce.min.js'}
initialValue={initDoc}
@@ -76,7 +76,6 @@ const SystemInsidePage:FC = ()=> {
};
useEffect(() => {
console.log(apiId, serviceId, currentUrl)
if(apiId !== undefined){
setActiveMenu('api')
}else if(serviceId !== currentUrl.split('/')[currentUrl.split('/').length - 1]){
@@ -84,7 +83,6 @@ const SystemInsidePage:FC = ()=> {
}else{
setActiveMenu('api')
}
console.log(activeMenu)
}, [currentUrl]);
useEffect(()=>{
@@ -119,7 +117,7 @@ const SystemInsidePage:FC = ()=> {
mode="inline"
items={menuData as unknown as ItemType<MenuItemType>[] }
/>
<div className={` ${['setting', 'upstream'].indexOf(activeMenu!) !== -1 ? 'pr-btnrbase' :''} w-full h-full flex flex-1 flex-col overflow-auto bg-MAIN_BG pt-[20px] pl-[20px] pb-[20px] ` }>
<div className={` ${['setting', 'upstream'].indexOf(activeMenu!) !== -1 ? '' :''} w-full h-full flex flex-1 flex-col overflow-auto bg-MAIN_BG pt-[20px] pl-[20px] pb-PAGE_INSIDE_B ` }>
<Outlet/>
</div>
</div>
@@ -126,7 +126,7 @@ const SystemInsideSubscriber:FC = ()=>{
useEffect(() => {
setBreadcrumb([
{
title:<Link to={`/service/list`}></Link>
title:<Link to={`/service/list`}></Link>
},
{
title:'订阅方管理'
@@ -151,6 +151,7 @@ const SystemInsideSubscriber:FC = ()=>{
addNewBtnTitle="新增订阅方"
onAddNewBtnClick={()=>{openModal('add')}}
addNewBtnAccess="team.service.subscription.add"
tableClass="pr-PAGE_INSIDE_X"
/>
)
}
@@ -164,12 +165,10 @@ export const SystemSubscriberConfig = forwardRef<SystemSubscriberConfigHandle,Sy
const [form] = Form.useForm();
const {fetchData} = useFetch()
const [systemOptionList, setSystemOptionList] = useState<DefaultOptionType[]>()
const [memberOptionList, setMemberOptionList] = useState<DefaultOptionType[]>()
const [subscriberTeamId, setSubscriberTeamId] = useState<string>()
const save:()=>Promise<boolean | string> = ()=>{
return new Promise((resolve, reject)=>{
form.validateFields().then((value)=>{
fetchData<BasicResponse<null>>('service/subscriber',{method:'POST',eoBody:({...value,service:serviceId}), eoParams:{service:serviceId,team:teamId}}).then(response=>{
fetchData<BasicResponse<null>>('service/subscriber',{method:'POST',eoBody:({...value}), eoParams:{service:serviceId,team:teamId}}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
@@ -195,7 +194,6 @@ export const SystemSubscriberConfig = forwardRef<SystemSubscriberConfigHandle,Sy
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
const teamMap = new Map<string, unknown>();
data.apps
.filter((x:SimpleSystemItem)=>x.id !== serviceId)
.forEach((item:SimpleSystemItem) => {
@@ -225,24 +223,6 @@ export const SystemSubscriberConfig = forwardRef<SystemSubscriberConfigHandle,Sy
})
}
useEffect(()=>{
subscriberTeamId && getMemberList()
form.setFieldValue('applier',null)
},[subscriberTeamId])
const getMemberList = ()=>{
setMemberOptionList([])
fetchData<BasicResponse<{ members: NewSimpleMemberItem[] }>>('team/members/simple',{method:'GET',eoParams:{team:subscriberTeamId}}).then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
setMemberOptionList(data.members?.map((x:NewSimpleMemberItem)=>{return {
label:x.user.name, value:x.user.id
}}))
}else{
message.error(msg || '操作失败')
}
})
}
useEffect(() => {
getSystemList()
@@ -262,7 +242,7 @@ export const SystemSubscriberConfig = forwardRef<SystemSubscriberConfigHandle,Sy
>
<Form.Item<SystemSubscriberConfigFieldType>
label="订阅方"
name="subscriber"
name="application"
rules={[{ required: true, message: '必填项' }]}
>
<TreeSelect
@@ -271,18 +251,9 @@ export const SystemSubscriberConfig = forwardRef<SystemSubscriberConfigHandle,Sy
treeData={systemOptionList}
placeholder="请选择"
treeDefaultExpandAll
onSelect={(_:unknown)=>{setSubscriberTeamId(_)}}
/>
</Form.Item>
<Form.Item
label="申请人"
name="applier"
rules={[{ required: true, message: '必填项' }]}
>
<Select className="w-INPUT_NORMAL" options={memberOptionList} placeholder="请选择"/>
</Form.Item>
</Form>
</WithPermission>)
})
@@ -11,6 +11,7 @@ import { SystemConfigHandle, SystemTableListItem } from "../../const/system/type
import { SYSTEM_TABLE_COLUMNS } from "../../const/system/const.tsx";
import { DrawerWithFooter } from "@common/components/aoplatform/DrawerWithFooter.tsx";
import SystemConfig from "./SystemConfig.tsx";
import { useGlobalContext } from "@common/contexts/GlobalStateContext.tsx";
const SystemList:FC = ()=>{
const navigate = useNavigate();
@@ -27,6 +28,7 @@ const SystemList:FC = ()=>{
const [memberValueEnum, setMemberValueEnum] = useState<{[k:string]:{text:string}}>({})
const [open, setOpen] = useState(false);
const drawerFormRef = useRef<SystemConfigHandle>(null)
const {checkPermission} = useGlobalContext()
const getSystemList = ()=>{
if(!tableHttpReload){
@@ -36,7 +38,7 @@ const SystemList:FC = ()=>{
success: true,
});
}
return fetchData<BasicResponse<{services:SystemTableListItem[]}>>('my_services',{method:'GET',eoParams:{keyword:tableSearchWord},eoTransformKeys:['api_num','service_num','create_time']}).then(response=>{
return fetchData<BasicResponse<{services:SystemTableListItem[]}>>(!checkPermission('system.workspace.service.view_all') ? 'my_services':'services',{method:'GET',eoParams:{keyword:tableSearchWord},eoTransformKeys:['api_num','service_num','create_time']}).then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
setTableListDataSource(data.services)
@@ -53,7 +55,7 @@ const SystemList:FC = ()=>{
}
const getTeamsList = ()=>{
fetchData<BasicResponse<{teams:SimpleTeamItem[]}>>('simple/teams/mine',{method:'GET'}).then(response=>{
fetchData<BasicResponse<{ teams: SimpleTeamItem[] }>>(!checkPermission('system.workspace.team.view_all') ?'simple/teams/mine' :'simple/teams',{method:'GET',eoTransformKeys:[]}).then(response=>{
const {code,data,msg} = response
setTeamList(data.teams)
if(code === STATUS_CODE.SUCCESS){
@@ -94,7 +96,7 @@ const SystemList:FC = ()=>{
getMemberList()
setBreadcrumb([
{
title: '内部数据服务'
title: '服务'
}])
}, []);
@@ -123,7 +125,7 @@ const SystemList:FC = ()=>{
return (
// <Skeleton className='m-btnbase w-[calc(100%-20px)]' loading={loading} active>
<div className="h-full w-full">
<div className="h-full w-full pr-PAGE_INSIDE_X pb-PAGE_INSIDE_B">
<PageList
id="global_system"
@@ -109,7 +109,7 @@ export default function SystemTopology() {
getNodeData()
setBreadcrumb([
{
title: <Link to={`/service/list`}></Link>
title: <Link to={`/service/list`}></Link>
},
{
title: '调用拓扑图'
@@ -176,7 +176,7 @@ const SystemInsideApiList:FC = ()=>{
useEffect(() => {
setBreadcrumb([
{
title:<Link to={`/service/list`}></Link>
title:<Link to={`/service/list`}></Link>
},
{
title:'API'
@@ -226,6 +226,7 @@ const SystemInsideApiList:FC = ()=>{
setTableHttpReload(false)
}}
onRowClick={(row:SystemApiTableListItem)=>openDrawer('view',row)}
tableClass="mr-PAGE_INSIDE_X "
/>
<DrawerWithFooter
title={drawerType === 'add' ? "添加 API":"API 详情"}
@@ -148,7 +148,7 @@ const SystemInsideApprovalList:FC = ()=>{
useEffect(() => {
setBreadcrumb([
{
title:<Link to={`/service/list`}></Link>
title:<Link to={`/service/list`}></Link>
},
{
title:'订阅审批'
@@ -182,6 +182,7 @@ const SystemInsideApprovalList:FC = ()=>{
}}
onRowClick={(row:SubscribeApprovalTableListItem)=>openModal(pageStatus === 0 ? 'approval': 'view',row)}
tableClickAccess={pageStatus === 0 ?'team.service.subscription.approval':'team.service.subscription.view'}
tableClass="pr-PAGE_INSIDE_X"
/>
</div>
)
@@ -27,7 +27,7 @@ const SystemInsidePublic:FC = ()=>{
useEffect(() => {
setBreadcrumb([
{
title:<Link to={`/service/list`}></Link>
title:<Link to={`/service/list`}></Link>
},
{
title:'发布'
@@ -354,7 +354,7 @@ const SystemInsidePublicList:FC = ()=>{
useEffect(() => {
setBreadcrumb([
{
title:<Link to={`/service/list`}></Link>
title:<Link to={`/service/list`}></Link>
},
{
title:'发布'
@@ -444,9 +444,9 @@ const SystemInsidePublicList:FC = ()=>{
onChange={() => {
setTableHttpReload(false)
}}
besidesTableHeight={58}
onRowClick={(row:PublishTableListItem|PublishVersionTableListItem)=>openDrawer('view',row)}
tableClickAccess="team.service.release.view"
tableClass="pr-PAGE_INSIDE_X"
/>
<DrawerWithFooter
destroyOnClose={true}
@@ -20,7 +20,7 @@ const DEFAULT_FORM_VALUE = {
balance:'round-robin',
limitPeerSecond:10000,
retry:3,
timeout:3,
timeout:10000,
}
const SystemInsideUpstreamContent= forwardRef<SystemInsideUpstreamContentHandle>((props,ref) => {
@@ -107,7 +107,7 @@ const globalConfigNodesRule: FormItemProps['rules'] = [
useEffect(() => {
setBreadcrumb([
{
title: <Link to={`/service/list`}></Link>
title: <Link to={`/service/list`}></Link>
},
{
title: '上游'
@@ -118,14 +118,14 @@ const globalConfigNodesRule: FormItemProps['rules'] = [
return (
<Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} spinning={loading}>
<div className={`flex-1 h-full overflow-auto`} >
<div className={`flex-1 h-full overflow-auto pr-PAGE_INSIDE_X`} >
<WithPermission access={'team.service.upstream.edit'}>
<Form
layout='vertical'
labelAlign='left'
name="systemInsideUpstreamContent"
scrollToFirstError
className="mx-auto mb-[20px] overflow-hidden"
className="mx-auto "
autoComplete="off"
form={form}
onFinish={saveUpstream}
@@ -224,7 +224,7 @@ const globalConfigNodesRule: FormItemProps['rules'] = [
</Form.Item>
<Form.Item
className="border-none bg-transparent pt-btnrbase"
className="border-none bg-transparent pt-btnrbase mb-0 pb-0"
>
<WithPermission access='team.service.upstream.edit'><Button type="primary" htmlType="submit" >
@@ -1,5 +1,5 @@
import { forwardRef, useEffect, useImperativeHandle, useState} from "react";
import {App, Button, Divider, Form, Input, Row, Select} from "antd";
import {App, Button, Form, Input, Row, Select} from "antd";
import {Link, useLocation, useNavigate, useParams} from "react-router-dom";
import {RouterParams} from "@core/components/aoplatform/RenderRoutes.tsx";
import { v4 as uuidv4 } from 'uuid'
@@ -125,13 +125,12 @@ const TeamConfig= forwardRef<TeamConfigHandle,TeamConfigProps>((props,ref) => {
setOnEdit(false);
form.setFieldsValue({id:uuidv4()}); // 清空 initialValues
}
// setPageType(currentUrl.split('/')[1] === 'myteam'? 'myteam':'manage')
return (form.setFieldsValue({}))
}, [teamId]);
return (
<>
<div className='overflow-auto h-full w-full'>
<div className='overflow-auto h-full w-full pr-PAGE_INSIDE_X'>
<WithPermission access={onEdit ?(currentUrl.split('/')[1] === 'myteam'? 'team.team.team.edit':'system.organization.team.edit') : 'system.organization.team.add'}>
<Form
layout='vertical'
@@ -191,16 +190,16 @@ const TeamConfig= forwardRef<TeamConfigHandle,TeamConfigProps>((props,ref) => {
</Row>
}
{onEdit &&
<>
<WithPermission access="system.organization.team.delete" showDisabled={false}>
<div className="bg-[rgb(255_120_117_/_5%)] rounded-[10px] mt-[50px] p-btnrbase pb-0">
<p className="text-left"><span className="font-bold"></span></p>
<div className="text-left">
<WithPermission access="system.organization.team.delete" disabled={!canDelete} tooltip={canDelete ? '':'服务数据清除后,方可删除'}>
<Button className="m-auto mt-[16px] mb-[20px]" type="default" danger onClick={()=>deleteTeam(entity!)}></Button></WithPermission>
<Button className="m-auto mt-[16px] mb-[20px]" type="default" danger onClick={()=>deleteTeam(entity!)}></Button>
</WithPermission>
</div>
</div>
</>
</WithPermission>
}
</Form>
</WithPermission>
@@ -58,7 +58,7 @@ const TeamInsideMember:FC = ()=>{
{
title: '操作',
key: 'option',
width: 76,
width: 88,
fixed:'right',
valueType: 'option',
render: (_: React.ReactNode, entity: TeamMemberTableListItem) => [
@@ -288,7 +288,7 @@ const TeamInsideMember:FC = ()=>{
request={()=>getMemberList()}
primaryKey="user.id"
addNewBtnTitle="添加成员"
className="ml-[20px] mt-[20px]"
className="ml-[20px] mt-[20px] "
searchPlaceholder="输入姓名查找"
onAddNewBtnClick={()=>{openModal('add')}}
addNewBtnAccess="team.team.member.add"
@@ -94,7 +94,8 @@ const TeamInsidePage:FC = ()=> {
tagList={[{label:
<Paragraph className="mb-0" copyable={teamId ? { text: teamId } : false}> ID{teamId || '-'}</Paragraph>
}]}
backUrl="/team/list">
backUrl="/team/list"
>
<div className="flex h-full">
<Menu
style={{ width: 220 }}
@@ -103,7 +104,7 @@ const TeamInsidePage:FC = ()=> {
onClick={onMenuClick}
selectedKeys={[activeMenu || '']}
/>
<div className={`flex flex-1 flex-col h-full overflow-auto bg-MAIN_BG pb-[20px] pt-[20px] pl-[10px] ${activeMenu === 'setting' ? ' pr-btnrbase ':''}`}>
<div className={`flex flex-1 flex-col h-full overflow-auto bg-MAIN_BG pb-PAGE_INSIDE_B pt-[20px] pl-[10px] ${activeMenu === 'setting' ? ' ':'pr-PAGE_INSIDE_X'}`}>
<Outlet />
</div>
</div>
@@ -14,6 +14,7 @@ import TableBtnWithPermission from "@common/components/aoplatform/TableBtnWithPe
import { useGlobalContext } from "@common/contexts/GlobalStateContext.tsx";
import { checkAccess } from "@common/utils/permission.ts";
import TeamConfig from "./TeamConfig.tsx";
import InsidePage from "@common/components/aoplatform/InsidePage.tsx";
const TeamList:FC = ()=>{
const [searchWord, setSearchWord] = useState<string>('')
@@ -32,7 +33,7 @@ const TeamList:FC = ()=>{
const [modalType, setModalType] = useState<'add'|'edit'>('add')
const getTeamList = ()=>{
return fetchData<BasicResponse<{teams:TeamTableListItem}>>(!checkPermission('system.organization.team.view') ? 'teams':'manager/teams',{method:'GET',eoParams:{keyword:searchWord},eoTransformKeys:['create_time','service_num','can_delete']}).then(response=>{
return fetchData<BasicResponse<{teams:TeamTableListItem}>>(!checkPermission('system.workspace.team.view_all') ? 'teams':'manager/teams',{method:'GET',eoParams:{keyword:searchWord},eoTransformKeys:['create_time','service_num','can_delete']}).then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
return {data:data.teams, success: true}
@@ -158,11 +159,12 @@ const TeamList:FC = ()=>{
return (
<div className="flex flex-col flex-1 h-full">
<div className="pb-[30px] pt-0">
<p className="text-theme text-[26px] mb-[20px]"></p>
<p>API</p>
</div>
<InsidePage
pageTitle='团队'
description="设置团队和成员,然后你可以在团队内创建服务和应用、订阅API,成员只能看到所属团队内的服务和应用。"
showBorder={false}
contentClassName=" pr-PAGE_INSIDE_X pb-PAGE_INSIDE_B"
>
<PageList
id="global_team"
className="pl-btnbase"
@@ -203,7 +205,7 @@ const TeamList:FC = ()=>{
>
<TeamConfig ref={teamConfigRef} inModal entity={modalType === 'add' ? undefined : curTeam} />
</Modal>
</div>
</InsidePage>
)
}
@@ -0,0 +1,107 @@
import { App, Button, Form, Input } from "antd";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { BasicResponse, STATUS_CODE } from "@common/const/const.ts";
import { useFetch } from "@common/hooks/http.ts";
const ChangePsw= () => {
const { message } = App.useApp()
const {fetchData} = useFetch()
const [form] = Form.useForm();
const savePsw = ()=>{
form.validateFields().then((value)=>{
return fetchData<BasicResponse<null>>(
'account/password/reset',
{
method:'PUT',
eoBody:({...value}),
}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || '操作成功!')
}else{
message.error(msg || '操作失败')
}
form.resetFields()
}).catch((errorInfo)=> {console.warn(errorInfo)})
})
}
return (
<div className={`flex-1 h-full overflow-auto pr-PAGE_INSIDE_X`} >
<WithPermission access={''}>
<Form
layout='vertical'
labelAlign='left'
name="changePsw"
scrollToFirstError
className="mx-auto pl-[10px] "
autoComplete="off"
form={form}
onFinish={savePsw}
>
<Form.Item
name="old_password"
label="旧密码"
rules={[
{
required: true,
message: '必填项',
},
]}
>
<Input.Password />
</Form.Item>
<Form.Item
name="new_password"
label="新密码"
rules={[
{
required: true,
message: '必填项',
},
]}
>
<Input.Password />
</Form.Item>
<Form.Item
name="confirm"
label="确认密码"
dependencies={['new_password']}
rules={[
{
required: true,
message: '必填项',
},
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('new_password') === value) {
return Promise.resolve();
}
return Promise.reject(new Error('两次密码不一致'));
},
}),
]}
>
<Input.Password />
</Form.Item>
<Form.Item
className="border-none bg-transparent pt-btnrbase mb-0 pb-0 pl-0"
>
<WithPermission access=''><Button type="primary" htmlType="submit" >
</Button></WithPermission>
</Form.Item>
</Form>
</WithPermission>
</div>
)
}
export default ChangePsw
@@ -0,0 +1,54 @@
import {FC, useEffect, useState} from "react";
import { Link, Outlet, useLocation, useNavigate, useParams} from "react-router-dom";
import {RouterParams} from "@core/components/aoplatform/RenderRoutes.tsx";
import { Menu} from "antd";
import InsidePage from "@common/components/aoplatform/InsidePage.tsx";
import { getItem } from "@common/utils/navigation.tsx";
const UserProfile:FC = ()=> {
const {teamId} = useParams<RouterParams>();
const location = useLocation()
const navigateTo = useNavigate()
const [activeMenu, setActiveMenu] = useState<string>()
const menuData = [
getItem(<Link to="changepsw"></Link>, 'changepsw')]
useEffect(() => {
if(location.pathname.split('/')[location.pathname.split('/').length -1] !== teamId){
setActiveMenu(location.pathname.split('/')[location.pathname.split('/').length -1].toLowerCase())
}
}, [location]);
useEffect(()=>{
if( activeMenu && teamId === location.pathname.split('/')[location.pathname.split('/').length - 1]){
navigateTo(`/userProfile/${activeMenu}`)
}
},[activeMenu])
return (
<>
<InsidePage
pageTitle='账号设置'
description="管理个人账号"
>
<div className="flex h-full">
<Menu
style={{ width: 220 }}
mode="inline"
items={menuData}
selectedKeys={[activeMenu || 'changepsw']}
/>
<div className={`flex flex-1 flex-col h-full overflow-auto bg-MAIN_BG pb-PAGE_INSIDE_B pt-[20px] pl-[10px] `}>
<Outlet />
</div>
</div>
</InsidePage>
</>
)
}
export default UserProfile
+1 -1
View File
@@ -25,6 +25,6 @@
"@market/*": ["../market/src/*"],
},
},
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TransferTable.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/Navigation.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/GroupTree.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../common/src/components/aoplatform/ScrollableSection.tsx", "../common/src/utils/postcat.tsx", "../common/src/utils/curl.ts", "../common/src/components/aoplatform/ResetPsw.tsx", "../common/src/components/aoplatform/SubscribeApprovalModalContent.tsx", "../common/src/components/aoplatform/InsidePageForHub.tsx", "src/components/aoplatform/RenderRoutes.tsx", "../common/src/components/aoplatform/PublishApprovalModalContent.tsx", "../common/src/components/aoplatform/InsidePage.tsx", "../common/src/const/type.ts", "../common/src/components/aoplatform/intelligent-plugin", "../common/src/const/domain"],
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TransferTable.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/Navigation.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/GroupTree.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../common/src/components/aoplatform/ScrollableSection.tsx", "../common/src/utils/postcat.tsx", "../common/src/utils/curl.ts", "../common/src/components/aoplatform/ResetPsw.tsx", "../common/src/components/aoplatform/SubscribeApprovalModalContent.tsx", "src/components/aoplatform/RenderRoutes.tsx", "../common/src/components/aoplatform/PublishApprovalModalContent.tsx", "../common/src/components/aoplatform/InsidePage.tsx", "../common/src/const/type.ts", "../common/src/components/aoplatform/intelligent-plugin", "../common/src/const/domain"],
"references": [{ "path": "./tsconfig.node.json" }]
}
@@ -22,7 +22,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
dataIndex: 'requestSuccess',
width: 106,
ellipsis:true,
copyable: true,
sorter: (a,b)=> {
return a.requestSuccess - b.requestSuccess
},
@@ -33,7 +32,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
eoTitle:'请求成功率',
dataIndex: 'requestRate',
valueType:'percent',
copyable: true,
ellipsis:true,
sorter: (a,b)=> {
return a.requestRate - b.requestRate
@@ -45,7 +43,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
eoTitle:'转发总数',
width: 96,
dataIndex: 'proxyTotal',
copyable: true,
ellipsis:true,
sorter: (a,b)=> {
return a.proxyTotal - b.proxyTotal
@@ -57,7 +54,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
eoTitle:'转发成功数',
width: 106,
dataIndex: 'proxySuccess',
copyable: true,
ellipsis:true,
sorter: (a,b)=> {
return a.proxySuccess - b.proxySuccess
@@ -70,7 +66,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
width: 106,
dataIndex: 'proxyRate',
valueType:'percent',
copyable: true,
ellipsis:true,
sorter: (a,b)=> {
return a.proxyRate - b.proxyRate
@@ -82,7 +77,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
eoTitle:'失败状态码数',
width: 120,
dataIndex: 'statusFail',
copyable: true,
ellipsis:true,
sorter: (a,b)=> {
return a.statusFail - b.statusFail
@@ -95,7 +89,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
width: 148,
dataIndex: 'avgResp',
valueType:'digit',
copyable: true,
ellipsis:true,
sorter: (a,b)=> {
return a.avgResp - b.avgResp
@@ -108,7 +101,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
width: 148,
dataIndex: 'maxResp',
valueType:'digit',
copyable: true,
ellipsis:true,
sorter: (a,b)=> {
return a.maxResp - b.maxResp
@@ -121,7 +113,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
width: 148,
dataIndex: 'minResp',
valueType:'digit',
copyable: true,
ellipsis:true,
sorter: (a,b)=> {
return a.minResp - b.minResp
@@ -134,7 +125,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
width: 148,
dataIndex: 'avgTraffic',
valueType:'digit',
copyable: true,
ellipsis:true,
sorter: (a,b)=> {
return a.avgTraffic - b.avgTraffic
@@ -147,7 +137,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
width: 148,
dataIndex: 'maxTraffic',
valueType:'digit',
copyable: true,
ellipsis:true,
sorter: (a,b)=> {
return a.maxTraffic - b.maxTraffic
@@ -160,7 +149,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
width: 148,
dataIndex: 'minTraffic',
valueType:'digit',
copyable: true,
ellipsis:true,
sorter: (a,b)=> {
return a.minTraffic - b.minTraffic
@@ -176,7 +164,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
dataIndex: 'name',
width:120,
ellipsis:true,
copyable:true,
fixed: 'left',
disable:true
},
@@ -185,7 +172,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
eoTitle:'请求路径',
dataIndex: 'path',
ellipsis:true,
copyable:true,
width: 80
},
{
@@ -208,7 +194,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
dataIndex: 'name',
width:160,
ellipsis:true,
copyable:true,
fixed: 'left',
disable:true
},
@@ -218,7 +203,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
dataIndex: 'id',
width: 140,
ellipsis:true,
copyable:true,
fixed: 'left'
},
...DASHBOARD_BASE_COLUMNS_CONFIG as (ProColumns<MonitorApiData>&{eoTitle:string})[]
@@ -232,7 +216,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
dataIndex: 'name',
width:160,
ellipsis:true,
copyable:true,
fixed: 'left',
disable:true
},
@@ -242,7 +225,6 @@ export const DASHBOARD_BASE_COLUMNS_CONFIG:(ProColumns<MonitorData>&{eoTitle:str
dataIndex: 'id',
width: 140,
ellipsis:true,
copyable:true,
fixed: 'left'
},
...DASHBOARD_BASE_COLUMNS_CONFIG as (ProColumns<MonitorApiData>&{eoTitle:string})[]
+2
View File
@@ -10,6 +10,8 @@
"test": "node ./__tests__/market.test.js"
},
"dependencies": {
"@types/dompurify": "^3.0.5",
"dompurify": "^3.1.6",
"react-virtuoso": "^4.7.11"
}
}
@@ -8,7 +8,6 @@ export const SERVICE_HUB_TABLE_COLUMNS: ProColumns<ServiceHubTableListItem>[] =
{
title: '服务名称',
dataIndex: 'name',
copyable: true,
ellipsis:true,
width:160,
fixed:'left',
@@ -20,7 +19,6 @@ export const SERVICE_HUB_TABLE_COLUMNS: ProColumns<ServiceHubTableListItem>[] =
title: '服务ID',
dataIndex: 'id',
width: 140,
copyable: true,
ellipsis:true
},
{
@@ -32,19 +30,16 @@ export const SERVICE_HUB_TABLE_COLUMNS: ProColumns<ServiceHubTableListItem>[] =
{
title: '所属系统',
dataIndex: ['app','name'],
copyable: true,
ellipsis:true
},
{
title: '所属团队',
dataIndex: ['team','name'],
copyable: true,
ellipsis:true
},
{
title: '服务分类',
dataIndex: ['catalogue','name'],
copyable: true,
ellipsis:true
}
];
@@ -49,10 +49,6 @@ export type CategorizesType = {
children:CategorizesType[]
}
export type TagType = {
id:string
name:string
}
export type ServiceHubTableListItem = {
+1 -1
View File
@@ -1184,4 +1184,4 @@ p{
.ant-form-item-label,
.ant-formily-item-label{
font-weight: bold;
}
}
@@ -11,11 +11,10 @@ import { EntityItem } from "@common/const/type.ts";
import { ApplyServiceModal } from "./ApplyServiceModal.tsx";
import ServiceHubApiDocument from "./ServiceHubApiDocument.tsx";
import { ApiFilled, ArrowLeftOutlined, LeftOutlined } from "@ant-design/icons";
import { Typography } from 'antd';
import { SimpleSystemItem } from "@core/const/system/type.ts";
import { Icon } from "@iconify/react/dist/iconify.js";
import DOMPurify from 'dompurify';
const { Title, Text } = Typography;
const ServiceHubDetail = ()=>{
const {serviceId} = useParams<RouterParams>();
@@ -42,7 +41,7 @@ const ServiceHubDetail = ()=>{
setServiceName(data.service.name)
setServiceDesc(data.service.description)
setApplied(data.service.applied)
setServiceDoc(data.service.document)
setServiceDoc(DOMPurify.sanitize(data.service.document))
setActiveKey(data.service.apis.map((x)=>x.id))
}else{
message.error(msg || '操作失败')
@@ -106,7 +105,7 @@ const ServiceHubDetail = ()=>{
{
key: 'introduction',
label: '介绍',
children: <><pre className="p-btnbase" dangerouslySetInnerHTML={{__html: serviceDoc || ''}}></pre></>,
children: <><div className="p-btnbase preview-document" dangerouslySetInnerHTML={{__html: serviceDoc || ''}}></div></>,
icon: <Icon icon="ic:baseline-space-dashboard" width="14" height="14"/>,
},
{
@@ -118,7 +117,7 @@ const ServiceHubDetail = ()=>{
]
return (
<section className=" grid grid-cols-5 h-full ">
<section className=" grid grid-cols-5 h-full mr-PAGE_INSIDE_X">
<section className="col-span-4 border-0 border-r-[1px] border-solid border-BORDER flex flex-col overflow-hidden">
<section className="flex flex-col gap-btnbase p-btnbase ">
@@ -128,7 +127,7 @@ const ServiceHubDetail = ()=>{
<div className="flex">
{/* <Avatar shape="square" size={50} className=" bg-[linear-gradient(135deg,white,#f0f0f0)] text-[#333] rounded-[12px]" > {service?.name?.substring(0,1)}</Avatar> */}
<Avatar shape="square" size={50}
className={ `rounded-[12px] border-none rounded-[12px] ${ serviceBasicInfo?.logo ? 'bg-[linear-gradient(135deg,white,#f0f0f0)]' : 'bg-[linear-gradient(135deg,#7F83F7,#4E54FF)]'}`}
className={ `rounded-[12px] border-none rounded-[12px] ${ serviceBasicInfo?.logo ? 'bg-[linear-gradient(135deg,white,#f0f0f0)]' : 'bg-theme'}`}
src={ serviceBasicInfo?.logo ? <img src={serviceBasicInfo?.logo} alt="Logo" style={{ maxWidth: '200px', width:'45px',height:'45px',objectFit:'unset'}}
/> : undefined}
icon={serviceBasicInfo?.logo ? '' :<iconpark-icon name="auto-generate-api"></iconpark-icon>}> </Avatar>
@@ -5,8 +5,9 @@ import {useCallback, useEffect, useState} from "react";
import Tree, {DataNode} from "antd/es/tree";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {useFetch} from "@common/hooks/http.ts";
import { CategorizesType, TagType } from "../../const/serviceHub/type.ts";
import { CategorizesType } from "../../const/serviceHub/type.ts";
import { filterServiceList, initialServiceHubListState, SERVICE_HUB_LIST_ACTIONS, ServiceHubListActionType } from "./ServiceHubList.tsx";
import { EntityItem } from "@common/const/type.ts";
type ServiceHubGroup = {
children:JSX.Element
@@ -30,21 +31,21 @@ export const ServiceHubGroup = ({children,filterOption,dispatch}:ServiceHubGroup
}
const getTagAndServiceClassifyList = ()=>{
fetchData<BasicResponse<{ catalogues:CategorizesType[],tags:TagType[]}>>('catalogues',{method:'GET'}).then(response=>{
fetchData<BasicResponse<{ catalogues:CategorizesType[],tags:EntityItem[]}>>('catalogues',{method:'GET'}).then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
dispatch({type:SERVICE_HUB_LIST_ACTIONS.GET_CATEGORIES,payload:data.catalogues})
dispatch({type:SERVICE_HUB_LIST_ACTIONS.GET_TAGS,payload:[...data.tags,{id:'empty',name:'无标签'}]})
dispatch({type:SERVICE_HUB_LIST_ACTIONS.SET_SELECTED_CATE,payload:[...data.catalogues.map((x:CategorizesType)=>x.id)]})
dispatch({type:SERVICE_HUB_LIST_ACTIONS.SET_SELECTED_TAG,payload:[...data.tags.map((x:TagType)=>x.id),'empty']})
dispatch({type:SERVICE_HUB_LIST_ACTIONS.SET_SELECTED_TAG,payload:[...data.tags.map((x:EntityItem)=>x.id),'empty']})
}else{
message.error(msg || '操作失败')
}
})
}
const transferToTreeData = useCallback((data:CategorizesType[] | TagType[] ):TreeDataNode[]=>{
const loop = (data: CategorizesType[] | TagType[] ): DataNode[] =>
const transferToTreeData = useCallback((data:CategorizesType[] | EntityItem[] ):TreeDataNode[]=>{
const loop = (data: CategorizesType[] | EntityItem[] ): DataNode[] =>
data?.map((item) => {
if ((item as CategorizesType).children) {
return {
@@ -6,11 +6,11 @@ import {useBreadcrumb} from "@common/contexts/BreadcrumbContext.tsx";
import {BasicResponse, STATUS_CODE} from "@common/const/const.ts";
import {useFetch} from "@common/hooks/http.ts";
import {RouterParams} from "@core/components/aoplatform/RenderRoutes.tsx";
import { CategorizesType, ServiceHubTableListItem, TagType } from "../../const/serviceHub/type.ts";
import { CategorizesType, ServiceHubTableListItem } from "../../const/serviceHub/type.ts";
import { VirtuosoGrid } from 'react-virtuoso';
import { ApiOutlined,LoadingOutlined } from "@ant-design/icons";
import ServiceHubGroup from "./ServiceHubGroup.tsx";
import { unset } from "lodash-es";
import { EntityItem } from "@common/const/type.ts";
export enum SERVICE_HUB_LIST_ACTIONS {
GET_CATEGORIES = 'GET_CATEGORIES',
@@ -19,33 +19,29 @@ export enum SERVICE_HUB_LIST_ACTIONS {
SET_SERVICES='SET_SERVICES',
SET_SELECTED_CATE = 'SET_SELECTED_CATE',
SET_SELECTED_TAG = 'SET_SELECTED_TAG',
SET_SELECTED_PARTITION = 'SET_SELECTED_PARTITION',
SET_KEYWORD = 'SET_KEYWORD',
LIST_LOADING = 'LIST_LOADING'
}
export type ServiceHubListActionType =
| { type: SERVICE_HUB_LIST_ACTIONS.GET_CATEGORIES, payload: CategorizesType[] }
| { type: SERVICE_HUB_LIST_ACTIONS.GET_TAGS, payload: TagType[] }
| { type: SERVICE_HUB_LIST_ACTIONS.GET_TAGS, payload: EntityItem[] }
| { type: SERVICE_HUB_LIST_ACTIONS.GET_SERVICES, payload: ServiceHubTableListItem[] }
| { type: SERVICE_HUB_LIST_ACTIONS.SET_SERVICES, payload: ServiceHubTableListItem[] }
| { type: SERVICE_HUB_LIST_ACTIONS.SET_SELECTED_CATE, payload: string[] }
| { type: SERVICE_HUB_LIST_ACTIONS.SET_SELECTED_TAG, payload: string[] }
| { type: SERVICE_HUB_LIST_ACTIONS.SET_SELECTED_PARTITION, payload: string[] }
| { type: SERVICE_HUB_LIST_ACTIONS.SET_KEYWORD, payload: string }
| { type: SERVICE_HUB_LIST_ACTIONS.LIST_LOADING, payload: boolean }
export const initialServiceHubListState = {
categoriesList: [] as CategorizesType[],
tagsList: [] as TagType[],
tagsList: [] as EntityItem[],
servicesList: [] as ServiceHubTableListItem[],
showServicesList: [] as ServiceHubTableListItem[],
selectedCate: [] as string[],
selectedTag: [] as string[],
selectedPartition: [] as string[],
keyword: '',
getCateAndTagData:false,
getPartitionData:false,
listLoading:false,
};
@@ -63,8 +59,6 @@ export const initialServiceHubListState = {
return { ...state, selectedCate: action.payload };
case SERVICE_HUB_LIST_ACTIONS.SET_SELECTED_TAG:
return { ...state, selectedTag: action.payload };
case SERVICE_HUB_LIST_ACTIONS.SET_SELECTED_PARTITION:
return { ...state, selectedPartition: action.payload };
case SERVICE_HUB_LIST_ACTIONS.SET_KEYWORD:
return { ...state, keyword: action.payload };
case SERVICE_HUB_LIST_ACTIONS.LIST_LOADING:
@@ -75,7 +69,7 @@ export const initialServiceHubListState = {
}
export const filterServiceList = (dataSet: typeof initialServiceHubListState)=>{
if(!dataSet.getCateAndTagData || !dataSet.getPartitionData){
if(!dataSet.getCateAndTagData ){
return dataSet.servicesList
}else{
return dataSet.servicesList.filter((x)=>{
@@ -83,7 +77,6 @@ export const initialServiceHubListState = {
if(!dataSet.selectedTag || dataSet.selectedTag.length === 0) return false
if((!x.tags || !x.tags.length )&& dataSet.selectedTag.indexOf('empty') === -1) return false
if(x.tags && x.tags.length && !x.tags.some(tag => dataSet.selectedTag.includes(tag.id))) return false;
if(!dataSet.selectedPartition || dataSet.selectedPartition.length === 0) return false
if( dataSet.keyword && !x.name.includes(dataSet.keyword)) return false
return true
})
@@ -143,7 +136,8 @@ const ServiceHubList:FC = ()=>{
return (
<div className="pt-[20px]">
<Card title={CardTitle(item)} className="shadow-[0_5px_10px_0_rgba(0,0,0,0.05)] rounded-[10px] overflow-visible cursor-pointer h-[180px] m-0 transition duration-500 hover:shadow-[0_5px_20px_0_rgba(0,0,0,0.15)] hover:scale-[1.05]" classNames={{header:'border-b-[0px] p-[20px] ', body:"pt-0"}} onClick={()=>showDocumentDetail(item)}>
<span className="line-clamp-3 break-all">{item.description || '暂无服务描述'}</span>
<span className="line-clamp-3 text-[12px] text-[#666] "
style={{'word-break':'auto-phrase'}}>{item.description || '暂无服务描述'}</span>
</Card>
</div>
);
@@ -186,7 +180,7 @@ const CardTitle = (service:ServiceHubTableListItem)=>{
<div className="pl-[20px] w-[calc(100%-50px)]">
<p className="text-[14px] h-[20px] leading-[20px] truncate w-full">{service.name}</p>
<div className="mt-[10px] h-[20px] flex items-center font-normal">
<Tag color="#7371fc1b" className="text-theme font-normal border-0 mr-[12px] max-w-[70px] truncate" key={service.id} bordered={false} title={service.catalogue?.name || '-'}>{service.catalogue?.name || '-'}</Tag>
<Tag color="#7371fc1b" className="text-theme font-normal border-0 mr-[12px] max-w-[150px] truncate" key={service.id} bordered={false} title={service.catalogue?.name || '-'}>{service.catalogue?.name || '-'}</Tag>
<Tooltip title='API 数量'>
<span className="mr-[12px]"><ApiOutlined className="mr-[1px] text-[14px] h-[14px] w-[14px]"/><span className="font-normal text-[14px]">{service.apiNum ?? '-'}</span></span>
@@ -142,7 +142,6 @@ const ManagementConfig = forwardRef<ManagementConfigHandle,ManagementConfigProps
<Form.Item<ManagementConfigFieldType>
label="应用 ID"
name="id"
extra="应用IDapp_id)可用于检索服务或日志"
rules={[{ required: true, message: '必填项' ,whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder="请输入" disabled={type === 'edit'}/>
@@ -168,14 +167,16 @@ const ManagementConfig = forwardRef<ManagementConfigHandle,ManagementConfigProps
{ type === 'edit' && <>
<div className="bg-[rgb(255_120_117_/_5%)] rounded-[10px] mt-[50px] p-btnrbase pb-0">
<p className="text-left"><span className="font-bold"></span></p>
<div className="text-left">
<WithPermission access="team.application.application.delete">
<Button className="m-auto mt-[16px] mb-[20px]" type="default" danger={true} onClick={deleteApplicationModal} loading={delBtnLoading}></Button>
<WithPermission access="team.application.application.delete" showDisabled={false}>
<div className="bg-[rgb(255_120_117_/_5%)] rounded-[10px] mt-[50px] p-btnrbase pb-0">
<p className="text-left"><span className="font-bold"></span></p>
<div className="text-left">
<WithPermission access="team.application.application.delete">
<Button className="m-auto mt-[16px] mb-[20px]" type="default" danger={true} onClick={deleteApplicationModal} loading={delBtnLoading}></Button>
</WithPermission>
</div>
</div>
</WithPermission>
</div>
</div>
</>}
</Form>
</WithPermission></>
@@ -25,14 +25,14 @@ export default function ServiceHubManagement() {
const [teamList, setTeamList] = useState<MenuItem[]>([])
const {setAppName} = useTenantManagementContext()
const navigateTo = useNavigate()
const {getTeamAccessData,cleanTeamAccessData} = useGlobalContext()
const {getTeamAccessData,cleanTeamAccessData,checkPermission} = useGlobalContext()
type MenuItem = Required<MenuProps>['items'][number];
const getServiceList = ()=>{
//console.log(pagination,sorter,categoryId,tagId)
setServiceLoading(true)
fetchData<BasicResponse<{apps:ServiceHubAppListItem}>>('my_apps',{method:'GET', eoParams:{ team:teamId,keyword:''},eoTransformKeys:['api_num','subscribe_num','subscribe_verify_num']}).then(response=>{
return fetchData<BasicResponse<{apps:ServiceHubAppListItem}>>(!checkPermission('system.workspace.application.view_all') ? 'my_apps':'apps',{method:'GET',eoParams:{ team:teamId,keyword:''},eoTransformKeys:['api_num','subscribe_num','subscribe_verify_num']}).then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
setServiceList([...data.apps,{type:'addNewItem'}])
@@ -51,7 +51,7 @@ const getServiceList = ()=>{
const getTeamsList = ()=>{
setPageLoading(true)
fetchData<BasicResponse<{teams:SimpleTeamItem[]}>>('simple/teams/mine',{method:'GET',eoTransformKeys:['app_num','subscribe_num']}).then(response=>{
fetchData<BasicResponse<{ teams: SimpleTeamItem[] }>>(!checkPermission('system.workspace.team.view_all') ?'simple/teams/mine' :'simple/teams',{method:'GET',eoTransformKeys:['app_num','subscribe_num']}).then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
setTeamList(data.teams.map((x:SimpleTeamItem)=>({label:<div className="flex items-center justify-between "><span className="w-[calc(100%-42px)] truncate" title={x.name}>{x.name}</span><span className="bg-[#fff] rounded-[5px] h-[20px] w-[30px] flex items-center justify-center">{x.appNum || 0}</span></div>, key:x.id})))
@@ -200,7 +200,7 @@ useEffect(() => {
const CardTitle = (service:ServiceHubAppListItem)=>{
return(
<div className="flex">
<Avatar shape="square" size={50} className=" bg-[linear-gradient(135deg,#7F83F7,#4E54FF)] rounded-[12px]" icon={<iconpark-icon className="" name="auto-generate-api"></iconpark-icon>} />
<Avatar shape="square" size={50} className=" bg-theme rounded-[12px]" icon={<iconpark-icon className="" name="auto-generate-api"></iconpark-icon>} />
<div className="pl-[20px] w-[calc(100%-50px)]">
<p className="text-[14px] h-[20px] leading-[20px] truncate">{service.name}</p>
<div className="mt-[10px] h-[20px] flex items-center font-normal">
@@ -19,9 +19,9 @@ export default function SystemRunningInstruction() {
</p> */}
<div className="flex mt-[28px]">
<div className="h-[208px] w-[384px] flex flex-col items-center py-[32px] px-[24px] gap-[16px] rounded-DEFAULT bg-MENU_BG mr-[24px]">
<p className="text-[20px] font-medium leading-[32px] text-MAIN_TEXT"></p>
<p className="text-[20px] font-medium leading-[32px] text-MAIN_TEXT"></p>
<p className="text-[12px] font-normal leading-[20px] text-DESC_TEXT"> API API </p>
<p><Link to="/service/list"></Link></p>
<p><Link to="/service/list"></Link></p>
</div>
</div>
</div></div>
+4 -3
View File
@@ -5,9 +5,9 @@ go 1.21
//toolchain go1.21.1
require (
github.com/eolinker/ap-account v1.0.6
github.com/eolinker/ap-account v1.0.9
github.com/eolinker/eosc v0.17.3
github.com/eolinker/go-common v1.0.1
github.com/eolinker/go-common v1.0.4
github.com/gabriel-vasile/mimetype v1.4.4
github.com/gin-gonic/gin v1.10.0
github.com/google/uuid v1.6.0
@@ -66,4 +66,5 @@ require (
gorm.io/driver/mysql v1.5.2 // indirect
)
//replace github.com/eolinker/ap-account v1.0.4 => ../ap-account
//replace github.com/eolinker/ap-account => ../../eolinker/ap-account
//replace github.com/eolinker/go-common => ../../eolinker/go-common
+4 -4
View File
@@ -23,12 +23,12 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eolinker/ap-account v1.0.6 h1:lzkmaItUoIguEKneUbP387qdgsUgjonKjHDnXsu6lTc=
github.com/eolinker/ap-account v1.0.6/go.mod h1:MViCOvUaS2QrVift1Be3yGjjMywzICL9317eOxoixSI=
github.com/eolinker/ap-account v1.0.9 h1:tW345b1wsn0V8pfMMlZOMfpbjxQhMWBVfgsClFTJGLk=
github.com/eolinker/ap-account v1.0.9/go.mod h1:5lsZwkQfnHO5YJ3Cu6X1PZwZ0gbmJBUcix0hxG8aEsY=
github.com/eolinker/eosc v0.17.3 h1:sr2yT+v/AsqEdciRaaZZj0zL9pTufR5RvDW6+65hraQ=
github.com/eolinker/eosc v0.17.3/go.mod h1:xgq816hpanlMXFtZw7Ztdctb1eEk9UPHchY4NfFO6Cw=
github.com/eolinker/go-common v1.0.1 h1:Uan6QmXAlPiX6hc1ptSIHWvaWXNA+VlBjC4gCaDEiz0=
github.com/eolinker/go-common v1.0.1/go.mod h1:Kb/jENMN1mApnodvRgV4YwO9FJby1Jkt2EUjrBjvSX4=
github.com/eolinker/go-common v1.0.4 h1:F0akjnzJfIFOVmK30fD0SsCLU7DAKPXuY21MeyMmQ7w=
github.com/eolinker/go-common v1.0.4/go.mod h1:Kb/jENMN1mApnodvRgV4YwO9FJby1Jkt2EUjrBjvSX4=
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
Binary file not shown.
+2 -3
View File
@@ -5,9 +5,8 @@ import (
)
const (
SystemGroup = "system"
TeamGroup = "team"
ProjectGroup = "project"
SystemGroup = "system"
TeamGroup = "team"
)
type IdentityTeamService interface {
+2
View File
@@ -51,6 +51,7 @@ func (p *PermitMiddleware) Check(method string, path string) (bool, []gin.Handle
func(ginCtx *gin.Context) {
userId := utils.UserId(ginCtx)
if userId == "" {
// 未开启游客模式
ginCtx.AbortWithStatusJSON(http.StatusForbidden, gin.H{"code": http.StatusForbidden, "msg": "not login", "success": "fail"})
ginCtx.Abort()
return
@@ -67,6 +68,7 @@ func (p *PermitMiddleware) Check(method string, path string) (bool, []gin.Handle
// 当前分组没有配置权限
continue
}
domainHandler, has := permit.SelectDomain(group)
if !has {
// 当前分组没有配置身份handler
+1
View File
@@ -6,6 +6,7 @@ type Item struct {
Id string `json:"id"`
Name string `json:"name"`
Children []*Item `json:"children"`
Sort int `json:"-"`
}
type ServiceItem struct {
+6 -1
View File
@@ -347,9 +347,10 @@ func (i *imlCatalogueModule) Services(ctx context.Context, keyword string) ([]*c
func (i *imlCatalogueModule) recurseUpdateSort(ctx context.Context, parent string, sorts []*catalogue_dto.SortItem) error {
for index, item := range sorts {
s := index
err := i.catalogueService.Save(ctx, item.Id, &catalogue.EditCatalogue{
Parent: &parent,
Sort: &index,
Sort: &s,
})
if err != nil {
return err
@@ -488,8 +489,12 @@ func treeItems(parentId string, parentMap map[string][]*catalogue.Catalogue) []*
Id: v.Id,
Name: v.Name,
Children: childItems,
Sort: v.Sort,
})
}
}
sort.Slice(items, func(i, j int) bool {
return items[i].Sort < items[j].Sort
})
return items
}
+2 -1
View File
@@ -55,6 +55,7 @@ func NewRoot(list []*catalogue.Catalogue) *Root {
Name: i.Name,
Depth: 0,
children: nil,
sort: i.Sort,
}
l = append(l, g)
m[g.Uuid] = g
@@ -137,7 +138,7 @@ func (g Groups) Less(i, j int) bool {
}
return g[i].sort < g[j].sort
}
return g[i].Parent < g[i].Parent
return g[i].Parent < g[j].Parent
}
return g[i].Depth < g[j].Depth
}
+2 -2
View File
@@ -21,8 +21,8 @@ type IDynamicModuleModule interface {
Render(ctx context.Context, module string) (map[string]interface{}, error)
ModuleDrivers(ctx context.Context, group string) ([]*dynamic_module_dto.ModuleDriver, error)
Online(ctx context.Context, module string, id string, clusterInput *dynamic_module_dto.ClusterInput) error
Offline(ctx context.Context, module string, id string, clusterInput *dynamic_module_dto.ClusterInput) error
Online(ctx context.Context, module string, id string) error
Offline(ctx context.Context, module string, id string) error
//PartitionStatuses(ctx context.Context, module string, keyword string, page int, pageSize int) (map[string]map[string]string, error)
//PartitionStatus(ctx context.Context, module string, id string) (*dynamic_module_dto.OnlineInfo, error)
}
+10 -17
View File
@@ -42,7 +42,7 @@ func (i *imlDynamicModule) initGateway(ctx context.Context, clusterId string, cl
return nil
}
func (i *imlDynamicModule) Online(ctx context.Context, module string, id string, clusterInput *dynamic_module_dto.ClusterInput) error {
func (i *imlDynamicModule) Online(ctx context.Context, module string, id string) error {
_, has := driver.Get(module)
if !has {
return fmt.Errorf("模块【%s】不存在", module)
@@ -56,7 +56,7 @@ func (i *imlDynamicModule) Online(ctx context.Context, module string, id string,
if err != nil {
return fmt.Errorf("上线失败,配置不存在")
}
clusters, err := i.clusterService.List(ctx, clusterInput.Clusters...)
clusters, err := i.clusterService.List(ctx)
if err != nil || len(clusters) == 0 {
return fmt.Errorf("上线失败,集群不存在")
}
@@ -102,28 +102,21 @@ func (i *imlDynamicModule) Online(ctx context.Context, module string, id string,
})
}
func (i *imlDynamicModule) Offline(ctx context.Context, module string, id string, clusterInput *dynamic_module_dto.ClusterInput) error {
func (i *imlDynamicModule) Offline(ctx context.Context, module string, id string) error {
_, has := driver.Get(module)
if !has {
return fmt.Errorf("模块【%s】不存在", module)
}
//if len(clusterInput.Clusters) == 0 {
// return fmt.Errorf("下线分区失败,分区为空")
//}
return i.transaction.Transaction(ctx, func(ctx context.Context) error {
id = strings.ToLower(fmt.Sprintf("%s_%s", id, module))
if len(clusterInput.Clusters) == 0 {
clusters, err := i.clusterService.List(ctx)
if err != nil {
return err
}
clusterInput.Clusters = make([]string, 0)
for _, c := range clusters {
clusterInput.Clusters = append(clusterInput.Clusters, c.Uuid)
}
clusters, err := i.clusterService.List(ctx)
if err != nil {
return err
}
for _, clusterId := range clusterInput.Clusters {
clusterIds := utils.SliceToSlice(clusters, func(s *cluster.Cluster) string {
return s.Uuid
})
for _, clusterId := range clusterIds {
err := i.dynamicClient(ctx, clusterId, module, func(dynamicClient gateway.IDynamicClient) error {
return dynamicClient.Offline(ctx, &gateway.DynamicRelease{
BasicItem: &gateway.BasicItem{
+4 -2
View File
@@ -64,14 +64,16 @@ type Member struct {
User auto.Label `json:"user" aolabel:"user"`
Roles []auto.Label `json:"roles" aolabel:"role"`
AttachTime auto.TimeLabel `json:"attach_time"`
IsDelete bool `json:"is_delete"`
}
func ToMember(model *team_member.Member, roles ...string) *Member {
func ToMember(model *team_member.Member, userId string, roles ...string) *Member {
return &Member{
User: auto.UUID(model.UID),
Roles: auto.List(roles),
AttachTime: auto.TimeLabel(model.CreateTime),
IsDelete: userId != model.UID,
}
}
+71 -24
View File
@@ -4,21 +4,21 @@ import (
"context"
"errors"
"fmt"
"github.com/eolinker/ap-account/service/role"
"gorm.io/gorm"
department_member "github.com/eolinker/ap-account/service/department-member"
"github.com/eolinker/go-common/auto"
"github.com/eolinker/ap-account/service/user"
"github.com/eolinker/go-common/store"
"github.com/APIParkLab/APIPark/service/service"
team_member "github.com/APIParkLab/APIPark/service/team-member"
team_dto "github.com/APIParkLab/APIPark/module/my-team/dto"
"github.com/APIParkLab/APIPark/service/team"
"github.com/eolinker/go-common/utils"
@@ -39,15 +39,54 @@ type imlTeamModule struct {
transaction store.ITransaction `autowired:""`
}
func (m *imlTeamModule) SimpleTeams(ctx context.Context, keyword string) ([]*team_dto.SimpleTeam, error) {
teams, err := m.teamService.Search(ctx, keyword, nil)
if err != nil {
return nil, err
}
projects, err := m.serviceService.Search(ctx, "", nil)
projectCount := make(map[string]int64)
appCount := make(map[string]int64)
for _, p := range projects {
if p.AsServer {
if _, ok := projectCount[p.Team]; !ok {
projectCount[p.Team] = 0
}
projectCount[p.Team]++
}
if p.AsApp {
if _, ok := appCount[p.Team]; !ok {
appCount[p.Team] = 0
}
appCount[p.Team]++
}
}
return utils.SliceToSlice(teams, func(s *team.Team) *team_dto.SimpleTeam {
return &team_dto.SimpleTeam{
Id: s.Id,
Name: s.Name,
Description: s.Description,
ServiceNum: projectCount[s.Id],
AppNum: appCount[s.Id],
}
}), nil
}
func (m *imlTeamModule) UpdateMemberRole(ctx context.Context, id string, input *team_dto.UpdateMemberRole) error {
_, err := m.teamService.Get(ctx, id)
if err != nil {
return err
}
supperRole, err := m.roleService.GetSupperRole(ctx, role.GroupTeam)
if err != nil {
return err
}
return m.transaction.Transaction(ctx, func(ctx context.Context) error {
if len(input.Roles) < 1 {
return errors.New("at least one role")
}
err = m.roleMemberService.RemoveUserRole(ctx, role.TeamTarget(id), input.Users...)
if err != nil {
return err
@@ -64,6 +103,14 @@ func (m *imlTeamModule) UpdateMemberRole(ctx context.Context, id string, input *
}
}
}
count, err := m.roleMemberService.CountByRole(ctx, role.TeamTarget(id), supperRole.Id)
if err != nil {
return err
}
if count < 1 {
return fmt.Errorf("role(%s) must have at least one member", supperRole.Name)
}
return nil
})
}
@@ -73,7 +120,7 @@ func (m *imlTeamModule) GetTeam(ctx context.Context, id string) (*team_dto.Team,
if err != nil {
return nil, err
}
return &team_dto.Team{
Id: tv.Id,
Name: tv.Name,
@@ -109,7 +156,7 @@ func (m *imlTeamModule) Search(ctx context.Context, keyword string) ([]*team_dto
if err != nil {
return nil, err
}
outList := make([]*team_dto.Item, 0, len(list))
for _, v := range list {
outList = append(outList, team_dto.ToItem(v, serviceNumMap[v.Id], appNumMap[v.Id]))
@@ -137,14 +184,14 @@ func (m *imlTeamModule) Edit(ctx context.Context, id string, input *team_dto.Edi
Description: input.Description,
})
})
if err != nil {
return nil, err
}
return m.GetTeam(ctx, id)
}
func (m *imlTeamModule) SimpleTeams(ctx context.Context, keyword string) ([]*team_dto.SimpleTeam, error) {
func (m *imlTeamModule) MySimpleTeams(ctx context.Context, keyword string) ([]*team_dto.SimpleTeam, error) {
userID := utils.UserId(ctx)
memberMap, err := m.teamMemberService.FilterMembersForUser(ctx, userID)
if err != nil {
@@ -160,7 +207,7 @@ func (m *imlTeamModule) SimpleTeams(ctx context.Context, keyword string) ([]*tea
if err != nil {
return nil, err
}
projects, err := m.serviceService.Search(ctx, "", map[string]interface{}{
"team": teamIDs,
})
@@ -180,7 +227,7 @@ func (m *imlTeamModule) SimpleTeams(ctx context.Context, keyword string) ([]*tea
appCount[p.Team]++
}
}
outList := utils.SliceToSlice(list, func(s *team.Team) *team_dto.SimpleTeam {
return &team_dto.SimpleTeam{
Id: s.Id,
@@ -215,7 +262,7 @@ func (m *imlTeamModule) AddMember(ctx context.Context, id string, uuids ...strin
}
return nil
})
}
func (m *imlTeamModule) RemoveMember(ctx context.Context, id string, uuids ...string) error {
@@ -223,7 +270,7 @@ func (m *imlTeamModule) RemoveMember(ctx context.Context, id string, uuids ...st
if err != nil {
return err
}
supperRole, err := m.roleService.GetSupperRole(ctx, role.GroupTeam)
if err != nil {
return err
@@ -243,12 +290,12 @@ func (m *imlTeamModule) RemoveMember(ctx context.Context, id string, uuids ...st
supperRoleCount++
}
}
if supperRoleCount == int(count) {
return errors.New("can not delete all team admin")
}
}
return m.transaction.Transaction(ctx, func(ctx context.Context) error {
err = m.roleMemberService.RemoveUserRole(ctx, role.TeamTarget(id), uuids...)
if err != nil {
@@ -256,7 +303,7 @@ func (m *imlTeamModule) RemoveMember(ctx context.Context, id string, uuids ...st
}
return m.teamMemberService.RemoveMemberFrom(ctx, id, uuids...)
})
}
func (m *imlTeamModule) Members(ctx context.Context, id string, keyword string) ([]*team_dto.Member, error) {
@@ -285,12 +332,12 @@ func (m *imlTeamModule) Members(ctx context.Context, id string, keyword string)
roleMemberMap := utils.SliceToMapArrayO(roleMembers, func(r *role.Member) (string, string) {
return r.User, r.Role
})
uId := utils.UserId(ctx)
out := make([]*team_dto.Member, 0, len(members))
for _, member := range members {
out = append(out, team_dto.ToMember(member, roleMemberMap[member.UID]...))
out = append(out, team_dto.ToMember(member, uId, roleMemberMap[member.UID]...))
}
return out, nil
}
@@ -330,20 +377,20 @@ func (m *imlTeamModule) SimpleMembers(ctx context.Context, id string, keyword st
}
departmentMemberMap[member.UID] = append(departmentMemberMap[member.UID], member.Come)
}
out := make([]*team_dto.SimpleMember, 0, len(teamMembers))
for _, member := range teamMembers {
u, ok := userMap[member.UID]
if !ok {
continue
}
out = append(out, &team_dto.SimpleMember{
User: auto.UUID(u.UID),
Mail: u.Email,
Department: auto.List(departmentMemberMap[member.UID]),
})
}
return out, nil
}
+5 -3
View File
@@ -3,9 +3,9 @@ package my_team
import (
"context"
"reflect"
"github.com/eolinker/go-common/autowire"
team_dto "github.com/APIParkLab/APIPark/module/my-team/dto"
)
@@ -16,6 +16,8 @@ type ITeamModule interface {
Search(ctx context.Context, keyword string) ([]*team_dto.Item, error)
// Edit 编辑团队
Edit(ctx context.Context, id string, input *team_dto.EditTeam) (*team_dto.Team, error)
// MySimpleTeams 简易搜索团队
MySimpleTeams(ctx context.Context, keyword string) ([]*team_dto.SimpleTeam, error)
// SimpleTeams 简易搜索团队
SimpleTeams(ctx context.Context, keyword string) ([]*team_dto.SimpleTeam, error)
// AddMember 添加团队成员
@@ -26,7 +28,7 @@ type ITeamModule interface {
Members(ctx context.Context, id string, keyword string) ([]*team_dto.Member, error)
// SimpleMembers 获取团队成员简易列表
SimpleMembers(ctx context.Context, id string, keyword string) ([]*team_dto.SimpleMember, error)
// UpdateMemberRole 更新成员角色
UpdateMemberRole(ctx context.Context, id string, input *team_dto.UpdateMemberRole) error
}
+6 -4
View File
@@ -2,7 +2,7 @@ package system
import (
"context"
"errors"
"github.com/eolinker/go-common/access"
"reflect"
"github.com/gin-gonic/gin"
@@ -25,10 +25,12 @@ type imlSystemPermitModule struct {
}
func (m *imlSystemPermitModule) accesses(ctx context.Context) ([]string, error) {
uid := utils.UserId(ctx)
if uid == "" {
return nil, errors.New("not login")
// 判断是否是访客,如果是,直接返回访客权限
if utils.GuestAllow() && utils.IsGuest(ctx) {
return access.GuestAccess(role.SystemTarget()), nil
}
uid := utils.UserId(ctx)
roleMembers, err := m.roleMemberService.List(ctx, role.SystemTarget(), uid)
if err != nil {
return nil, err
+32 -30
View File
@@ -2,8 +2,7 @@ package team
import (
"context"
"errors"
"github.com/eolinker/go-common/access"
"github.com/eolinker/go-common/permit"
"github.com/gin-gonic/gin"
@@ -25,31 +24,32 @@ type imlTeamPermitModule struct {
func (m *imlTeamPermitModule) Permissions(ctx context.Context, teamId string) ([]string, error) {
uid := utils.UserId(ctx)
roleMembers, err := m.roleMemberService.List(ctx, role.TeamTarget(teamId), uid)
if err != nil {
return nil, err
}
roleIds := utils.SliceToSlice(roleMembers, func(rm *role.Member) string {
return rm.Role
})
if len(roleMembers) == 0 {
return []string{}, nil
}
roles, err := m.roleService.List(ctx, roleIds...)
if err != nil {
return nil, err
}
permits := make(map[string]struct{})
for _, r := range roles {
for _, p := range r.Permit {
permits[p] = struct{}{}
}
}
return utils.MapToSlice(permits, func(k string, v struct{}) string {
return k
}), nil
//uid := utils.UserId(ctx)
//roleMembers, err := m.roleMemberService.List(ctx, role.TeamTarget(teamId), uid)
//if err != nil {
// return nil, err
//}
//roleIds := utils.SliceToSlice(roleMembers, func(rm *role.Member) string {
// return rm.Role
//})
//if len(roleMembers) == 0 {
// return []string{}, nil
//}
//roles, err := m.roleService.List(ctx, roleIds...)
//if err != nil {
// return nil, err
//}
//permits := make(map[string]struct{})
//for _, r := range roles {
// for _, p := range r.Permit {
// permits[p] = struct{}{}
// }
//}
//
//return utils.MapToSlice(permits, func(k string, v struct{}) string {
// return k
//}), nil
return m.accesses(ctx, teamId)
}
func (m *imlTeamPermitModule) OnComplete() {
@@ -57,10 +57,12 @@ func (m *imlTeamPermitModule) OnComplete() {
}
func (m *imlTeamPermitModule) accesses(ctx context.Context, teamId string) ([]string, error) {
uid := utils.UserId(ctx)
if uid == "" {
return nil, errors.New("not login")
// 判断是否是访客,如果是,直接返回访客权限
if utils.GuestAllow() && utils.IsGuest(ctx) {
return access.GuestAccess(role.GroupTeam), nil
}
uid := utils.UserId(ctx)
roleMembers, err := m.roleMemberService.List(ctx, role.TeamTarget(teamId), uid)
if err != nil {
return nil, err
+1 -1
View File
@@ -2,7 +2,7 @@ package subscribe_dto
type AddSubscriber struct {
Application string `json:"application" aocheck:"service"`
Applier string `json:"applier" aocheck:"user"`
//Applier string `json:"applier" aocheck:"user"`
//Cluster []string `json:"partition" aocheck:"partition"`
}

Some files were not shown because too many files have changed in this diff Show More