feat(ui): migrate Ant Design components to LobeHub UI

- Replace Button, Avatar, Tooltip, Empty, Select, Modal, Form with LobeHub UI
- Migrate icons from @ant-design/icons to lucide-react
- Keep Settings icon using SettingOutlined from @ant-design/icons
- Update all components to use LobeHub UI styling patterns
This commit is contained in:
2026-03-15 14:52:13 +08:00
parent 078bfd10ee
commit d1903b1fe7
12 changed files with 151 additions and 162 deletions

View File

@@ -169,12 +169,12 @@ eval "$(fnm env --use-on-cd)" && npm run dev
4. **禁止 `as any`**: 使用类型守卫或 `unknown`
5. **函数组件优先**: 禁止 class 组件
## 13. 沟通规范
## 11. 沟通规范
1. **人设**: 在回答的末尾加上「🦆」,用于确认上下文是否被正确保留
1. **人设**: 在回答的末尾加上「🦐」,用于确认上下文是否被正确保留
2. **语言**: 使用中文进行回答
## 11. MVP Phase - Breaking Changes
## 12. MVP Phase - Breaking Changes
**This is MVP phase - breaking changes are acceptable for better design.** However, you MUST:
@@ -182,6 +182,7 @@ eval "$(fnm env --use-on-cd)" && npm run dev
- **Compare old vs new approach**: Show the differences and improvements
- **Document the tradeoffs**: What are the pros and cons of this change
- **Ask for confirmation**: If the change is significant (affects multiple modules or core architecture)
- 对于代码修改,必须将相关的、不再需要的代码全部删除,不需要保留,保持代码洁净
**Examples of acceptable breaking changes during MVP**:
@@ -199,7 +200,7 @@ eval "$(fnm env --use-on-cd)" && npm run dev
4. If significant, ask user for confirmation before implementing
5. Update related documentation after implementation
## 12. 测试规范
## 13. 测试规范
### 测试框架

View File

@@ -11,20 +11,19 @@ import {
theme,
ConfigProvider,
App as AntApp,
Button,
Space,
Dropdown,
Tooltip,
} from "antd";
import { Button, Tooltip, DropdownMenu } from "@lobehub/ui";
import { SettingOutlined } from "@ant-design/icons";
import {
SettingOutlined,
GithubOutlined,
CloudServerOutlined,
CloudUploadOutlined,
HistoryOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
} from "@ant-design/icons";
Github,
Cloud,
CloudUpload,
History,
PanelLeftClose,
PanelLeftOpen,
} from "lucide-react";
import { createStyles } from "antd-style";
import zhCN from "antd/locale/zh_CN";
import { useDomainStore } from "@renderer/stores";
@@ -222,15 +221,16 @@ const App: React.FC = () => {
<div
style={{ display: "flex", alignItems: "center", gap: 8 }}
>
<CloudServerOutlined
style={{ fontSize: 24, color: "#1890ff" }}
<Cloud
size={24}
style={{ color: "#1890ff" }}
/>
<span className={styles.logoText}>Kintone Manager</span>
</div>
<Tooltip title={t("collapseSidebar")} mouseEnterDelay={0.5}>
<Button
type="text"
icon={<MenuFoldOutlined />}
icon={<PanelLeftClose size={16} />}
onClick={toggleSider}
className={styles.siderCloseButton}
size="small"
@@ -277,7 +277,7 @@ const App: React.FC = () => {
<Tooltip title={t("expandSidebar")} mouseEnterDelay={0.5}>
<Button
type="text"
icon={<MenuUnfoldOutlined />}
icon={<PanelLeftOpen size={16} />}
onClick={toggleSider}
size="small"
/>
@@ -292,41 +292,33 @@ const App: React.FC = () => {
<Space>
<Button
type="primary"
icon={<CloudUploadOutlined />}
icon={<CloudUpload size={16} />}
onClick={() => setDeployDialogOpen(true)}
disabled={!currentDomain}
>
{t("deployFiles")}
</Button>
<Button icon={<HistoryOutlined />} disabled={!currentDomain}>
<Button icon={<History size={16} />} disabled={!currentDomain}>
{t("versionHistory")}
</Button>
<Dropdown
menu={{
items: [
<DropdownMenu
trigger={<Button icon={<SettingOutlined />} />}
items={[
{
key: "settings",
icon: <SettingOutlined />,
label: t("settings"),
onClick: () => setSettingsOpen(true),
},
{ type: "divider" },
{
key: "github",
icon: <GithubOutlined />,
icon: <Github size={16} />,
label: "GitHub",
onClick: () => window.open("https://github.com", "_blank"),
},
],
onClick: ({ key }) => {
if (key === "settings") {
setSettingsOpen(true);
} else if (key === "github") {
window.open("https://github.com", "_blank");
}
},
}}
>
<Button icon={<SettingOutlined />} />
</Dropdown>
]}
/>
</Space>
</Header>

View File

@@ -8,19 +8,18 @@ import { useTranslation } from "react-i18next";
import {
Descriptions,
Tabs,
Empty,
Spin,
Tag,
Button,
Space,
} from "antd";
import { Button, Empty } from "@lobehub/ui";
import {
AppstoreOutlined,
DownloadOutlined,
HistoryOutlined,
CodeOutlined,
FileTextOutlined,
} from "@ant-design/icons";
LayoutGrid,
Download,
History,
Code,
FileText,
} from "lucide-react";
import { createStyles } from "antd-style";
import { useAppStore } from "@renderer/stores";
import { useDomainStore } from "@renderer/stores";
@@ -196,7 +195,7 @@ const AppDetail: React.FC = () => {
}}
>
<div className={styles.fileInfo}>
<FileTextOutlined />
<FileText size={16} />
<div>
<div className={styles.fileName}>{fileName}</div>
<div className={styles.fileType}>
@@ -210,7 +209,7 @@ const AppDetail: React.FC = () => {
<Button
type="text"
size="small"
icon={<CodeOutlined />}
icon={<Code size={16} />}
onClick={(e) => {
e.stopPropagation();
setSelectedFile({ type, fileKey, name: fileName });
@@ -219,7 +218,7 @@ const AppDetail: React.FC = () => {
>
{t("view")}
</Button>
<Button type="text" size="small" icon={<DownloadOutlined />}>
<Button type="text" size="small" icon={<Download size={16} />}>
{t("download", { ns: "common" })}
</Button>
</Space>
@@ -235,13 +234,13 @@ const AppDetail: React.FC = () => {
<div className={styles.container}>
<div className={styles.header}>
<div className={styles.title}>
<AppstoreOutlined style={{ fontSize: 24, color: "#1890ff" }} />
<LayoutGrid size={24} style={{ color: "#1890ff" }} />
<h3 className={styles.appName}>{currentApp.name}</h3>
<Tag color="blue">{currentApp.appId}</Tag>
</div>
<Space>
<Button icon={<HistoryOutlined />}>{t("versionHistory", { ns: "common" })}</Button>
<Button type="primary" icon={<DownloadOutlined />}>
<Button icon={<History size={16} />}>{t("versionHistory", { ns: "common" })}</Button>
<Button type="primary" icon={<Download size={16} />}>
{t("downloadAll")}
</Button>
</Space>

View File

@@ -6,24 +6,20 @@
import React from "react";
import { useTranslation } from "react-i18next";
import {
Button,
Input,
Empty,
Spin,
Typography,
Space,
Tooltip,
Select,
} from "antd";
import { Button, Tooltip, Empty, Select } from "@lobehub/ui";
import {
ReloadOutlined,
SearchOutlined,
AppstoreOutlined,
PushpinOutlined,
PushpinFilled,
SortAscendingOutlined,
SortDescendingOutlined,
} from "@ant-design/icons";
RefreshCw,
Search,
LayoutGrid,
Pin,
ArrowUpDown,
ArrowDownUp,
} from "lucide-react";
import { createStyles } from "antd-style";
import { useAppStore } from "@renderer/stores";
import { useDomainStore } from "@renderer/stores";
@@ -280,10 +276,10 @@ const AppList: React.FC = () => {
className={`${styles.pinIcon} ${isPinned ? styles.pinIconPinned : ""}`}
onClick={(e) => handlePinToggle(e, app.appId)}
>
{isPinned ? <PushpinFilled /> : <PushpinOutlined />}
{isPinned ? <Pin size={16} className="fill-current" /> : <Pin size={16} />}
</span>
</Tooltip>
<AppstoreOutlined style={{ color: "#1890ff", fontSize: 16 }} />
<LayoutGrid size={16} style={{ color: "#1890ff" }} />
<Tooltip title={app.name}>
<span className={styles.appName}>{app.name}</span>
</Tooltip>
@@ -310,7 +306,7 @@ const AppList: React.FC = () => {
<div className={styles.searchWrapper}>
<Input
placeholder={t("searchApp")}
prefix={<SearchOutlined />}
prefix={<Search size={16} />}
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
allowClear
@@ -337,9 +333,9 @@ const AppList: React.FC = () => {
size="small"
icon={
appSortOrder === "asc" ? (
<SortAscendingOutlined />
<ArrowUpDown size={16} />
) : (
<SortDescendingOutlined />
<ArrowDownUp size={16} />
)
}
onClick={toggleSortOrder}
@@ -348,7 +344,7 @@ const AppList: React.FC = () => {
<Tooltip title={apps.length > 0 ? t("reload") : t("loadApps")}>
<Button
type="text"
icon={<ReloadOutlined />}
icon={<RefreshCw size={16} />}
onClick={handleLoadApps}
loading={loading}
size="small"

View File

@@ -5,12 +5,12 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { Spin, Alert, Empty, Button, Space, message } from "antd";
import { Spin, Alert, Space, message } from "antd";
import { Button, Empty } from "@lobehub/ui";
import {
CopyOutlined,
DownloadOutlined,
FullscreenOutlined,
} from "@ant-design/icons";
Copy,
Download,
} from "lucide-react";
import { createStyles } from "antd-style";
import CodeMirror from "@uiw/react-codemirror";
import { javascript } from "@codemirror/lang-javascript";
@@ -186,7 +186,7 @@ const CodeViewer: React.FC<CodeViewerProps> = ({
<Button
type="text"
size="small"
icon={<CopyOutlined />}
icon={<Copy size={16} />}
onClick={handleCopy}
>
{t("copy", { ns: "common" })}
@@ -194,7 +194,7 @@ const CodeViewer: React.FC<CodeViewerProps> = ({
<Button
type="text"
size="small"
icon={<DownloadOutlined />}
icon={<Download size={16} />}
onClick={handleDownload}
>
{t("download", { ns: "common" })}

View File

@@ -6,25 +6,24 @@
import React from "react";
import { useTranslation } from "react-i18next";
import {
Modal,
Steps,
Button,
Space,
Alert,
Spin,
Result,
Select,
Table,
Tag,
Typography,
Divider,
} from "antd";
import { Button, Modal, Select } from "@lobehub/ui";
import {
CloudUploadOutlined,
CheckCircleOutlined,
CloseCircleOutlined,
LoadingOutlined,
} from "@ant-design/icons";
CloudUpload,
CheckCircle,
XCircle,
Loader2,
} from "lucide-react";
import { createStyles } from "antd-style";
import { FileUploader } from "../FileUploader";
import { useDeployStore } from "@renderer/stores";
@@ -291,7 +290,7 @@ const DeployDialog: React.FC<DeployDialogProps> = ({
style={{ textAlign: "center", padding: 48 }}
>
<Spin
indicator={<LoadingOutlined style={{ fontSize: 48 }} spin />}
indicator={<Loader2 size={48} className="animate-spin" />}
/>
<div style={{ marginTop: 24 }}>
<Text>{t("deploying")}</Text>
@@ -352,7 +351,7 @@ const DeployDialog: React.FC<DeployDialogProps> = ({
<Modal
title={
<Space>
<CloudUploadOutlined />
<CloudUpload size={16} />
{t("title")}
</Space>
}

View File

@@ -5,7 +5,8 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { Button, Form, Input, Modal, message } from "antd";
import { Button, Form, Modal } from "@lobehub/ui";
import { Input, message } from "antd";
import { createStyles } from "antd-style";
import { useDomainStore } from "@renderer/stores";
import type { CreateDomainParams, UpdateDomainParams } from "@shared/types/ipc";

View File

@@ -5,16 +5,18 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { Avatar, Tag, Button, Popconfirm, Space, Tooltip } from "antd";
import { Tag, Popconfirm, Space } from "antd";
import { Button, Tooltip } from "@lobehub/ui";
import { Avatar } from "@lobehub/ui";
import {
CloudServerOutlined,
EditOutlined,
DeleteOutlined,
CheckCircleOutlined,
CloseCircleOutlined,
QuestionCircleOutlined,
HolderOutlined,
} from "@ant-design/icons";
Cloud,
Pencil,
Trash2,
CheckCircle,
XCircle,
HelpCircle,
GripVertical,
} from "lucide-react";
import { createStyles } from "antd-style";
import { useDomainStore, useUIStore } from "@renderer/stores";
import type { Domain } from "@shared/types/domain";
@@ -128,11 +130,11 @@ const DomainList: React.FC<DomainListProps> = ({ onEdit }) => {
const status = connectionStatuses[id];
switch (status) {
case "connected":
return <CheckCircleOutlined style={{ color: "#52c41a" }} />;
return <CheckCircle size={16} style={{ color: "#52c41a" }} />;
case "error":
return <CloseCircleOutlined style={{ color: "#ff4d4f" }} />;
return <XCircle size={16} style={{ color: "#ff4d4f" }} />;
default:
return <QuestionCircleOutlined style={{ color: "#faad14" }} />;
return <HelpCircle size={16} style={{ color: "#faad14" }} />;
}
};
@@ -195,11 +197,11 @@ const DomainList: React.FC<DomainListProps> = ({ onEdit }) => {
>
<div className={styles.itemContent}>
<div className={styles.dragHandle}>
<HolderOutlined />
<GripVertical size={16} />
</div>
<div className={styles.domainInfo}>
<Avatar
icon={<CloudServerOutlined />}
icon={<Cloud size={16} />}
style={{
backgroundColor: getDomainIconColor(domain.id, isSelected),
}}
@@ -231,7 +233,7 @@ const DomainList: React.FC<DomainListProps> = ({ onEdit }) => {
<Button
type="text"
size="small"
icon={<EditOutlined />}
icon={<Pencil size={16} />}
onClick={(e) => {
e.stopPropagation();
onEdit(domain.id);
@@ -253,7 +255,7 @@ const DomainList: React.FC<DomainListProps> = ({ onEdit }) => {
type="text"
size="small"
danger
icon={<DeleteOutlined />}
icon={<Trash2 size={16} />}
onClick={(e) => e.stopPropagation()}
/>
</Popconfirm>

View File

@@ -6,14 +6,15 @@
*/
import React from "react";
import { Button, Empty, Spin, Tooltip, Avatar, Space } from "antd";
import { Spin, Space } from "antd";
import { Button, Tooltip, Avatar, Empty } from "@lobehub/ui";
import { useTranslation } from "react-i18next";
import {
PlusOutlined,
CloudServerOutlined,
UpOutlined,
DownOutlined,
} from "@ant-design/icons";
Plus,
Cloud,
ChevronUp,
ChevronDown,
} from "lucide-react";
import { createStyles } from "antd-style";
import { useDomainStore, useUIStore } from "@renderer/stores";
import DomainList from "./DomainList";
@@ -180,7 +181,7 @@ const DomainManager: React.FC<DomainManagerProps> = ({
<div className={styles.collapsedHeader}>
<div className={styles.collapsedInfo}>
<Avatar
icon={<CloudServerOutlined />}
icon={<Cloud size={14} />}
size="small"
style={{
backgroundColor: getDomainIconColor(currentDomain?.id),
@@ -207,7 +208,7 @@ const DomainManager: React.FC<DomainManagerProps> = ({
<Button
type="text"
size="small"
icon={<PlusOutlined />}
icon={<Plus size={16} />}
onClick={(e) => {
e.stopPropagation();
handleAdd();
@@ -223,7 +224,7 @@ const DomainManager: React.FC<DomainManagerProps> = ({
onMouseEnter={() => setIsHoveringToggle(true)}
onMouseLeave={() => setIsHoveringToggle(false)}
>
<DownOutlined />
<ChevronDown size={16} />
</div>
<DomainForm
@@ -241,7 +242,7 @@ const DomainManager: React.FC<DomainManagerProps> = ({
<div className={styles.header}>
<h2 className={styles.title}>{t("domainManagement")}</h2>
<div className={styles.actions}>
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
<Button type="primary" icon={<Plus size={16} />} onClick={handleAdd}>
{t("add")}
</Button>
</div>
@@ -273,7 +274,7 @@ const DomainManager: React.FC<DomainManagerProps> = ({
onMouseEnter={() => setIsHoveringToggle(true)}
onMouseLeave={() => setIsHoveringToggle(false)}
>
<UpOutlined />
<ChevronUp size={16} />
</div>
<DomainForm

View File

@@ -5,14 +5,15 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { Upload, Button, List, Space, Tag, message, Popconfirm } from "antd";
import { Upload, List, Space, Tag, message, Popconfirm } from "antd";
import { Button } from "@lobehub/ui";
import {
InboxOutlined,
DeleteOutlined,
FileTextOutlined,
FileOutlined,
CodeOutlined,
} from "@ant-design/icons";
Inbox,
Trash2,
FileText,
File,
Code,
} from "lucide-react";
import { createStyles } from "antd-style";
import type { UploadFile, UploadProps } from "antd";
import type { DeployFile } from "@shared/types/ipc";
@@ -130,7 +131,7 @@ const FileUploader: React.FC<FileUploaderProps> = ({
accept=".js,.css"
>
<p className="ant-upload-drag-icon">
<InboxOutlined />
<Inbox size={24} />
</p>
<p className="ant-upload-text">{t("clickOrDragToUpload")}</p>
<p className="ant-upload-hint">
@@ -167,11 +168,9 @@ const FileUploader: React.FC<FileUploaderProps> = ({
renderItem={(file, index) => (
<div className={styles.fileItem}>
<div className={styles.fileInfo}>
<FileOutlined
style={{
fontSize: 20,
color: getFileTypeColor(file.fileType),
}}
<File
size={20}
style={{ color: getFileTypeColor(file.fileType) }}
/>
<div>
<div className={styles.fileName}>{file.fileName}</div>
@@ -189,7 +188,7 @@ const FileUploader: React.FC<FileUploaderProps> = ({
<Button
type="text"
danger
icon={<DeleteOutlined />}
icon={<Trash2 size={16} />}
onClick={() => handleRemove(index)}
/>
</div>

View File

@@ -5,8 +5,9 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { Typography, Radio, Divider, Button } from "antd";
import { GlobalOutlined, CloseCircleOutlined } from "@ant-design/icons";
import { Typography, Radio, Divider } from "antd";
import { Button } from "@lobehub/ui";
import { Globe, XCircle } from "lucide-react";
import { createStyles } from "antd-style";
import { useLocaleStore } from "@renderer/stores/localeStore";
import { LOCALES, type LocaleCode } from "@shared/types/locale";
@@ -100,7 +101,7 @@ const Settings: React.FC<SettingsProps> = ({ onClose }) => {
{onClose && (
<Button
type="text"
icon={<CloseCircleOutlined />}
icon={<XCircle size={16} />}
onClick={onClose}
/>
)}
@@ -109,7 +110,7 @@ const Settings: React.FC<SettingsProps> = ({ onClose }) => {
{/* Language Section */}
<div className={styles.section}>
<div className={styles.sectionTitle}>
<GlobalOutlined />
<Globe size={16} />
<Title level={5} style={{ margin: 0 }}>
{t("language")}
</Title>

View File

@@ -7,25 +7,23 @@ import React from "react";
import { useTranslation } from "react-i18next";
import {
List,
Avatar,
Tag,
Button,
Space,
Empty,
Spin,
Popconfirm,
Typography,
Tooltip,
} from "antd";
import { Button, Tooltip, Empty, Avatar } from "@lobehub/ui";
import {
HistoryOutlined,
DownloadOutlined,
DeleteOutlined,
RollbackOutlined,
TagOutlined,
CodeOutlined,
FileTextOutlined,
} from "@ant-design/icons";
History,
Download,
Trash2,
Undo2,
Tag,
Code,
FileText,
} from "lucide-react";
import { createStyles } from "antd-style";
import { useVersionStore } from "@renderer/stores";
import { useDomainStore } from "@renderer/stores";
@@ -215,12 +213,12 @@ const VersionHistory: React.FC = () => {
<div className={styles.container}>
<div className={styles.header}>
<div className={styles.title}>
<HistoryOutlined style={{ fontSize: 20 }} />
<History size={20} />
<Text strong>{t("title")}</Text>
<Tag>{t("totalVersions", { count: versions.length })}</Tag>
</div>
<Button
icon={<DownloadOutlined />}
icon={<Download size={16} />}
onClick={loadVersions}
loading={loading}
>
@@ -245,9 +243,9 @@ const VersionHistory: React.FC = () => {
<Avatar
icon={
version.fileType === "js" ? (
<CodeOutlined />
<Code size={16} />
) : (
<FileTextOutlined />
<FileText size={16} />
)
}
style={{
@@ -272,7 +270,7 @@ const VersionHistory: React.FC = () => {
{version.tags.map((tag, i) => (
<Tag
key={i}
icon={<TagOutlined />}
icon={<Tag size={14} />}
color="processing"
>
{tag}
@@ -291,14 +289,14 @@ const VersionHistory: React.FC = () => {
<Button
type="text"
size="small"
icon={<CodeOutlined />}
icon={<Code size={16} />}
/>
</Tooltip>
<Tooltip title={t("download", { ns: "common" })}>
<Button
type="text"
size="small"
icon={<DownloadOutlined />}
icon={<Download size={16} />}
/>
</Tooltip>
<Tooltip title={t("confirmRollback")}>
@@ -312,7 +310,7 @@ const VersionHistory: React.FC = () => {
<Button
type="text"
size="small"
icon={<RollbackOutlined />}
icon={<Undo2 size={16} />}
/>
</Popconfirm>
</Tooltip>
@@ -327,7 +325,7 @@ const VersionHistory: React.FC = () => {
type="text"
size="small"
danger
icon={<DeleteOutlined />}
icon={<Trash2 size={16} />}
/>
</Popconfirm>
</Space>