跳到正文
灵动桌面框架
Esc
本栏目菜单

Ant Design 组件

灵动桌面框架集成了 Ant Design 6(antd ^6.3.1),配合 @ant-design/cssinjs ^2.1.2 和 @ant-design/icons ^6.1.0,提供企业级 UI 组件。

全局配置

// src/App.tsx(节选)
import { ConfigProvider } from "antd";
import { StyleProvider } from "@ant-design/cssinjs";
import zhCN from "antd/locale/zh_CN";
import { getAntdTheme } from "@/theme/antdTheme";

<StyleProvider layer>
  <ConfigProvider locale={zhCN} theme={getAntdTheme(resolved)}>
    <ErrorBoundary>
      <AppRouter />
    </ErrorBoundary>
    <GlobalNativeTooltip />
  </ConfigProvider>
</StyleProvider>
  • locale={zhCN}:组件内置文案(分页、空状态、确认按钮等)使用简体中文
  • theme={getAntdTheme(resolved)}:根据当前生效的主题返回 lightTheme 或 darkTheme,两套配置定义在 src/theme/antdTheme.ts,详见 主题切换
  • <StyleProvider layer>:把 antd 运行时注入的样式放进 CSS 层 @layer antd,让 Tailwind 工具类能覆盖 antd 组件样式,详见 TailwindCSS 样式

两套主题共享的 token(src/theme/antdTheme.ts):

const sharedToken: ThemeConfig["token"] = {
  borderRadius: 6,
  borderRadiusSM: 3,
  borderRadiusLG: 8,
  fontSize: 13,
  controlHeight: 32,
  controlHeightSM: 24,
  controlHeightLG: 40,
};

组件级覆盖(sharedComponents)统一了 Button / Input / Select / Table 的圆角为 6、Modal 的 borderRadiusLG 为 12;Tooltip 的背景色和文字色按亮 / 暗主题分别配置。

常用布局

主布局(Sider + Header + Content)

// src/components/layout/AppLayout.tsx(节选)
import { Outlet, useNavigate } from "react-router-dom";
import { Layout, Button, theme as antdTheme } from "antd";
import { MenuFoldOutlined, MenuUnfoldOutlined, SettingOutlined } from "@ant-design/icons";
import { useAppStore } from "@/store";
import { Sidebar } from "./Sidebar";
import { WindowControls } from "./WindowControls";
import { ThemeToggle } from "@/components/ui/ThemeToggle";

const { Header, Sider, Content } = Layout;

export function AppLayout({ headerExtra }: AppLayoutProps) {
  const { sidebarCollapsed, toggleSidebar } = useAppStore();
  const { token } = antdTheme.useToken();
  const navigate = useNavigate();

  return (
    <Layout style={{ height: "100vh" }}>
      <Sider
        collapsed={sidebarCollapsed}
        collapsedWidth={60}
        width={220}
        style={{ background: token.colorBgContainer, borderRight: `1px solid var(--border)` }}
      >
        <Sidebar />
      </Sider>
      <Layout>
        <Header style={{ padding: 0, height: "var(--header-height)", display: "flex", alignItems: "center" }}>
          <Button
            type="text"
            icon={sidebarCollapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
            onClick={toggleSidebar}
          />
          <DragRegion />
          <div style={{ display: "flex", alignItems: "center" }}>
            {headerExtra}
            <ThemeToggle />
            <Button type="text" icon={<SettingOutlined />} onClick={() => navigate("/settings")} title="设置" />
            <WindowControls />
          </div>
        </Header>
        <Content style={{ padding: 24, overflow: "auto", background: token.colorBgLayout }}>
          <Outlet />
        </Content>
      </Layout>
    </Layout>
  );
}

要点:

  • 侧边栏折叠状态由 Zustand 的 sidebarCollapsed 控制(不是 Sider 自带的 collapsible 触发器),折叠按钮在顶栏左侧
  • 窗口是无边框的(tauri.conf.json 中 "decorations": false),顶栏中间的 DragRegion 按下时调用 startDragging() 拖动窗口、双击调用 toggleMaximize();右侧 WindowControls 自绘最小化 / 最大化 / 关闭按钮
  • headerExtra 属性可以在主题切换按钮之前插入额外操作
  • 颜色取自 antdTheme.useToken() 的 token 或 var(--xxx) CSS 变量,不写死色值

表单

import { Form, Input, Button, message } from "antd";
import { configApi } from "@/lib/api";

function SettingsForm() {
  const [form] = Form.useForm();

  const onFinish = async (values: { key: string; value: string }) => {
    try {
      await configApi.set(values.key, values.value);
      message.success("保存成功");
    } catch (e) {
      message.error(String(e));
    }
  };

  return (
    <Form form={form} layout="vertical" onFinish={onFinish}>
      <Form.Item name="key" label="配置项" rules={[{ required: true }]}>
        <Input placeholder="输入配置键名" />
      </Form.Item>
      <Form.Item name="value" label="值" rules={[{ required: true }]}>
        <Input.TextArea rows={3} />
      </Form.Item>
      <Button type="primary" htmlType="submit">保存</Button>
    </Form>
  );
}

configApi 定义在 src/lib/api/config.ts,提供 getAll()、get(key)、set(key, value)、delete(key) 四个方法,分别对应 Rust Command get_all_config / get_config / set_config / delete_config。

表格

// src/pages/settings/index.tsx(节选)
import { Table, Tag } from "antd";
import type { AppConfig } from "@/types";

const columns = [
  {
    title: "配置键",
    dataIndex: "key",
    key: "key",
    render: (text: string) => <Tag color="blue">{text}</Tag>,
  },
  { title: "配置值", dataIndex: "value", key: "value" },
];

<Table
  columns={columns}
  dataSource={configs}
  rowKey="key"
  loading={loading}
  pagination={false}
  size="small"
/>

AppConfig 只有 key 和 value 两个字段(src/types/config.ts),因此用 key 作为 rowKey。

原生 title 提示统一

App.tsx 中挂载了 GlobalNativeTooltip(src/components/GlobalNativeTooltip.tsx):它在捕获阶段监听 mouseover,把任意 HTML 元素上的原生 title 属性「借走」存到 data-native-title,再用受控的 antd Tooltip 在该元素位置显示气泡,同时补上 aria-label。

因此写普通按钮时直接加 title="设置" 即可,悬停效果与 antd Tooltip 一致并跟随主题;antd 组件自己的 Tooltip / Modal 不受影响。

图标

框架同时使用 Lucide React(lucide-react ^0.577.0)和 Ant Design Icons:

import { Home, Info, Sun, Moon, Monitor } from "lucide-react";
import { MenuFoldOutlined, SettingOutlined, SyncOutlined } from "@ant-design/icons";

<Home size={18} />
<Moon size={16} />
<SettingOutlined />
<SyncOutlined spin={checking} />

现有代码中,侧边栏菜单、主题切换、窗口控制按钮用 Lucide;顶栏折叠 / 设置按钮、检查更新按钮用 Ant Design Icons。