前端重构
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
.pub-plug-container {
|
||||
padding: 16px;
|
||||
|
||||
.pub-plug-alert {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.plugin-name-input {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.transfer-card {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.transfer-warning {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.parent-menu-input {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.transfer-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 16px 0;
|
||||
|
||||
:global {
|
||||
.ant-transfer {
|
||||
.ant-transfer-list {
|
||||
width: 400px;
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
.ant-transfer-list-header {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.ant-transfer-list-body {
|
||||
.ant-transfer-list-content-item {
|
||||
padding: 8px 12px;
|
||||
|
||||
.transfer-item-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
|
||||
.item-title {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.item-path {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.transfer-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.pub-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
// 暗色主题适配
|
||||
.ant-layout-content[data-theme='dark'] {
|
||||
.pub-plug-container {
|
||||
.transfer-card {
|
||||
.transfer-wrapper {
|
||||
:global {
|
||||
.ant-transfer {
|
||||
.ant-transfer-list-header {
|
||||
background: #1f1f1f;
|
||||
}
|
||||
|
||||
.ant-transfer-list-body {
|
||||
.ant-transfer-list-content-item {
|
||||
.transfer-item-content {
|
||||
.item-title {
|
||||
color: #d9d9d9;
|
||||
}
|
||||
|
||||
.item-path {
|
||||
color: #8c8c8c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card, Input, Transfer, Button, Alert, message, Modal, Space } from 'antd';
|
||||
import type { TransferProps } from 'antd';
|
||||
import { pubPlug, initMenu, initAPI } from '@/services/kratos/autoCode';
|
||||
import { getAllApis } from '@/services/kratos/api';
|
||||
import { getMenuList } from '@/services/kratos/menu';
|
||||
import './index.less';
|
||||
|
||||
interface MenuItem {
|
||||
ID: number;
|
||||
meta: {
|
||||
title: string;
|
||||
};
|
||||
component: string;
|
||||
children?: MenuItem[];
|
||||
}
|
||||
|
||||
interface ApiItem {
|
||||
ID: number;
|
||||
description: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件打包页面
|
||||
*/
|
||||
const PubPlug: React.FC = () => {
|
||||
const [plugName, setPlugName] = useState('');
|
||||
const [parentMenu, setParentMenu] = useState('');
|
||||
|
||||
// 菜单穿梭框
|
||||
const [menuTargetKeys, setMenuTargetKeys] = useState<string[]>([]);
|
||||
const [menuDataSource, setMenuDataSource] = useState<any[]>([]);
|
||||
|
||||
// API穿梭框
|
||||
const [apiTargetKeys, setApiTargetKeys] = useState<string[]>([]);
|
||||
const [apiDataSource, setApiDataSource] = useState<any[]>([]);
|
||||
|
||||
// 递归展开菜单到一级
|
||||
const flattenMenus = (menus: MenuItem[]): MenuItem[] => {
|
||||
const result: MenuItem[] = [];
|
||||
menus.forEach((item) => {
|
||||
if (item.children && item.children.length > 0) {
|
||||
result.push(...flattenMenus(item.children));
|
||||
} else {
|
||||
result.push(item);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// 初始化数据
|
||||
const initData = async () => {
|
||||
try {
|
||||
// 获取菜单列表
|
||||
const menuRes = await getMenuList();
|
||||
if (menuRes.code === 0 && menuRes.data) {
|
||||
const flatMenus = flattenMenus(menuRes.data);
|
||||
setMenuDataSource(flatMenus.map((item) => ({
|
||||
key: String(item.ID),
|
||||
ID: item.ID,
|
||||
title: item.meta?.title || '',
|
||||
component: item.component || '',
|
||||
})));
|
||||
}
|
||||
|
||||
// 获取API列表
|
||||
const apiRes = await getAllApis();
|
||||
if (apiRes.code === 0 && apiRes.data?.apis) {
|
||||
setApiDataSource(apiRes.data.apis.map((item: ApiItem) => ({
|
||||
key: String(item.ID),
|
||||
ID: item.ID,
|
||||
description: item.description || '',
|
||||
path: item.path || '',
|
||||
})));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('初始化数据失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
initData();
|
||||
}, []);
|
||||
|
||||
// 菜单穿梭框筛选
|
||||
const filterMenuOption: TransferProps['filterOption'] = (inputValue, option) => {
|
||||
return (
|
||||
option.title?.toLowerCase().includes(inputValue.toLowerCase()) ||
|
||||
option.component?.toLowerCase().includes(inputValue.toLowerCase())
|
||||
);
|
||||
};
|
||||
|
||||
// API穿梭框筛选
|
||||
const filterApiOption: TransferProps['filterOption'] = (inputValue, option) => {
|
||||
return (
|
||||
option.description?.toLowerCase().includes(inputValue.toLowerCase()) ||
|
||||
option.path?.toLowerCase().includes(inputValue.toLowerCase())
|
||||
);
|
||||
};
|
||||
|
||||
// 定义安装菜单
|
||||
const handleInitMenu = () => {
|
||||
if (!parentMenu) {
|
||||
message.error('请填写菜单组名');
|
||||
return;
|
||||
}
|
||||
if (menuTargetKeys.length === 0) {
|
||||
message.error('请至少选择一个菜单');
|
||||
return;
|
||||
}
|
||||
if (!plugName) {
|
||||
message.error('请填写插件名');
|
||||
return;
|
||||
}
|
||||
|
||||
Modal.confirm({
|
||||
title: '生成初始菜单',
|
||||
content: `点击后将会覆盖 server 下的 /plugin/${plugName}/initialize/menu. 是否继续?`,
|
||||
okText: '生成',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
try {
|
||||
const res = await initMenu({
|
||||
plugName,
|
||||
parentMenu,
|
||||
menus: menuTargetKeys.map(Number),
|
||||
});
|
||||
if (res.code === 0) {
|
||||
message.success('菜单初始化成功');
|
||||
} else {
|
||||
message.error(res.msg || '菜单初始化失败');
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('菜单初始化失败');
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 定义安装API
|
||||
const handleInitAPI = () => {
|
||||
if (apiTargetKeys.length === 0) {
|
||||
message.error('请至少选择一个API');
|
||||
return;
|
||||
}
|
||||
if (!plugName) {
|
||||
message.error('请填写插件名');
|
||||
return;
|
||||
}
|
||||
|
||||
Modal.confirm({
|
||||
title: '生成初始API',
|
||||
content: `点击后将会覆盖 server 下的 /plugin/${plugName}/initialize/api. 是否继续?`,
|
||||
okText: '生成',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
try {
|
||||
const res = await initAPI({
|
||||
plugName,
|
||||
apis: apiTargetKeys.map(Number),
|
||||
});
|
||||
if (res.code === 0) {
|
||||
message.success('API初始化成功');
|
||||
} else {
|
||||
message.error(res.msg || 'API初始化失败');
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('API初始化失败');
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 打包插件
|
||||
const handlePubPlugin = () => {
|
||||
if (!plugName) {
|
||||
message.error('请填写插件名');
|
||||
return;
|
||||
}
|
||||
|
||||
Modal.confirm({
|
||||
title: '打包',
|
||||
content: `请检查 server 下的 /plugin/${plugName}/plugin.go 是否已放开需要的 initialize.Api(ctx) 和 initialize.Menu(ctx)?`,
|
||||
okText: '打包',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
try {
|
||||
const res = await pubPlug({ plugName });
|
||||
if (res.code === 0) {
|
||||
message.success(res.msg || '打包成功');
|
||||
} else {
|
||||
message.error(res.msg || '打包失败');
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('打包失败');
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="pub-plug-container">
|
||||
<Alert
|
||||
message="目前只支持标准插件(通过插件模板生成的标准目录插件),非标准插件请自行打包"
|
||||
type="warning"
|
||||
showIcon
|
||||
className="pub-plug-alert"
|
||||
/>
|
||||
|
||||
<div className="plugin-name-input">
|
||||
<Input
|
||||
value={plugName}
|
||||
onChange={(e) => setPlugName(e.target.value)}
|
||||
placeholder="插件模板处填写的【插件名】"
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 菜单选择 */}
|
||||
<Card title="菜单选择" className="transfer-card">
|
||||
<Alert
|
||||
message="穿梭框请只选择子级菜单即可"
|
||||
type="info"
|
||||
showIcon
|
||||
className="transfer-warning"
|
||||
/>
|
||||
|
||||
<Input
|
||||
value={parentMenu}
|
||||
onChange={(e) => setParentMenu(e.target.value)}
|
||||
placeholder="请输入菜单组名,例:公告管理"
|
||||
className="parent-menu-input"
|
||||
/>
|
||||
|
||||
<div className="transfer-wrapper">
|
||||
<Transfer
|
||||
dataSource={menuDataSource}
|
||||
targetKeys={menuTargetKeys}
|
||||
onChange={(newTargetKeys) => setMenuTargetKeys(newTargetKeys as string[])}
|
||||
filterOption={filterMenuOption}
|
||||
showSearch
|
||||
titles={['可选菜单', '使用菜单']}
|
||||
operations={['选中', '移除']}
|
||||
render={(item) => (
|
||||
<div className="transfer-item-content">
|
||||
<span className="item-title">{item.title}</span>
|
||||
<span className="item-path">{item.component}</span>
|
||||
</div>
|
||||
)}
|
||||
listStyle={{
|
||||
width: 400,
|
||||
height: 350,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="transfer-actions">
|
||||
<Button type="primary" onClick={handleInitMenu}>
|
||||
定义安装菜单
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* API选择 */}
|
||||
<Card title="API选择" className="transfer-card">
|
||||
<div className="transfer-wrapper">
|
||||
<Transfer
|
||||
dataSource={apiDataSource}
|
||||
targetKeys={apiTargetKeys}
|
||||
onChange={(newTargetKeys) => setApiTargetKeys(newTargetKeys as string[])}
|
||||
filterOption={filterApiOption}
|
||||
showSearch
|
||||
titles={['可选API', '使用API']}
|
||||
operations={['选中', '移除']}
|
||||
render={(item) => (
|
||||
<div className="transfer-item-content">
|
||||
<span className="item-title">{item.description}</span>
|
||||
<span className="item-path">{item.path}</span>
|
||||
</div>
|
||||
)}
|
||||
listStyle={{
|
||||
width: 400,
|
||||
height: 350,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="transfer-actions">
|
||||
<Button type="primary" onClick={handleInitAPI}>
|
||||
定义安装API
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<div className="pub-actions">
|
||||
<Button type="primary" size="large" onClick={handlePubPlugin}>
|
||||
打包插件
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PubPlug;
|
||||
Reference in New Issue
Block a user