Compare commits
39 Commits
d34edb273b
...
54c1e03830
| Author | SHA1 | Date |
|---|---|---|
|
|
54c1e03830 | |
|
|
6b3deec414 | |
|
|
f236c22d7f | |
|
|
281fa1f4e5 | |
|
|
993722e4a0 | |
|
|
7cca3a439f | |
|
|
99cc370025 | |
|
|
7a07fae8f9 | |
|
|
6633a09861 | |
|
|
8a0329fb4b | |
|
|
f6f66e1627 | |
|
|
18fe750dab | |
|
|
d5c75ad04a | |
|
|
d92dd377cc | |
|
|
3466208112 | |
|
|
b958e1f607 | |
|
|
253cbdb5fb | |
|
|
1b3b23a63e | |
|
|
a3e2a95fc6 | |
|
|
bb35f25900 | |
|
|
0e764c997b | |
|
|
87e199ab4b | |
|
|
b128535cfc | |
|
|
1edda375a8 | |
|
|
3f4004c349 | |
|
|
ec0523cbea | |
|
|
582f03bff3 | |
|
|
ab40ad4c27 | |
|
|
0e2113e99c | |
|
|
0b12e0b88e | |
|
|
dbacc884be | |
|
|
51d24b5dec | |
|
|
e08751bdb2 | |
|
|
e49f8b1463 | |
|
|
7dcbffe211 | |
|
|
bbe760abf6 | |
|
|
ffe6ce25df | |
|
|
19a226a5de | |
|
|
15f0d9aba5 |
|
|
@ -0,0 +1,607 @@
|
|||
# Nex Design 设计规范指南
|
||||
|
||||
> 面向 React + Ant Design + Tailwind CSS 的前端设计语言规范
|
||||
|
||||
## 目录
|
||||
|
||||
- [概述](#概述)
|
||||
- [设计原则](#设计原则)
|
||||
- [颜色系统](#颜色系统)
|
||||
- [排版规范](#排版规范)
|
||||
- [间距系统](#间距系统)
|
||||
- [组件规范](#组件规范)
|
||||
- [布局规范](#布局规范)
|
||||
- [交互规范](#交互规范)
|
||||
- [响应式设计](#响应式设计)
|
||||
- [页面模板](#页面模板)
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
Nex Design 是为开发团队打造的一套标准化设计语言系统,旨在提供统一、高效、易维护的前端设计规范。
|
||||
|
||||
### 技术栈
|
||||
|
||||
- **框架**: React 18+
|
||||
- **组件库**: Ant Design 5.x
|
||||
- **样式方案**: Tailwind CSS 3.x
|
||||
- **包管理**: Yarn
|
||||
- **运行时**: Node.js 16+
|
||||
|
||||
### 设计目标
|
||||
|
||||
1. **一致性**: 确保所有页面和组件保持视觉与交互的一致性
|
||||
2. **高效性**: 提供可复用的设计模式,加快开发速度
|
||||
3. **可维护性**: 建立清晰的规范体系,降低维护成本
|
||||
4. **用户体验**: 优先考虑用户体验,打造直观易用的界面
|
||||
|
||||
---
|
||||
|
||||
## 设计原则
|
||||
|
||||
### 1. 清晰明确
|
||||
界面设计应当清晰直观,避免歧义,让用户能够快速理解和操作。
|
||||
|
||||
### 2. 一致性优先
|
||||
保持视觉、交互、用词的一致性,减少用户学习成本。
|
||||
|
||||
### 3. 效率至上
|
||||
优化操作流程,减少用户的操作步骤,提高工作效率。
|
||||
|
||||
### 4. 反馈及时
|
||||
对用户的每一个操作都应给予明确的反馈,包括成功、失败、加载等状态。
|
||||
|
||||
### 5. 容错友好
|
||||
预防用户错误,在错误发生时提供清晰的提示和解决方案。
|
||||
|
||||
---
|
||||
|
||||
## 颜色系统
|
||||
|
||||
### 主色调
|
||||
|
||||
```css
|
||||
/* 品牌主色 - Primary (基于 NEX Logo 紫红色) */
|
||||
--primary-50: #fce7f6;
|
||||
--primary-100: #f5bae6;
|
||||
--primary-200: #ee8dd6;
|
||||
--primary-300: #e760c6;
|
||||
--primary-400: #e033b6;
|
||||
--primary-500: #b8178d; /* 主色 - 匹配 NEX Logo */
|
||||
--primary-600: #9c1477;
|
||||
--primary-700: #801161;
|
||||
--primary-800: #640d4b;
|
||||
--primary-900: #480a35;
|
||||
```
|
||||
|
||||
### 辅助色系
|
||||
|
||||
```css
|
||||
/* 蓝色 - Blue (用于信息提示) */
|
||||
--blue-50: #e6f4ff;
|
||||
--blue-100: #bae0ff;
|
||||
--blue-200: #91caff;
|
||||
--blue-300: #69b1ff;
|
||||
--blue-400: #4096ff;
|
||||
--blue-500: #1677ff; /* 信息色 */
|
||||
--blue-600: #0958d9;
|
||||
--blue-700: #003eb3;
|
||||
--blue-800: #002c8c;
|
||||
--blue-900: #001d66;
|
||||
```
|
||||
|
||||
### 功能色
|
||||
|
||||
```css
|
||||
/* 成功 - Success */
|
||||
--success: #52c41a;
|
||||
--success-bg: #f6ffed;
|
||||
--success-border: #b7eb8f;
|
||||
|
||||
/* 警告 - Warning */
|
||||
--warning: #faad14;
|
||||
--warning-bg: #fffbe6;
|
||||
--warning-border: #ffe58f;
|
||||
|
||||
/* 错误 - Error */
|
||||
--error: #ff4d4f;
|
||||
--error-bg: #fff2f0;
|
||||
--error-border: #ffccc7;
|
||||
|
||||
/* 信息 - Info */
|
||||
--info: #1677ff;
|
||||
--info-bg: #e6f4ff;
|
||||
--info-border: #91caff;
|
||||
```
|
||||
|
||||
### 中性色
|
||||
|
||||
```css
|
||||
/* 文本颜色 */
|
||||
--text-primary: rgba(0, 0, 0, 0.88); /* 主要文本 */
|
||||
--text-secondary: rgba(0, 0, 0, 0.65); /* 次要文本 */
|
||||
--text-tertiary: rgba(0, 0, 0, 0.45); /* 辅助文本 */
|
||||
--text-disabled: rgba(0, 0, 0, 0.25); /* 禁用文本 */
|
||||
|
||||
/* 背景颜色 */
|
||||
--bg-primary: #ffffff;
|
||||
--bg-secondary: #fafafa;
|
||||
--bg-tertiary: #f5f5f5;
|
||||
--bg-disabled: #f0f0f0;
|
||||
|
||||
/* 边框颜色 */
|
||||
--border-primary: #d9d9d9;
|
||||
--border-secondary: #f0f0f0;
|
||||
```
|
||||
|
||||
### 颜色使用规范
|
||||
|
||||
1. **主色使用**: 主要用于关键操作按钮、重要信息高亮、链接等
|
||||
2. **功能色使用**: 严格按照语义使用,不可混淆
|
||||
3. **中性色使用**: 用于文本、背景、边框等基础元素
|
||||
4. **对比度**: 确保文本与背景的对比度符合 WCAG 2.0 标准(至少 4.5:1)
|
||||
|
||||
---
|
||||
|
||||
## 排版规范
|
||||
|
||||
### 字体家族
|
||||
|
||||
```css
|
||||
/* 默认字体栈 */
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||
'Noto Color Emoji';
|
||||
|
||||
/* 等宽字体(代码、数字) */
|
||||
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code',
|
||||
'Fira Mono', 'Droid Sans Mono', 'Source Code Pro', monospace;
|
||||
```
|
||||
|
||||
### 字号规范
|
||||
|
||||
| 用途 | 字号 | 行高 | Tailwind Class | 使用场景 |
|
||||
|------|------|------|----------------|----------|
|
||||
| 特大标题 | 32px | 1.35 | `text-4xl` | 页面主标题 |
|
||||
| 大标题 | 24px | 1.35 | `text-2xl` | 区块标题 |
|
||||
| 中标题 | 20px | 1.4 | `text-xl` | 卡片标题 |
|
||||
| 小标题 | 16px | 1.5 | `text-base` | 表单标签 |
|
||||
| 正文 | 14px | 1.5714 | `text-sm` | 正文内容 |
|
||||
| 辅助文字 | 12px | 1.6667 | `text-xs` | 说明文字 |
|
||||
|
||||
### 字重规范
|
||||
|
||||
- **Regular (400)**: 正文内容
|
||||
- **Medium (500)**: 表单标签、列表项
|
||||
- **Semibold (600)**: 小标题、强调文本
|
||||
- **Bold (700)**: 标题、重要信息
|
||||
|
||||
### 文本颜色使用
|
||||
|
||||
```jsx
|
||||
// 主要文本
|
||||
<p className="text-gray-900">主要内容</p>
|
||||
|
||||
// 次要文本
|
||||
<p className="text-gray-600">次要内容</p>
|
||||
|
||||
// 辅助文本
|
||||
<p className="text-gray-400">辅助说明</p>
|
||||
|
||||
// 禁用文本
|
||||
<p className="text-gray-300">禁用状态</p>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 间距系统
|
||||
|
||||
### 基础间距单位
|
||||
|
||||
基于 **8px** 网格系统,所有间距应为 8 的倍数。
|
||||
|
||||
| 名称 | 数值 | Tailwind Class | 用途 |
|
||||
|------|------|----------------|------|
|
||||
| xs | 4px | `space-1` / `p-1` / `m-1` | 紧凑间距 |
|
||||
| sm | 8px | `space-2` / `p-2` / `m-2` | 小间距 |
|
||||
| md | 16px | `space-4` / `p-4` / `m-4` | 标准间距 |
|
||||
| lg | 24px | `space-6` / `p-6` / `m-6` | 大间距 |
|
||||
| xl | 32px | `space-8` / `p-8` / `m-8` | 超大间距 |
|
||||
| 2xl | 48px | `space-12` / `p-12` / `m-12` | 区块间距 |
|
||||
|
||||
### 间距使用场景
|
||||
|
||||
1. **组件内边距**: 通常使用 12px (p-3) 或 16px (p-4)
|
||||
2. **组件外边距**: 标准使用 16px (m-4) 或 24px (m-6)
|
||||
3. **区块间距**: 使用 32px (mb-8) 或 48px (mb-12)
|
||||
4. **栅格间距**: 标准使用 16px 或 24px
|
||||
|
||||
---
|
||||
|
||||
## 组件规范
|
||||
|
||||
### 按钮 (Button)
|
||||
|
||||
#### 类型与场景
|
||||
|
||||
```jsx
|
||||
import { Button } from 'antd';
|
||||
|
||||
// 主要按钮 - 用于主要操作
|
||||
<Button type="primary">确定</Button>
|
||||
|
||||
// 次要按钮 - 用于次要操作
|
||||
<Button>取消</Button>
|
||||
|
||||
// 文本按钮 - 用于辅助操作
|
||||
<Button type="text">详情</Button>
|
||||
|
||||
// 链接按钮 - 用于跳转
|
||||
<Button type="link">查看更多</Button>
|
||||
|
||||
// 危险按钮 - 用于删除等危险操作
|
||||
<Button danger>删除</Button>
|
||||
```
|
||||
|
||||
#### 尺寸规范
|
||||
|
||||
- **Large**: 高度 40px,用于页面主要操作
|
||||
- **Middle**: 高度 32px,默认尺寸
|
||||
- **Small**: 高度 24px,用于紧凑场景
|
||||
|
||||
#### 使用规范
|
||||
|
||||
1. 一个操作区域最多只有一个主要按钮
|
||||
2. 按钮文字应简洁明确,建议不超过 4 个字
|
||||
3. 危险操作必须使用二次确认
|
||||
4. 按钮组内按钮间距为 8px
|
||||
|
||||
### 表单 (Form)
|
||||
|
||||
#### 布局规范
|
||||
|
||||
```jsx
|
||||
import { Form, Input } from 'antd';
|
||||
|
||||
<Form layout="vertical" className="max-w-2xl">
|
||||
<Form.Item
|
||||
label="用户名"
|
||||
name="username"
|
||||
rules={[{ required: true, message: '请输入用户名' }]}
|
||||
>
|
||||
<Input placeholder="请输入用户名" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
```
|
||||
|
||||
#### 表单规范
|
||||
|
||||
1. **标签对齐**: 默认使用垂直布局 (vertical)
|
||||
2. **必填标识**: 使用红色星号 (*) 标识
|
||||
3. **字段宽度**: 根据内容长度设置合理宽度
|
||||
4. **错误提示**: 实时验证,错误信息显示在字段下方
|
||||
5. **表单间距**: 表单项之间间距 24px
|
||||
|
||||
### 表格 (Table)
|
||||
|
||||
#### 基础配置
|
||||
|
||||
```jsx
|
||||
import { Table } from 'antd';
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
}}
|
||||
scroll={{ x: 1200 }}
|
||||
/>
|
||||
```
|
||||
|
||||
#### 表格规范
|
||||
|
||||
1. **分页**: 默认每页 10 条,提供分页器
|
||||
2. **行高**: 标准行高 54px (middle 模式)
|
||||
3. **操作列**: 固定在右侧,宽度根据操作数量调整
|
||||
4. **空状态**: 使用统一的空状态提示
|
||||
5. **加载状态**: 使用 loading 属性显示加载状态
|
||||
|
||||
### 卡片 (Card)
|
||||
|
||||
```jsx
|
||||
import { Card } from 'antd';
|
||||
|
||||
<Card
|
||||
title="卡片标题"
|
||||
extra={<Button type="link">更多</Button>}
|
||||
className="mb-4"
|
||||
>
|
||||
<p>卡片内容</p>
|
||||
</Card>
|
||||
```
|
||||
|
||||
#### 卡片规范
|
||||
|
||||
1. **内边距**: 标准 24px
|
||||
2. **圆角**: 8px (rounded-lg)
|
||||
3. **阴影**: 默认使用 `shadow-sm`
|
||||
4. **间距**: 卡片之间间距 16px
|
||||
|
||||
---
|
||||
|
||||
## 布局规范
|
||||
|
||||
### 页面布局结构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Header (64px) │
|
||||
├─────────────────────────────────────┤
|
||||
│ Sider │ Content Area │
|
||||
│ (200px)│ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 栅格系统
|
||||
|
||||
采用 24 栏栅格系统,基于 Ant Design Grid 组件。
|
||||
|
||||
```jsx
|
||||
import { Row, Col } from 'antd';
|
||||
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col xs={24} sm={12} md={8} lg={6} xl={4}>
|
||||
内容
|
||||
</Col>
|
||||
</Row>
|
||||
```
|
||||
|
||||
### 布局规范
|
||||
|
||||
1. **页面内边距**: 24px
|
||||
2. **内容最大宽度**: 1200px (根据实际需求调整)
|
||||
3. **侧边栏宽度**: 200px (收起后 64px)
|
||||
4. **顶部导航高度**: 64px
|
||||
5. **栅格间距**: 水平 16px,垂直 16px
|
||||
|
||||
---
|
||||
|
||||
## 交互规范
|
||||
|
||||
### 反馈
|
||||
|
||||
#### 全局提示 (Message)
|
||||
|
||||
```jsx
|
||||
import { message } from 'antd';
|
||||
|
||||
// 成功提示
|
||||
message.success('操作成功');
|
||||
|
||||
// 错误提示
|
||||
message.error('操作失败,请重试');
|
||||
|
||||
// 警告提示
|
||||
message.warning('请注意数据安全');
|
||||
|
||||
// 加载提示
|
||||
const hide = message.loading('加载中...');
|
||||
```
|
||||
|
||||
#### 通知提醒 (Notification)
|
||||
|
||||
```jsx
|
||||
import { notification } from 'antd';
|
||||
|
||||
notification.open({
|
||||
message: '系统通知',
|
||||
description: '您有新的消息,请及时查看',
|
||||
duration: 4.5,
|
||||
});
|
||||
```
|
||||
|
||||
#### 模态对话框 (Modal)
|
||||
|
||||
```jsx
|
||||
import { Modal } from 'antd';
|
||||
|
||||
// 确认对话框
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '删除后数据无法恢复,确定要删除吗?',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
onOk() {
|
||||
// 处理确认
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 加载状态
|
||||
|
||||
1. **局部加载**: 使用 Spin 组件
|
||||
2. **按钮加载**: 设置 loading 属性
|
||||
3. **页面加载**: 骨架屏 (Skeleton)
|
||||
4. **表格加载**: Table 的 loading 属性
|
||||
|
||||
### 动画效果
|
||||
|
||||
1. **过渡时间**: 标准 300ms
|
||||
2. **缓动函数**: ease-in-out
|
||||
3. **使用场景**: 展开/收起、显示/隐藏、页面切换
|
||||
|
||||
---
|
||||
|
||||
## 响应式设计
|
||||
|
||||
### 断点定义
|
||||
|
||||
| 断点 | 屏幕宽度 | 设备类型 | Tailwind 前缀 |
|
||||
|------|----------|----------|---------------|
|
||||
| xs | < 576px | 手机竖屏 | - |
|
||||
| sm | ≥ 576px | 手机横屏 | `sm:` |
|
||||
| md | ≥ 768px | 平板 | `md:` |
|
||||
| lg | ≥ 992px | 桌面显示器 | `lg:` |
|
||||
| xl | ≥ 1200px | 大桌面显示器 | `xl:` |
|
||||
| 2xl | ≥ 1600px | 超大显示器 | `2xl:` |
|
||||
|
||||
### 响应式策略
|
||||
|
||||
1. **移动优先**: 先设计移动端,再扩展到桌面端
|
||||
2. **弹性布局**: 使用 Flexbox 和 Grid
|
||||
3. **自适应图片**: 使用相对单位和 max-width
|
||||
4. **触摸友好**: 移动端可点击区域不小于 44x44px
|
||||
|
||||
---
|
||||
|
||||
## 页面模板
|
||||
|
||||
### 通用页面结构
|
||||
|
||||
```jsx
|
||||
import { Layout, Breadcrumb } from 'antd';
|
||||
const { Content } = Layout;
|
||||
|
||||
function PageTemplate() {
|
||||
return (
|
||||
<Content className="p-6">
|
||||
{/* 面包屑导航 */}
|
||||
<Breadcrumb className="mb-4">
|
||||
<Breadcrumb.Item>首页</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>当前页面</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
|
||||
{/* 页面标题区 */}
|
||||
<div className="mb-6">
|
||||
<h1 className="text-2xl font-semibold text-gray-900">页面标题</h1>
|
||||
<p className="text-sm text-gray-500 mt-1">页面描述信息</p>
|
||||
</div>
|
||||
|
||||
{/* 主要内容区 */}
|
||||
<div className="bg-white rounded-lg shadow-sm p-6">
|
||||
{/* 页面内容 */}
|
||||
</div>
|
||||
</Content>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 已实现的页面模板
|
||||
|
||||
#### 1. 主框架页面 ✓
|
||||
|
||||
完整的应用框架布局,包含:
|
||||
- 侧边菜单栏(支持收起/展开,两级菜单)
|
||||
- 顶部导航栏(搜索、消息、用户信息)
|
||||
- 内容区域(可滚动)
|
||||
|
||||
**详细文档**: [主框架页面设计规范](../docs/pages/main-layout.md)
|
||||
|
||||
**主要特性**:
|
||||
- 基于 NEX Logo 的品牌配色 (#b8178d)
|
||||
- 菜单数据 JSON 配置
|
||||
- 响应式布局
|
||||
- 徽章系统(HOT/NEW)
|
||||
- 用户下拉菜单
|
||||
|
||||
**使用示例**:
|
||||
```jsx
|
||||
import MainLayout from './components/MainLayout'
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<MainLayout>
|
||||
{/* 页面内容 */}
|
||||
</MainLayout>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 概览页 (Dashboard) ✓
|
||||
|
||||
数据概览页面,展示:
|
||||
- 统计卡片(证书总量、资产数量等)
|
||||
- 环形进度图表
|
||||
- 数据走势图
|
||||
|
||||
**主要组件**:
|
||||
- 响应式栅格布局 (Row/Col)
|
||||
- 统计卡片 (Statistic)
|
||||
- 进度条 (Progress)
|
||||
- 图表区域
|
||||
|
||||
### 待完善的页面模板
|
||||
|
||||
后续将添加以下页面模板:
|
||||
|
||||
- [ ] **列表页**: 数据表格、筛选、操作
|
||||
- [ ] **详情页**: 信息展示、关联数据
|
||||
- [ ] **表单页**: 数据录入、验证、提交
|
||||
- [ ] **设置页**: 配置管理、偏好设置
|
||||
|
||||
---
|
||||
|
||||
## 开发规范
|
||||
|
||||
### 代码组织
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/ # 公共组件
|
||||
│ ├── Button/
|
||||
│ ├── Card/
|
||||
│ └── ...
|
||||
├── pages/ # 页面组件
|
||||
│ ├── Dashboard/
|
||||
│ ├── List/
|
||||
│ └── ...
|
||||
├── styles/ # 样式文件
|
||||
│ ├── globals.css
|
||||
│ └── variables.css
|
||||
├── utils/ # 工具函数
|
||||
└── constants/ # 常量定义
|
||||
```
|
||||
|
||||
### 命名规范
|
||||
|
||||
1. **组件命名**: 大驼峰 (PascalCase),如 `UserList`
|
||||
2. **文件命名**: 与组件同名,如 `UserList.jsx`
|
||||
3. **样式类命名**: 小写短横线 (kebab-case)
|
||||
4. **常量命名**: 全大写下划线 (UPPER_SNAKE_CASE)
|
||||
|
||||
### CSS 使用规范
|
||||
|
||||
1. **优先使用 Tailwind**: 布局、间距、颜色等
|
||||
2. **Ant Design 定制**: 通过主题配置实现
|
||||
3. **自定义样式**: 仅在必要时使用,放在组件目录下
|
||||
|
||||
---
|
||||
|
||||
## 版本记录
|
||||
|
||||
| 版本 | 日期 | 说明 |
|
||||
|------|------|------|
|
||||
| 1.0.0 | 2024-11-04 | 初始版本,建立基础设计规范 |
|
||||
| 1.1.0 | 2024-11-04 | 更新品牌配色,完成主框架页面和概览页 |
|
||||
|
||||
---
|
||||
|
||||
## 参考资源
|
||||
|
||||
- [Ant Design 官方文档](https://ant.design/)
|
||||
- [Tailwind CSS 官方文档](https://tailwindcss.com/)
|
||||
- [React 官方文档](https://react.dev/)
|
||||
- [Material Design 设计指南](https://material.io/design)
|
||||
|
||||
---
|
||||
|
||||
**维护者**: Nex Design Team
|
||||
**最后更新**: 2024-11-04
|
||||
|
|
@ -0,0 +1,330 @@
|
|||
# ConfirmDialog 组件
|
||||
|
||||
## 组件说明
|
||||
|
||||
确认对话框组件,基于 Ant Design Modal 封装,提供统一的确认对话框样式和交互。支持单个删除、批量删除、警告确认和通用确认等多种场景。
|
||||
|
||||
## 组件位置
|
||||
|
||||
```
|
||||
src/components/ConfirmDialog/ConfirmDialog.jsx
|
||||
```
|
||||
|
||||
## API 方法
|
||||
|
||||
组件以静态方法的形式提供,无需实例化,直接调用即可。
|
||||
|
||||
### ConfirmDialog.delete()
|
||||
|
||||
显示单个项目删除确认对话框。
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| title | string | 否 | '确认删除' | 对话框标题 |
|
||||
| itemName | string | 是 | - | 要删除的项目名称 |
|
||||
| itemInfo | string | 否 | - | 项目附加信息 |
|
||||
| onOk | function | 否 | - | 确认回调,支持返回 Promise |
|
||||
| onCancel | function | 否 | - | 取消回调 |
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```jsx
|
||||
import ConfirmDialog from '../components/ConfirmDialog/ConfirmDialog'
|
||||
|
||||
const handleDeleteUser = (record) => {
|
||||
ConfirmDialog.delete({
|
||||
itemName: `用户名:${record.userName}`,
|
||||
itemInfo: `姓名:${record.name}`,
|
||||
onOk() {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
// 执行删除操作
|
||||
deleteUser(record.id)
|
||||
resolve()
|
||||
Toast.success('删除成功', `用户 "${record.userName}" 已成功删除`)
|
||||
}, 1000)
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### ConfirmDialog.batchDelete()
|
||||
|
||||
显示批量删除确认对话框。
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| count | number | 是 | - | 要删除的项目数量 |
|
||||
| items | Array<ItemInfo> | 是 | - | 项目列表 |
|
||||
| onOk | function | 否 | - | 确认回调,支持返回 Promise |
|
||||
| onCancel | function | 否 | - | 取消回调 |
|
||||
|
||||
##### ItemInfo 对象
|
||||
|
||||
| 属性名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| name | string | 是 | 项目名称 |
|
||||
| info | string | 否 | 项目附加信息 |
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```jsx
|
||||
const handleBatchDelete = () => {
|
||||
const selectedUsers = filteredUsers.filter((u) => selectedRowKeys.includes(u.id))
|
||||
const items = selectedUsers.map((user) => ({
|
||||
name: user.userName,
|
||||
info: user.name,
|
||||
}))
|
||||
|
||||
ConfirmDialog.batchDelete({
|
||||
count: selectedRowKeys.length,
|
||||
items,
|
||||
onOk() {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
const count = selectedRowKeys.length
|
||||
const newUsers = filteredUsers.filter((u) => !selectedRowKeys.includes(u.id))
|
||||
setFilteredUsers(newUsers)
|
||||
setSelectedRowKeys([])
|
||||
resolve()
|
||||
Toast.success('批量删除成功', `已成功删除 ${count} 个用户`)
|
||||
}, 1000)
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### ConfirmDialog.warning()
|
||||
|
||||
显示警告确认对话框。
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| title | string | 是 | - | 对话框标题 |
|
||||
| content | string\|ReactNode | 是 | - | 对话框内容 |
|
||||
| okText | string | 否 | '确定' | 确认按钮文字 |
|
||||
| cancelText | string | 否 | '取消' | 取消按钮文字 |
|
||||
| onOk | function | 否 | - | 确认回调 |
|
||||
| onCancel | function | 否 | - | 取消回调 |
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```jsx
|
||||
const handleRiskyOperation = () => {
|
||||
ConfirmDialog.warning({
|
||||
title: '操作警告',
|
||||
content: '此操作可能影响系统稳定性,确定要继续吗?',
|
||||
okText: '继续操作',
|
||||
onOk() {
|
||||
// 执行危险操作
|
||||
performRiskyOperation()
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### ConfirmDialog.confirm()
|
||||
|
||||
显示通用确认对话框。
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| title | string | 是 | - | 对话框标题 |
|
||||
| content | string\|ReactNode | 是 | - | 对话框内容 |
|
||||
| okText | string | 否 | '确定' | 确认按钮文字 |
|
||||
| cancelText | string | 否 | '取消' | 取消按钮文字 |
|
||||
| okType | string | 否 | 'primary' | 确认按钮类型(primary/danger) |
|
||||
| onOk | function | 否 | - | 确认回调 |
|
||||
| onCancel | function | 否 | - | 取消回调 |
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```jsx
|
||||
const handleSubmit = () => {
|
||||
ConfirmDialog.confirm({
|
||||
title: '提交确认',
|
||||
content: '确定要提交当前表单吗?',
|
||||
okText: '提交',
|
||||
onOk() {
|
||||
// 执行提交操作
|
||||
submitForm()
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 完整使用示例
|
||||
|
||||
### 单个删除
|
||||
|
||||
```jsx
|
||||
import ConfirmDialog from '../components/ConfirmDialog/ConfirmDialog'
|
||||
import Toast from '../components/Toast/Toast'
|
||||
|
||||
function UserListPage() {
|
||||
const handleDeleteUser = (record) => {
|
||||
ConfirmDialog.delete({
|
||||
itemName: `用户名:${record.userName}`,
|
||||
itemInfo: `姓名:${record.name}`,
|
||||
onOk() {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
const newUsers = filteredUsers.filter((u) => u.id !== record.id)
|
||||
setFilteredUsers(newUsers)
|
||||
resolve()
|
||||
Toast.success('删除成功', `用户 "${record.userName}" 已成功删除`)
|
||||
}, 1000)
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Button onClick={() => handleDeleteUser(user)} danger>
|
||||
删除
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 批量删除
|
||||
|
||||
```jsx
|
||||
function UserListPage() {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState([])
|
||||
|
||||
const handleBatchDelete = () => {
|
||||
const selectedUsers = filteredUsers.filter((u) => selectedRowKeys.includes(u.id))
|
||||
const items = selectedUsers.map((user) => ({
|
||||
name: user.userName,
|
||||
info: user.name,
|
||||
}))
|
||||
|
||||
ConfirmDialog.batchDelete({
|
||||
count: selectedRowKeys.length,
|
||||
items,
|
||||
onOk() {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
const count = selectedRowKeys.length
|
||||
const newUsers = filteredUsers.filter((u) => !selectedRowKeys.includes(u.id))
|
||||
setFilteredUsers(newUsers)
|
||||
setSelectedRowKeys([])
|
||||
resolve()
|
||||
Toast.success('批量删除成功', `已成功删除 ${count} 个用户`)
|
||||
}, 1000)
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={handleBatchDelete}
|
||||
disabled={selectedRowKeys.length === 0}
|
||||
danger
|
||||
>
|
||||
批量删除
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 自定义内容
|
||||
|
||||
```jsx
|
||||
ConfirmDialog.confirm({
|
||||
title: '重置密码',
|
||||
content: (
|
||||
<div>
|
||||
<p>确定要重置用户 <strong>{user.userName}</strong> 的密码吗?</p>
|
||||
<p style={{ color: '#faad14' }}>新密码将发送到用户邮箱</p>
|
||||
</div>
|
||||
),
|
||||
okText: '确认重置',
|
||||
okType: 'primary',
|
||||
onOk() {
|
||||
resetPassword(user.id)
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### 异步操作处理
|
||||
|
||||
```jsx
|
||||
const handleDeleteUser = (record) => {
|
||||
ConfirmDialog.delete({
|
||||
itemName: `用户名:${record.userName}`,
|
||||
itemInfo: `姓名:${record.name}`,
|
||||
async onOk() {
|
||||
try {
|
||||
// 调用 API 删除用户
|
||||
await api.deleteUser(record.id)
|
||||
|
||||
// 更新本地数据
|
||||
const newUsers = filteredUsers.filter((u) => u.id !== record.id)
|
||||
setFilteredUsers(newUsers)
|
||||
|
||||
Toast.success('删除成功', `用户 "${record.userName}" 已成功删除`)
|
||||
} catch (error) {
|
||||
Toast.error('删除失败', error.message)
|
||||
throw error // 阻止对话框关闭
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 特性说明
|
||||
|
||||
### 删除确认样式
|
||||
|
||||
- 红色危险图标
|
||||
- 高亮显示要删除的项目信息
|
||||
- 红色警告提示"此操作不可恢复,请谨慎操作!"
|
||||
- 危险样式的确认按钮
|
||||
|
||||
### 批量删除列表
|
||||
|
||||
- 最多显示 200px 高度的滚动列表
|
||||
- 每个项目显示名称和附加信息
|
||||
- 项目间用分隔线隔开
|
||||
|
||||
### Promise 支持
|
||||
|
||||
- `onOk` 回调支持返回 Promise
|
||||
- 异步操作进行时,确认按钮显示 loading 状态
|
||||
- Promise reject 时,对话框不会关闭
|
||||
- 适合调用 API 等异步操作
|
||||
|
||||
### 居中显示
|
||||
|
||||
- 所有对话框默认垂直居中显示(`centered: true`)
|
||||
|
||||
## 使用场景
|
||||
|
||||
1. **删除确认** - 删除用户、设备、订单等数据前的确认
|
||||
2. **批量删除** - 批量删除多条数据前的确认
|
||||
3. **危险操作警告** - 执行可能影响系统的操作前的警告
|
||||
4. **通用确认** - 提交表单、保存设置等操作的确认
|
||||
5. **重要操作二次确认** - 任何需要用户明确确认的操作
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 删除操作统一使用红色危险样式,提醒用户谨慎操作
|
||||
2. `onOk` 回调支持同步和异步(返回 Promise)两种方式
|
||||
3. 批量删除时,`items` 数组会在对话框中完整展示,注意数量控制
|
||||
4. 对话框内容支持字符串和 React 节点,可以自定义复杂内容
|
||||
5. 确认按钮文字建议明确操作类型,如"确认删除"、"确认提交"等
|
||||
6. 配合 Toast 组件使用,提供操作结果反馈
|
||||
7. 异步操作失败时,throw error 可以阻止对话框关闭
|
||||
|
|
@ -0,0 +1,469 @@
|
|||
# DetailDrawer 组件
|
||||
|
||||
## 组件说明
|
||||
|
||||
详情抽屉组件,用于从页面右侧滑出显示详细信息。支持自定义标题、操作按钮、标签页等功能,内容区域可滚动,顶部标题栏固定。
|
||||
|
||||
## 组件位置
|
||||
|
||||
```
|
||||
src/components/DetailDrawer/DetailDrawer.jsx
|
||||
src/components/DetailDrawer/DetailDrawer.css
|
||||
```
|
||||
|
||||
## 参数说明
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| visible | boolean | 是 | - | 是否显示抽屉 |
|
||||
| onClose | function | 是 | - | 关闭抽屉回调 |
|
||||
| title | TitleConfig | 否 | - | 标题配置对象 |
|
||||
| headerActions | Array<ActionConfig> | 否 | [] | 顶部操作按钮数组 |
|
||||
| width | number | 否 | 1080 | 抽屉宽度(像素) |
|
||||
| children | ReactNode | 否 | - | 主要内容区域 |
|
||||
| tabs | Array<TabConfig> | 否 | - | 标签页配置数组 |
|
||||
|
||||
### 1. 小型抽屉 (Small) - 480px
|
||||
**适用场景:**
|
||||
- 简单的信息展示
|
||||
- 少量字段的表单(1-3个字段)
|
||||
- 快速操作面板
|
||||
- 通知详情
|
||||
|
||||
**示例:**
|
||||
```jsx
|
||||
<Drawer width={480} ... />
|
||||
```
|
||||
|
||||
### 2. 中型抽屉 (Medium) - 720px
|
||||
**适用场景:**
|
||||
- 详细信息展示(如主机详情)
|
||||
- 中等复杂度的表单(4-10个字段)
|
||||
- 数据编辑面板
|
||||
- 配置设置
|
||||
|
||||
**示例:**
|
||||
```jsx
|
||||
<Drawer width={720} ... />
|
||||
```
|
||||
**当前主机列表页面使用此宽度模式**
|
||||
|
||||
### 3. 大型抽屉 (Large) - 1080px
|
||||
**适用场景:**
|
||||
- 复杂的多步骤表单
|
||||
- 需要并排展示多列信息
|
||||
- 包含图表或复杂可视化内容
|
||||
- 嵌套子表格或列表
|
||||
|
||||
**示例:**
|
||||
```jsx
|
||||
<Drawer width={1080} ... />
|
||||
```
|
||||
|
||||
### TitleConfig 配置项
|
||||
|
||||
| 属性名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| text | string | 是 | 标题文本 |
|
||||
| badge | ReactNode | 否 | 状态徽标(如 Tag、Badge 组件) |
|
||||
| icon | ReactNode | 否 | 标题图标 |
|
||||
|
||||
### ActionConfig 配置项
|
||||
|
||||
| 属性名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| key | string | 是 | 按钮唯一标识 |
|
||||
| label | string | 是 | 按钮文本 |
|
||||
| icon | ReactNode | 否 | 按钮图标 |
|
||||
| type | string | 否 | 按钮类型(primary/default/dashed/text/link) |
|
||||
| danger | boolean | 否 | 是否为危险按钮 |
|
||||
| disabled | boolean | 否 | 是否禁用 |
|
||||
| onClick | function | 否 | 点击回调函数 |
|
||||
|
||||
### TabConfig 配置项
|
||||
|
||||
| 属性名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| key | string | 是 | 标签页唯一标识 |
|
||||
| label | ReactNode | 是 | 标签页标题(支持图标+文字) |
|
||||
| content | ReactNode | 是 | 标签页内容 |
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础用法
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react'
|
||||
import DetailDrawer from '../components/DetailDrawer/DetailDrawer'
|
||||
|
||||
function MyPage() {
|
||||
const [showDrawer, setShowDrawer] = useState(false)
|
||||
const [selectedItem, setSelectedItem] = useState(null)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setShowDrawer(true)}>查看详情</Button>
|
||||
|
||||
<DetailDrawer
|
||||
visible={showDrawer}
|
||||
onClose={() => setShowDrawer(false)}
|
||||
title={{
|
||||
text: selectedItem?.name || '详情',
|
||||
}}
|
||||
>
|
||||
<div style={{ padding: 24 }}>
|
||||
<p>详情内容</p>
|
||||
</div>
|
||||
</DetailDrawer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 带状态徽标
|
||||
|
||||
```jsx
|
||||
import { Tag, Badge } from 'antd'
|
||||
|
||||
<DetailDrawer
|
||||
visible={showDetailDrawer}
|
||||
onClose={() => setShowDetailDrawer(false)}
|
||||
title={{
|
||||
text: selectedUser?.userName || '',
|
||||
badge: (
|
||||
<Tag color={selectedUser?.status === 'enabled' ? 'green' : 'default'}>
|
||||
{selectedUser?.status === 'enabled' ? '启用' : '停用'}
|
||||
</Tag>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{/* 内容 */}
|
||||
</DetailDrawer>
|
||||
```
|
||||
|
||||
### 带操作按钮
|
||||
|
||||
```jsx
|
||||
import { EditOutlined, DeleteOutlined } from '@ant-design/icons'
|
||||
|
||||
<DetailDrawer
|
||||
visible={showDetailDrawer}
|
||||
onClose={() => setShowDetailDrawer(false)}
|
||||
title={{
|
||||
text: selectedUser?.userName || '',
|
||||
}}
|
||||
headerActions={[
|
||||
{
|
||||
key: 'edit',
|
||||
label: '编辑',
|
||||
icon: <EditOutlined />,
|
||||
onClick: () => {
|
||||
setEditMode('edit')
|
||||
setShowEditDrawer(true)
|
||||
setShowDetailDrawer(false)
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: '删除',
|
||||
icon: <DeleteOutlined />,
|
||||
danger: true,
|
||||
onClick: () => {
|
||||
setShowDetailDrawer(false)
|
||||
handleDelete(selectedUser)
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
{/* 内容 */}
|
||||
</DetailDrawer>
|
||||
```
|
||||
|
||||
### 使用 InfoPanel 显示信息
|
||||
|
||||
```jsx
|
||||
import DetailDrawer from '../components/DetailDrawer/DetailDrawer'
|
||||
import InfoPanel from '../components/InfoPanel/InfoPanel'
|
||||
|
||||
const userFields = [
|
||||
{ key: 'userName', label: '用户名', span: 6 },
|
||||
{ key: 'group', label: '用户分组', span: 6 },
|
||||
{ key: 'name', label: '姓名', span: 6 },
|
||||
{ key: 'userType', label: '用户类型', span: 6 },
|
||||
{
|
||||
key: 'status',
|
||||
label: '状态',
|
||||
span: 6,
|
||||
render: (value) => (
|
||||
<Tag color={value === 'enabled' ? 'green' : 'default'}>
|
||||
{value === 'enabled' ? '启用' : '停用'}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
<DetailDrawer
|
||||
visible={showDetailDrawer}
|
||||
onClose={() => setShowDetailDrawer(false)}
|
||||
title={{
|
||||
text: selectedUser?.userName || '',
|
||||
}}
|
||||
>
|
||||
<InfoPanel
|
||||
data={selectedUser}
|
||||
fields={userFields}
|
||||
actions={[
|
||||
{ key: 'reset', label: '重置密码', onClick: () => console.log('重置密码') },
|
||||
{ key: 'disable', label: '停用', onClick: () => console.log('停用') },
|
||||
]}
|
||||
/>
|
||||
</DetailDrawer>
|
||||
```
|
||||
|
||||
### 带标签页
|
||||
|
||||
```jsx
|
||||
import { DatabaseOutlined, UserOutlined } from '@ant-design/icons'
|
||||
|
||||
<DetailDrawer
|
||||
visible={showDetailDrawer}
|
||||
onClose={() => setShowDetailDrawer(false)}
|
||||
title={{
|
||||
text: selectedHost?.name || '',
|
||||
badge: (
|
||||
<Badge
|
||||
status={selectedHost?.status === 'online' ? 'success' : 'default'}
|
||||
text={selectedHost?.status === 'online' ? '在线' : '离线'}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
headerActions={[
|
||||
{
|
||||
key: 'edit',
|
||||
label: '编辑',
|
||||
icon: <EditOutlined />,
|
||||
onClick: handleEdit,
|
||||
},
|
||||
]}
|
||||
width={1080}
|
||||
tabs={[
|
||||
{
|
||||
key: 'images',
|
||||
label: (
|
||||
<span>
|
||||
<DatabaseOutlined style={{ marginRight: 8 }} />
|
||||
终端镜像
|
||||
</span>
|
||||
),
|
||||
content: (
|
||||
<div className="card-list">
|
||||
{/* 镜像列表内容 */}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'users',
|
||||
label: (
|
||||
<span>
|
||||
<UserOutlined style={{ marginRight: 8 }} />
|
||||
终端用户
|
||||
</span>
|
||||
),
|
||||
content: (
|
||||
<div className="card-list">
|
||||
{/* 用户列表内容 */}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InfoPanel data={selectedHost} fields={hostFields} />
|
||||
</DetailDrawer>
|
||||
```
|
||||
|
||||
### 完整示例
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react'
|
||||
import DetailDrawer from '../components/DetailDrawer/DetailDrawer'
|
||||
import InfoPanel from '../components/InfoPanel/InfoPanel'
|
||||
import { Tag, Badge, Card } from 'antd'
|
||||
import { EditOutlined, DeleteOutlined, DatabaseOutlined, UserOutlined } from '@ant-design/icons'
|
||||
|
||||
function UserListPage() {
|
||||
const [showDetailDrawer, setShowDetailDrawer] = useState(false)
|
||||
const [selectedUser, setSelectedUser] = useState(null)
|
||||
|
||||
const userFields = [
|
||||
{ key: 'userName', label: '用户名', span: 6 },
|
||||
{ key: 'group', label: '用户分组', span: 6 },
|
||||
{ key: 'name', label: '姓名', span: 6 },
|
||||
{ key: 'grantedImages', label: '授权镜像', span: 6 },
|
||||
{ key: 'userType', label: '用户类型', span: 6 },
|
||||
{ key: 'grantedTerminals', label: '授权终端', span: 6 },
|
||||
{
|
||||
key: 'status',
|
||||
label: '启停用',
|
||||
span: 6,
|
||||
render: (value) => (
|
||||
<Tag color={value === 'enabled' ? 'green' : 'default'}>
|
||||
{value === 'enabled' ? '启用' : '停用'}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
const detailTabs = [
|
||||
{
|
||||
key: 'terminals',
|
||||
label: (
|
||||
<span>
|
||||
<DesktopOutlined style={{ marginRight: 8 }} />
|
||||
授权终端
|
||||
</span>
|
||||
),
|
||||
content: (
|
||||
<div className="card-list">
|
||||
{selectedUser?.terminals?.map((terminal) => (
|
||||
<Card key={terminal.id}>
|
||||
<h4>{terminal.name}</h4>
|
||||
<p>IP: {terminal.ip}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'images',
|
||||
label: (
|
||||
<span>
|
||||
<DatabaseOutlined style={{ marginRight: 8 }} />
|
||||
授权镜像
|
||||
</span>
|
||||
),
|
||||
content: (
|
||||
<div className="card-list">
|
||||
{selectedUser?.images?.map((image) => (
|
||||
<Card key={image.id}>
|
||||
<h4>{image.name}</h4>
|
||||
<p>系统: {image.os}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 列表页面 */}
|
||||
<ListTable
|
||||
columns={columns}
|
||||
dataSource={users}
|
||||
onRowClick={(record) => {
|
||||
setSelectedUser(record)
|
||||
setShowDetailDrawer(true)
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 详情抽屉 */}
|
||||
<DetailDrawer
|
||||
visible={showDetailDrawer}
|
||||
onClose={() => setShowDetailDrawer(false)}
|
||||
title={{
|
||||
text: selectedUser?.userName || '',
|
||||
badge: (
|
||||
<Tag color={selectedUser?.status === 'enabled' ? 'green' : 'default'}>
|
||||
{selectedUser?.status === 'enabled' ? '启用' : '停用'}
|
||||
</Tag>
|
||||
),
|
||||
}}
|
||||
headerActions={[
|
||||
{
|
||||
key: 'edit',
|
||||
label: '编辑',
|
||||
icon: <EditOutlined />,
|
||||
onClick: () => {
|
||||
setShowDetailDrawer(false)
|
||||
handleEdit(selectedUser)
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: '删除',
|
||||
icon: <DeleteOutlined />,
|
||||
danger: true,
|
||||
onClick: () => {
|
||||
setShowDetailDrawer(false)
|
||||
handleDelete(selectedUser)
|
||||
},
|
||||
},
|
||||
]}
|
||||
width={1080}
|
||||
tabs={detailTabs}
|
||||
>
|
||||
<InfoPanel
|
||||
data={selectedUser}
|
||||
fields={userFields}
|
||||
actions={[
|
||||
{ key: 'move', label: '转移分组', type: 'primary', onClick: () => console.log('转移分组') },
|
||||
{ key: 'reset', label: '重置密码', onClick: () => console.log('重置密码') },
|
||||
{ key: 'disable', label: '停用', onClick: () => console.log('停用') },
|
||||
]}
|
||||
/>
|
||||
</DetailDrawer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 布局结构
|
||||
|
||||
抽屉采用固定头部、可滚动内容的布局:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ 标题栏(固定,不滚动) │
|
||||
│ [关闭] [标题] [徽标] [操作按钮] │
|
||||
├─────────────────────────────────────┤
|
||||
│ │
|
||||
│ 内容区域(可滚动) │
|
||||
│ - children 主要内容 │
|
||||
│ - tabs 标签页(可选) │
|
||||
│ │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 样式定制
|
||||
|
||||
组件提供以下 CSS 类名供自定义样式:
|
||||
|
||||
- `.detail-drawer-content` - 抽屉内容容器
|
||||
- `.detail-drawer-header` - 顶部标题栏
|
||||
- `.detail-drawer-header-left` - 标题栏左侧区域
|
||||
- `.detail-drawer-header-right` - 标题栏右侧区域
|
||||
- `.detail-drawer-close-button` - 关闭按钮
|
||||
- `.detail-drawer-header-info` - 标题信息容器
|
||||
- `.detail-drawer-title-icon` - 标题图标
|
||||
- `.detail-drawer-title` - 标题文本
|
||||
- `.detail-drawer-badge` - 徽标容器
|
||||
- `.detail-drawer-scrollable-content` - 可滚动内容区域
|
||||
- `.detail-drawer-tabs` - 标签页容器
|
||||
- `.detail-drawer-tab-content` - 标签页内容
|
||||
|
||||
## 使用场景
|
||||
|
||||
1. **查看详细信息** - 点击列表行显示详细信息
|
||||
2. **多标签页详情** - 在详情中展示不同类型的关联数据
|
||||
3. **带快捷操作的详情** - 在详情顶部提供编辑、删除等操作
|
||||
4. **复杂数据展示** - 配合 InfoPanel、Card 等组件展示复杂信息
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 抽屉宽度默认 1080px,可根据内容调整,建议取值范围:720-1200px
|
||||
2. 标题栏固定在顶部,不随内容滚动,确保操作按钮始终可见
|
||||
3. `children` 内容区域会自动应用内边距,`tabs` 内容需要自行控制样式
|
||||
4. 操作按钮数量不宜过多,建议不超过 3 个
|
||||
5. 使用 `tabs` 时,第一个标签页默认激活
|
||||
6. 关闭抽屉时建议清空选中状态,避免下次打开时显示旧数据
|
||||
7. 配合 InfoPanel 使用时,InfoPanel 会自动处理内边距
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
# InfoPanel 组件
|
||||
|
||||
## 组件说明
|
||||
|
||||
信息展示面板组件,用于以网格布局展示数据的字段信息,支持自定义字段渲染和操作按钮。常用于详情页面或抽屉中展示结构化数据。
|
||||
|
||||
## 组件位置
|
||||
|
||||
```
|
||||
src/components/InfoPanel/InfoPanel.jsx
|
||||
src/components/InfoPanel/InfoPanel.css
|
||||
```
|
||||
|
||||
## 参数说明
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| data | Object | 是 | - | 数据源对象 |
|
||||
| fields | Array<FieldConfig> | 否 | [] | 字段配置数组 |
|
||||
| actions | Array<ActionConfig> | 否 | [] | 操作按钮配置数组 |
|
||||
| gutter | Array<number> | 否 | [24, 16] | Grid 网格间距 [水平, 垂直] |
|
||||
|
||||
### FieldConfig 配置项
|
||||
|
||||
| 属性名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| key | string | 是 | 数据字段名 |
|
||||
| label | string | 是 | 字段显示标签 |
|
||||
| span | number | 否 | 网格占位份数(24栅格系统),默认 6 |
|
||||
| render | function(value, data) | 否 | 自定义渲染函数 |
|
||||
|
||||
### ActionConfig 配置项
|
||||
|
||||
| 属性名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| key | string | 是 | 按钮唯一标识 |
|
||||
| label | string | 是 | 按钮文本 |
|
||||
| icon | ReactNode | 否 | 按钮图标 |
|
||||
| type | string | 否 | 按钮类型(primary/default/dashed/text/link) |
|
||||
| danger | boolean | 否 | 是否为危险按钮 |
|
||||
| disabled | boolean | 否 | 是否禁用 |
|
||||
| onClick | function | 否 | 点击回调函数 |
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础用法
|
||||
|
||||
```jsx
|
||||
import InfoPanel from '../components/InfoPanel/InfoPanel'
|
||||
|
||||
function UserDetail() {
|
||||
const userData = {
|
||||
userName: 'admin',
|
||||
name: '系统管理员',
|
||||
group: '管理员组',
|
||||
userType: '管理员',
|
||||
status: 'enabled',
|
||||
grantedTerminals: 8,
|
||||
grantedImages: 21,
|
||||
}
|
||||
|
||||
const userFields = [
|
||||
{ key: 'userName', label: '用户名', span: 6 },
|
||||
{ key: 'name', label: '姓名', span: 6 },
|
||||
{ key: 'group', label: '用户分组', span: 6 },
|
||||
{ key: 'userType', label: '用户类型', span: 6 },
|
||||
{ key: 'grantedTerminals', label: '授权终端', span: 6 },
|
||||
{ key: 'grantedImages', label: '授权镜像', span: 6 },
|
||||
]
|
||||
|
||||
return <InfoPanel data={userData} fields={userFields} />
|
||||
}
|
||||
```
|
||||
|
||||
### 自定义字段渲染
|
||||
|
||||
```jsx
|
||||
import { Tag } from 'antd'
|
||||
|
||||
const userFields = [
|
||||
{ key: 'userName', label: '用户名', span: 6 },
|
||||
{ key: 'name', label: '姓名', span: 6 },
|
||||
{
|
||||
key: 'status',
|
||||
label: '状态',
|
||||
span: 6,
|
||||
render: (value) => (
|
||||
<Tag color={value === 'enabled' ? 'green' : 'default'}>
|
||||
{value === 'enabled' ? '启用' : '停用'}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: '描述',
|
||||
span: 18,
|
||||
render: (value) => value || '--', // 空值显示默认占位符
|
||||
},
|
||||
]
|
||||
|
||||
<InfoPanel data={userData} fields={userFields} />
|
||||
```
|
||||
|
||||
### 带操作按钮
|
||||
|
||||
```jsx
|
||||
import { LockOutlined } from '@ant-design/icons'
|
||||
|
||||
<InfoPanel
|
||||
data={userData}
|
||||
fields={userFields}
|
||||
actions={[
|
||||
{
|
||||
key: 'move',
|
||||
label: '转移分组',
|
||||
type: 'primary',
|
||||
onClick: () => console.log('转移分组'),
|
||||
},
|
||||
{
|
||||
key: 'blacklist',
|
||||
label: '加入黑名单',
|
||||
onClick: () => console.log('加入黑名单'),
|
||||
},
|
||||
{
|
||||
key: 'reset',
|
||||
label: '重置密码',
|
||||
onClick: () => console.log('重置密码'),
|
||||
},
|
||||
{
|
||||
key: 'disable',
|
||||
label: '停用',
|
||||
icon: <LockOutlined />,
|
||||
onClick: () => console.log('停用'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
```
|
||||
|
||||
### 配合 DetailDrawer 使用
|
||||
|
||||
```jsx
|
||||
import DetailDrawer from '../components/DetailDrawer/DetailDrawer'
|
||||
import InfoPanel from '../components/InfoPanel/InfoPanel'
|
||||
import { Tag } from 'antd'
|
||||
|
||||
function UserListPage() {
|
||||
const [showDetailDrawer, setShowDetailDrawer] = useState(false)
|
||||
const [selectedUser, setSelectedUser] = useState(null)
|
||||
|
||||
const userFields = [
|
||||
{ key: 'userName', label: '用户名', span: 6 },
|
||||
{ key: 'group', label: '用户分组', span: 6 },
|
||||
{ key: 'name', label: '姓名', span: 6 },
|
||||
{ key: 'grantedImages', label: '授权镜像', span: 6 },
|
||||
{ key: 'userType', label: '用户类型', span: 6 },
|
||||
{ key: 'grantedTerminals', label: '授权终端', span: 6 },
|
||||
{
|
||||
key: 'status',
|
||||
label: '启停用',
|
||||
span: 6,
|
||||
render: (value) => (
|
||||
<Tag color={value === 'enabled' ? 'green' : 'default'}>
|
||||
{value === 'enabled' ? '启用' : '停用'}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{ key: 'description', label: '描述', span: 18, render: () => '--' },
|
||||
]
|
||||
|
||||
return (
|
||||
<DetailDrawer
|
||||
visible={showDetailDrawer}
|
||||
onClose={() => setShowDetailDrawer(false)}
|
||||
title={{
|
||||
text: selectedUser?.userName || '',
|
||||
}}
|
||||
>
|
||||
<InfoPanel
|
||||
data={selectedUser}
|
||||
fields={userFields}
|
||||
actions={[
|
||||
{ key: 'move', label: '转移分组', type: 'primary', onClick: () => console.log('转移分组') },
|
||||
{ key: 'blacklist', label: '加入黑名单', onClick: () => console.log('加入黑名单') },
|
||||
{ key: 'reset', label: '重置密码', onClick: () => console.log('重置密码') },
|
||||
{ key: 'disable', label: '停用', onClick: () => console.log('停用') },
|
||||
]}
|
||||
/>
|
||||
</DetailDrawer>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 不同 span 值的布局
|
||||
|
||||
```jsx
|
||||
// 24栅格系统,一行总共24份
|
||||
const fields = [
|
||||
{ key: 'field1', label: '字段1', span: 6 }, // 占1/4宽度
|
||||
{ key: 'field2', label: '字段2', span: 6 }, // 占1/4宽度
|
||||
{ key: 'field3', label: '字段3', span: 6 }, // 占1/4宽度
|
||||
{ key: 'field4', label: '字段4', span: 6 }, // 占1/4宽度,满一行
|
||||
{ key: 'field5', label: '字段5', span: 8 }, // 占1/3宽度
|
||||
{ key: 'field6', label: '字段6', span: 8 }, // 占1/3宽度
|
||||
{ key: 'field7', label: '字段7', span: 8 }, // 占1/3宽度,满一行
|
||||
{ key: 'field8', label: '字段8', span: 12 }, // 占1/2宽度
|
||||
{ key: 'field9', label: '字段9', span: 12 }, // 占1/2宽度,满一行
|
||||
{ key: 'description', label: '描述', span: 24 }, // 占满整行
|
||||
]
|
||||
|
||||
<InfoPanel data={data} fields={fields} />
|
||||
```
|
||||
|
||||
### 访问完整数据对象
|
||||
|
||||
```jsx
|
||||
const fields = [
|
||||
{ key: 'userName', label: '用户名', span: 6 },
|
||||
{
|
||||
key: 'status',
|
||||
label: '状态信息',
|
||||
span: 18,
|
||||
// render 函数的第二个参数是完整的数据对象
|
||||
render: (value, data) => (
|
||||
<div>
|
||||
<Tag color={value === 'enabled' ? 'green' : 'default'}>
|
||||
{value === 'enabled' ? '启用' : '停用'}
|
||||
</Tag>
|
||||
<span style={{ marginLeft: 8 }}>
|
||||
授权终端:{data.grantedTerminals} 台
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
<InfoPanel data={userData} fields={fields} />
|
||||
```
|
||||
|
||||
### 自定义网格间距
|
||||
|
||||
```jsx
|
||||
// 默认间距 [24, 16]
|
||||
<InfoPanel
|
||||
data={userData}
|
||||
fields={userFields}
|
||||
gutter={[32, 24]} // 更大的间距
|
||||
/>
|
||||
|
||||
// 紧凑布局
|
||||
<InfoPanel
|
||||
data={userData}
|
||||
fields={userFields}
|
||||
gutter={[16, 12]} // 更小的间距
|
||||
/>
|
||||
```
|
||||
|
||||
## 布局说明
|
||||
|
||||
组件使用 Ant Design 的 24 栅格系统:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ span=6 span=6 span=6 span=6 │ 一行4列
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ span=8 span=8 span=8 │ 一行3列
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ span=12 span=12 │ 一行2列
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ span=24 │ 占满一行
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
常用 span 值:
|
||||
- `span=6` - 一行 4 列
|
||||
- `span=8` - 一行 3 列
|
||||
- `span=12` - 一行 2 列
|
||||
- `span=24` - 占满整行(适合描述、备注等长文本字段)
|
||||
|
||||
## 样式定制
|
||||
|
||||
组件提供以下 CSS 类名供自定义样式:
|
||||
|
||||
- `.info-panel` - 组件根容器
|
||||
- `.info-panel-item` - 单个字段容器
|
||||
- `.info-panel-label` - 字段标签
|
||||
- `.info-panel-value` - 字段值
|
||||
- `.info-panel-actions` - 操作按钮区域
|
||||
|
||||
## 使用场景
|
||||
|
||||
1. **详情页信息展示** - 在详情抽屉或页面中展示对象的属性信息
|
||||
2. **用户信息展示** - 展示用户的基本信息和状态
|
||||
3. **设备信息展示** - 展示设备的配置和参数
|
||||
4. **订单信息展示** - 展示订单的详细信息
|
||||
5. **任何结构化数据展示** - 以标签-值形式展示的数据
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. `data` 为 `null` 或 `undefined` 时组件不渲染任何内容
|
||||
2. 字段 `span` 值总和建议为 24 的倍数以保持布局整齐
|
||||
3. 长文本字段(如描述、备注)建议使用 `span=24` 或 `span=18`
|
||||
4. `render` 函数可以返回任何 React 节点,包括组件、文本、HTML 等
|
||||
5. 操作按钮会显示在所有字段下方,建议不超过 6 个按钮
|
||||
6. 使用 `render` 函数时,第一个参数是字段值,第二个参数是完整数据对象
|
||||
7. 网格间距 `gutter` 的第一个值是水平间距,第二个值是垂直间距
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
# ListActionBar 组件
|
||||
|
||||
## 组件说明
|
||||
|
||||
列表操作栏组件,提供统一的操作按钮区、搜索框、高级筛选和刷新功能。常用于列表页面的顶部操作区域。
|
||||
|
||||
## 组件位置
|
||||
|
||||
```
|
||||
src/components/ListActionBar/ListActionBar.jsx
|
||||
src/components/ListActionBar/ListActionBar.css
|
||||
```
|
||||
|
||||
## 参数说明
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| actions | Array<ActionConfig> | 否 | [] | 左侧操作按钮配置数组 |
|
||||
| search | SearchConfig | 否 | - | 搜索框配置对象 |
|
||||
| filter | FilterConfig | 否 | - | 高级筛选配置对象 |
|
||||
| showRefresh | boolean | 否 | false | 是否显示刷新按钮 |
|
||||
| onRefresh | function | 否 | - | 刷新按钮点击回调 |
|
||||
|
||||
### ActionConfig 配置项
|
||||
|
||||
| 属性名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| key | string | 是 | 按钮唯一标识 |
|
||||
| label | string | 是 | 按钮文本 |
|
||||
| icon | ReactNode | 否 | 按钮图标 |
|
||||
| type | string | 否 | 按钮类型(primary/default/dashed/text/link) |
|
||||
| disabled | boolean | 否 | 是否禁用 |
|
||||
| danger | boolean | 否 | 是否为危险按钮 |
|
||||
| onClick | function | 否 | 点击回调函数 |
|
||||
|
||||
### SearchConfig 配置项
|
||||
|
||||
| 属性名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| placeholder | string | 否 | 搜索框占位文本 |
|
||||
| width | number | 否 | 搜索框宽度,默认 280 |
|
||||
| value | string | 否 | 搜索框值(受控模式) |
|
||||
| onSearch | function(value: string) | 否 | 搜索回调 |
|
||||
| onChange | function(value: string) | 否 | 输入变化回调 |
|
||||
|
||||
### FilterConfig 配置项
|
||||
|
||||
| 属性名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| content | ReactNode | 是 | 筛选面板内容 |
|
||||
| title | string | 否 | 筛选面板标题 |
|
||||
| visible | boolean | 否 | 控制面板显示/隐藏 |
|
||||
| onVisibleChange | function(visible: boolean) | 否 | 显示状态变化回调 |
|
||||
| selectedLabel | string | 否 | 筛选按钮显示的标签文本 |
|
||||
| isActive | boolean | 否 | 是否处于激活状态(高亮显示) |
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础用法(仅操作按钮)
|
||||
|
||||
```jsx
|
||||
import ListActionBar from '../components/ListActionBar/ListActionBar'
|
||||
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'
|
||||
|
||||
<ListActionBar
|
||||
actions={[
|
||||
{
|
||||
key: 'add',
|
||||
label: '新增',
|
||||
icon: <PlusOutlined />,
|
||||
type: 'primary',
|
||||
onClick: () => console.log('新增'),
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: '批量删除',
|
||||
icon: <DeleteOutlined />,
|
||||
danger: true,
|
||||
disabled: selectedRowKeys.length === 0,
|
||||
onClick: handleBatchDelete,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
```
|
||||
|
||||
### 带搜索功能
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react'
|
||||
|
||||
function MyPage() {
|
||||
const [searchKeyword, setSearchKeyword] = useState('')
|
||||
|
||||
return (
|
||||
<ListActionBar
|
||||
actions={[
|
||||
{
|
||||
key: 'add',
|
||||
label: '新增用户',
|
||||
icon: <PlusOutlined />,
|
||||
type: 'primary',
|
||||
onClick: () => console.log('新增'),
|
||||
},
|
||||
]}
|
||||
search={{
|
||||
placeholder: '搜索用户名或姓名',
|
||||
value: searchKeyword,
|
||||
onSearch: (value) => {
|
||||
console.log('搜索:', value)
|
||||
setSearchKeyword(value)
|
||||
},
|
||||
onChange: (value) => setSearchKeyword(value),
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 带高级筛选
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react'
|
||||
import TreeFilterPanel from '../components/TreeFilterPanel/TreeFilterPanel'
|
||||
|
||||
function MyPage() {
|
||||
const [showFilterPopover, setShowFilterPopover] = useState(false)
|
||||
const [selectedGroup, setSelectedGroup] = useState(null)
|
||||
const [selectedGroupName, setSelectedGroupName] = useState('')
|
||||
|
||||
return (
|
||||
<ListActionBar
|
||||
actions={[
|
||||
{
|
||||
key: 'add',
|
||||
label: '新增',
|
||||
icon: <PlusOutlined />,
|
||||
type: 'primary',
|
||||
onClick: () => console.log('新增'),
|
||||
},
|
||||
]}
|
||||
search={{
|
||||
placeholder: '搜索关键词',
|
||||
onSearch: handleSearch,
|
||||
}}
|
||||
filter={{
|
||||
content: (
|
||||
<TreeFilterPanel
|
||||
treeData={treeData}
|
||||
selectedKey={selectedGroup}
|
||||
onConfirm={handleConfirmFilter}
|
||||
onClear={handleClearFilter}
|
||||
/>
|
||||
),
|
||||
title: '高级筛选',
|
||||
visible: showFilterPopover,
|
||||
onVisibleChange: setShowFilterPopover,
|
||||
selectedLabel: selectedGroupName,
|
||||
isActive: !!selectedGroup,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 完整示例(所有功能)
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react'
|
||||
import ListActionBar from '../components/ListActionBar/ListActionBar'
|
||||
import TreeFilterPanel from '../components/TreeFilterPanel/TreeFilterPanel'
|
||||
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'
|
||||
|
||||
function UserListPage() {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState([])
|
||||
const [searchKeyword, setSearchKeyword] = useState('')
|
||||
const [showFilterPopover, setShowFilterPopover] = useState(false)
|
||||
const [selectedGroup, setSelectedGroup] = useState(null)
|
||||
const [selectedGroupName, setSelectedGroupName] = useState('')
|
||||
|
||||
return (
|
||||
<ListActionBar
|
||||
actions={[
|
||||
{
|
||||
key: 'add',
|
||||
label: '新增用户',
|
||||
icon: <PlusOutlined />,
|
||||
type: 'primary',
|
||||
onClick: handleAdd,
|
||||
},
|
||||
{
|
||||
key: 'batchDelete',
|
||||
label: '批量删除',
|
||||
icon: <DeleteOutlined />,
|
||||
danger: true,
|
||||
disabled: selectedRowKeys.length === 0,
|
||||
onClick: handleBatchDelete,
|
||||
},
|
||||
]}
|
||||
search={{
|
||||
placeholder: '搜索用户名或姓名',
|
||||
value: searchKeyword,
|
||||
onSearch: handleSearch,
|
||||
onChange: handleSearch,
|
||||
}}
|
||||
filter={{
|
||||
content: (
|
||||
<TreeFilterPanel
|
||||
treeData={treeData}
|
||||
selectedKey={selectedGroup}
|
||||
onConfirm={handleConfirmFilter}
|
||||
onClear={handleClearFilter}
|
||||
/>
|
||||
),
|
||||
title: '高级筛选',
|
||||
visible: showFilterPopover,
|
||||
onVisibleChange: setShowFilterPopover,
|
||||
selectedLabel: selectedGroupName,
|
||||
isActive: !!selectedGroup,
|
||||
}}
|
||||
showRefresh
|
||||
onRefresh={() => console.log('刷新')}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 样式定制
|
||||
|
||||
组件提供以下 CSS 类名供自定义样式:
|
||||
|
||||
- `.list-action-bar` - 组件根容器
|
||||
- `.list-action-bar-left` - 左侧操作按钮区域
|
||||
- `.list-action-bar-right` - 右侧搜索筛选区域
|
||||
- `.filter-popover` - 筛选弹出层容器
|
||||
|
||||
## 使用场景
|
||||
|
||||
1. **列表页面顶部操作区** - 提供新增、批量操作等功能
|
||||
2. **带搜索的列表** - 快速搜索列表内容
|
||||
3. **带分组筛选的列表** - 通过树形结构筛选数据
|
||||
4. **需要刷新的列表** - 提供手动刷新功能
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. `actions` 数组中的每个按钮必须提供唯一的 `key` 值
|
||||
2. 搜索框支持受控和非受控两种模式,推荐使用受控模式以便更好地管理状态
|
||||
3. 筛选功能的 `content` 可以是任意 React 组件,常配合 `TreeFilterPanel` 使用
|
||||
4. 当筛选激活时(`isActive: true`),筛选按钮会显示为 primary 类型以提示用户
|
||||
5. 左侧操作按钮不宜过多,建议不超过 5 个,过多的操作可以通过下拉菜单组织
|
||||
|
|
@ -0,0 +1,385 @@
|
|||
# ListTable 组件
|
||||
|
||||
## 组件说明
|
||||
|
||||
列表表格组件,基于 Ant Design Table 组件封装,提供统一的表格样式、行选择、分页、滚动和行点击等功能。
|
||||
|
||||
## 组件位置
|
||||
|
||||
```
|
||||
src/components/ListTable/ListTable.jsx
|
||||
src/components/ListTable/ListTable.css
|
||||
```
|
||||
|
||||
## 参数说明
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| columns | Array<ColumnConfig> | 是 | - | 表格列配置数组 |
|
||||
| dataSource | Array<Object> | 是 | - | 表格数据源 |
|
||||
| rowKey | string | 否 | 'id' | 行数据的唯一标识字段名 |
|
||||
| selectedRowKeys | Array<string\|number> | 否 | [] | 选中行的 key 数组 |
|
||||
| onSelectionChange | function(keys: Array) | 否 | - | 行选择变化回调 |
|
||||
| pagination | Object\|false | 否 | 默认配置 | 分页配置,false 表示不分页 |
|
||||
| scroll | Object | 否 | { x: 1200 } | 表格滚动配置 |
|
||||
| onRowClick | function(record: Object) | 否 | - | 行点击回调 |
|
||||
| selectedRow | Object | 否 | - | 当前选中的行数据对象 |
|
||||
| loading | boolean | 否 | false | 表格加载状态 |
|
||||
| className | string | 否 | '' | 自定义类名 |
|
||||
|
||||
### ColumnConfig 列配置
|
||||
|
||||
继承自 Ant Design Table 的列配置,常用属性:
|
||||
|
||||
| 属性名 | 类型 | 说明 |
|
||||
|--------|------|------|
|
||||
| title | string\|ReactNode | 列标题 |
|
||||
| dataIndex | string | 数据字段名 |
|
||||
| key | string | 列唯一标识 |
|
||||
| width | number | 列宽度 |
|
||||
| align | 'left'\|'center'\|'right' | 对齐方式 |
|
||||
| fixed | 'left'\|'right' | 固定列 |
|
||||
| render | function(value, record, index) | 自定义渲染函数 |
|
||||
|
||||
### 默认分页配置
|
||||
|
||||
```javascript
|
||||
{
|
||||
pageSize: 10,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
}
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础用法
|
||||
|
||||
```jsx
|
||||
import ListTable from '../components/ListTable/ListTable'
|
||||
|
||||
function MyPage() {
|
||||
const columns = [
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'userName',
|
||||
key: 'userName',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '姓名',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render: (status) => (
|
||||
<Tag color={status === 'enabled' ? 'green' : 'default'}>
|
||||
{status === 'enabled' ? '启用' : '停用'}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
const dataSource = [
|
||||
{ id: 1, userName: 'admin', name: '管理员', status: 'enabled' },
|
||||
{ id: 2, userName: 'user', name: '张三', status: 'disabled' },
|
||||
]
|
||||
|
||||
return (
|
||||
<ListTable
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 带行选择
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react'
|
||||
import ListTable from '../components/ListTable/ListTable'
|
||||
|
||||
function UserListPage() {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState([])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* 显示选中的数量 */}
|
||||
<div>已选择 {selectedRowKeys.length} 项</div>
|
||||
|
||||
<ListTable
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
selectedRowKeys={selectedRowKeys}
|
||||
onSelectionChange={setSelectedRowKeys}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 带行点击和高亮
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react'
|
||||
import ListTable from '../components/ListTable/ListTable'
|
||||
|
||||
function UserListPage() {
|
||||
const [selectedUser, setSelectedUser] = useState(null)
|
||||
|
||||
const handleRowClick = (record) => {
|
||||
setSelectedUser(record)
|
||||
// 打开详情抽屉等操作
|
||||
setShowDetailDrawer(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<ListTable
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
onRowClick={handleRowClick}
|
||||
selectedRow={selectedUser}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 自定义分页
|
||||
|
||||
```jsx
|
||||
<ListTable
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
pagination={{
|
||||
pageSize: 20,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `总计 ${total} 条记录`,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
### 禁用分页
|
||||
|
||||
```jsx
|
||||
<ListTable
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
pagination={false}
|
||||
/>
|
||||
```
|
||||
|
||||
### 带加载状态
|
||||
|
||||
```jsx
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
function UserListPage() {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [dataSource, setDataSource] = useState([])
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const data = await api.fetchUsers()
|
||||
setDataSource(data)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<ListTable
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
loading={loading}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 横向滚动(列较多时)
|
||||
|
||||
```jsx
|
||||
<ListTable
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
scroll={{ x: 1600 }} // 内容宽度超过容器时出现横向滚动
|
||||
/>
|
||||
```
|
||||
|
||||
### 固定列
|
||||
|
||||
```jsx
|
||||
const columns = [
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
width: 80,
|
||||
fixed: 'left', // 固定在左侧
|
||||
},
|
||||
// ... 其他列
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
fixed: 'right', // 固定在右侧
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<Button type="link" size="small">编辑</Button>
|
||||
<Button type="link" size="small" danger>删除</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
<ListTable
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
scroll={{ x: 1600 }}
|
||||
/>
|
||||
```
|
||||
|
||||
### 完整示例
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react'
|
||||
import ListTable from '../components/ListTable/ListTable'
|
||||
import { Tag, Button, Space } from 'antd'
|
||||
import { EditOutlined, DeleteOutlined } from '@ant-design/icons'
|
||||
|
||||
function UserListPage() {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState([])
|
||||
const [selectedUser, setSelectedUser] = useState(null)
|
||||
const [showDetailDrawer, setShowDetailDrawer] = useState(false)
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'userName',
|
||||
key: 'userName',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '姓名',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '用户分组',
|
||||
dataIndex: 'group',
|
||||
key: 'group',
|
||||
width: 150,
|
||||
render: (text) => <Tag color="blue">{text}</Tag>,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
render: (status) => (
|
||||
<Tag color={status === 'enabled' ? 'green' : 'default'}>
|
||||
{status === 'enabled' ? '启用' : '停用'}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
render: (_, record) => (
|
||||
<Space size="small" onClick={(e) => e.stopPropagation()}>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => handleEdit(record)}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
icon={<DeleteOutlined />}
|
||||
danger
|
||||
onClick={() => handleDelete(record)}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
const handleRowClick = (record) => {
|
||||
setSelectedUser(record)
|
||||
setShowDetailDrawer(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<ListTable
|
||||
columns={columns}
|
||||
dataSource={filteredUsers}
|
||||
selectedRowKeys={selectedRowKeys}
|
||||
onSelectionChange={setSelectedRowKeys}
|
||||
onRowClick={handleRowClick}
|
||||
selectedRow={selectedUser}
|
||||
scroll={{ x: 1400 }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 样式定制
|
||||
|
||||
组件提供以下 CSS 类名供自定义样式:
|
||||
|
||||
- `.list-table-container` - 表格容器
|
||||
- `.row-selected` - 选中行的类名
|
||||
|
||||
## 使用场景
|
||||
|
||||
1. **用户列表** - 显示和管理用户数据
|
||||
2. **设备列表** - 显示和管理设备信息
|
||||
3. **订单列表** - 显示订单数据
|
||||
4. **任何需要表格展示的数据列表**
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. `columns` 配置中的 `key` 必须唯一
|
||||
2. `dataSource` 中的每条数据必须有 `rowKey` 指定的唯一标识字段(默认为 `id`)
|
||||
3. 操作列中的点击事件需要使用 `e.stopPropagation()` 阻止事件冒泡,避免触发行点击
|
||||
4. 当列数较多时,建议设置合适的 `scroll.x` 值并固定首尾列
|
||||
5. `selectedRow` 用于高亮显示,`selectedRowKeys` 用于多选
|
||||
6. 分页的 `total` 值会自动根据 `dataSource.length` 计算
|
||||
7. 使用 `render` 函数时,要注意性能,避免在渲染函数中进行复杂计算
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
# PageTitleBar 组件
|
||||
|
||||
## 组件说明
|
||||
|
||||
页面标题栏组件,用于显示页面的标题、描述信息、操作按钮和可选的展开/收起控制按钮。
|
||||
|
||||
## 组件位置
|
||||
|
||||
```
|
||||
src/components/PageTitleBar/PageTitleBar.jsx
|
||||
src/components/PageTitleBar/PageTitleBar.css
|
||||
```
|
||||
|
||||
## 参数说明
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| title | string | 是 | - | 页面标题文本 |
|
||||
| badge | ReactNode | 否 | - | 标题右侧的徽章内容(如状态标签等) |
|
||||
| description | string | 否 | - | 页面描述文本,显示在标题下方 |
|
||||
| actions | ReactNode | 否 | - | 右侧操作按钮区域内容 |
|
||||
| showToggle | boolean | 否 | false | 是否显示展开/收起按钮 |
|
||||
| onToggle | function(expanded: boolean) | 否 | - | 展开/收起状态变化时的回调函数 |
|
||||
| defaultExpanded | boolean | 否 | true | 默认展开状态 |
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础用法
|
||||
|
||||
```jsx
|
||||
import PageTitleBar from '../components/PageTitleBar/PageTitleBar'
|
||||
|
||||
function MyPage() {
|
||||
return (
|
||||
<div>
|
||||
<PageTitleBar
|
||||
title="用户列表"
|
||||
description="管理系统用户,包括用户信息、权限和授权管理"
|
||||
/>
|
||||
{/* 页面内容 */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 带徽章的标题
|
||||
|
||||
```jsx
|
||||
import { Tag } from 'antd'
|
||||
|
||||
<PageTitleBar
|
||||
title="主机详情"
|
||||
badge={<Tag color="green">在线</Tag>}
|
||||
description="查看主机的详细信息和运行状态"
|
||||
/>
|
||||
```
|
||||
|
||||
### 带展开/收起功能
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react'
|
||||
import PageTitleBar from '../components/PageTitleBar/PageTitleBar'
|
||||
|
||||
function MyPage() {
|
||||
const [showStatsPanel, setShowStatsPanel] = useState(true)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageTitleBar
|
||||
title="主机列表"
|
||||
description="查看和管理所有接入的主机终端"
|
||||
showToggle={true}
|
||||
onToggle={(expanded) => setShowStatsPanel(expanded)}
|
||||
/>
|
||||
|
||||
{/* 可展开/收起的内容区域 */}
|
||||
{showStatsPanel && (
|
||||
<div className="stats-panel">
|
||||
{/* 统计面板内容 */}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 带操作按钮
|
||||
|
||||
```jsx
|
||||
import { Button } from 'antd'
|
||||
import { PlusOutlined } from '@ant-design/icons'
|
||||
|
||||
<PageTitleBar
|
||||
title="用户列表"
|
||||
description="管理系统用户"
|
||||
actions={
|
||||
<Button type="primary" icon={<PlusOutlined />}>
|
||||
新增用户
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
```
|
||||
|
||||
## 样式定制
|
||||
|
||||
组件提供以下 CSS 类名供自定义样式:
|
||||
|
||||
- `.page-title-bar` - 组件根容器
|
||||
- `.title-bar-content` - 内容容器
|
||||
- `.title-bar-left` - 左侧内容区域
|
||||
- `.title-bar-right` - 右侧内容区域
|
||||
- `.title-group` - 标题和徽章组合
|
||||
- `.page-title` - 标题文本
|
||||
- `.title-badge` - 徽章容器
|
||||
- `.page-description` - 描述文本
|
||||
- `.title-actions` - 操作按钮区域
|
||||
- `.toggle-button` - 展开/收起按钮
|
||||
|
||||
## 使用场景
|
||||
|
||||
1. **列表页面** - 显示列表页面的标题和描述
|
||||
2. **详情页面** - 显示详情页的标题和状态标签
|
||||
3. **带统计面板的页面** - 配合展开/收起功能控制统计信息的显示
|
||||
4. **需要快捷操作的页面** - 通过 actions 参数在标题栏添加常用操作按钮
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. `title` 参数为必填项,建议简洁明了
|
||||
2. 当使用 `showToggle` 时,建议同时提供 `onToggle` 回调以响应状态变化
|
||||
3. `badge` 参数支持任何 React 节点,常用的如 Ant Design 的 Tag、Badge 组件
|
||||
4. `actions` 区域不建议放置过多按钮,以保持界面简洁
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
# 组件文档目录
|
||||
|
||||
## 概述
|
||||
|
||||
本目录包含 Nex Design 系统所有主要组件的详细文档,包括组件说明、参数配置、使用示例等。
|
||||
|
||||
## 组件列表
|
||||
|
||||
### 页面布局组件
|
||||
|
||||
1. **[PageTitleBar](./PageTitleBar.md)** - 页面标题栏组件
|
||||
- 显示页面标题、描述和操作按钮
|
||||
- 支持展开/收起功能
|
||||
- 适用于所有页面的顶部区域
|
||||
|
||||
### 列表相关组件
|
||||
|
||||
2. **[ListActionBar](./ListActionBar.md)** - 列表操作栏组件
|
||||
- 提供操作按钮、搜索、筛选功能
|
||||
- 适用于列表页面的顶部操作区
|
||||
|
||||
3. **[TreeFilterPanel](./TreeFilterPanel.md)** - 树形筛选面板组件
|
||||
- 树形结构的数据筛选
|
||||
- 支持搜索和多级展开
|
||||
- 配合 ListActionBar 使用
|
||||
|
||||
4. **[ListTable](./ListTable.md)** - 列表表格组件
|
||||
- 统一的表格样式和交互
|
||||
- 支持行选择、分页、排序
|
||||
- 适用于所有列表页面
|
||||
|
||||
### 详情展示组件
|
||||
|
||||
5. **[DetailDrawer](./DetailDrawer.md)** - 详情抽屉组件
|
||||
- 从右侧滑出的详情面板
|
||||
- 支持标签页和操作按钮
|
||||
- 固定头部,内容可滚动
|
||||
|
||||
6. **[InfoPanel](./InfoPanel.md)** - 信息展示面板组件
|
||||
- 网格布局展示结构化数据
|
||||
- 支持自定义字段渲染
|
||||
- 配合 DetailDrawer 使用
|
||||
|
||||
### 交互反馈组件
|
||||
|
||||
7. **[ConfirmDialog](./ConfirmDialog.md)** - 确认对话框组件
|
||||
- 提供统一的确认对话框样式
|
||||
- 支持删除、警告、通用确认等场景
|
||||
- 支持异步操作
|
||||
|
||||
8. **[Toast](./Toast.md)** - 通知反馈组件
|
||||
- 操作完成后的提示信息
|
||||
- 支持成功、错误、警告、信息四种类型
|
||||
- 从右上角滑出,自动消失
|
||||
|
||||
## 组件关系图
|
||||
|
||||
```
|
||||
页面结构层次:
|
||||
|
||||
┌─────────────────────────────────────────┐
|
||||
│ PageTitleBar (页面标题栏) │
|
||||
├─────────────────────────────────────────┤
|
||||
│ ListActionBar (操作栏) │
|
||||
│ ├─ 操作按钮 │
|
||||
│ ├─ 搜索框 │
|
||||
│ └─ TreeFilterPanel (筛选面板) │
|
||||
├─────────────────────────────────────────┤
|
||||
│ ListTable (数据表格) │
|
||||
│ └─ 点击行 → DetailDrawer │
|
||||
├─────────────────────────────────────────┤
|
||||
│ DetailDrawer (详情抽屉) │
|
||||
│ ├─ InfoPanel (基本信息) │
|
||||
│ └─ Tabs (关联数据标签页) │
|
||||
└─────────────────────────────────────────┘
|
||||
|
||||
交互反馈:
|
||||
操作 → ConfirmDialog (确认) → Toast (结果反馈)
|
||||
```
|
||||
|
||||
## 组件组合示例
|
||||
|
||||
### 标准列表页面
|
||||
|
||||
```jsx
|
||||
<PageTitleBar title="用户列表" description="..." />
|
||||
|
||||
<ListActionBar
|
||||
actions={[...]}
|
||||
search={{...}}
|
||||
filter={{
|
||||
content: <TreeFilterPanel {...} />
|
||||
}}
|
||||
/>
|
||||
|
||||
<ListTable
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
onRowClick={showDetail}
|
||||
/>
|
||||
|
||||
<DetailDrawer visible={showDrawer}>
|
||||
<InfoPanel data={selectedItem} fields={fields} />
|
||||
</DetailDrawer>
|
||||
```
|
||||
|
||||
### 删除操作流程
|
||||
|
||||
```jsx
|
||||
// 1. 点击删除按钮
|
||||
<Button onClick={() => handleDelete(record)}>删除</Button>
|
||||
|
||||
// 2. 显示确认对话框
|
||||
ConfirmDialog.delete({
|
||||
itemName: record.name,
|
||||
onOk: async () => {
|
||||
// 3. 执行删除
|
||||
await api.delete(record.id)
|
||||
// 4. 显示结果反馈
|
||||
Toast.success('删除成功')
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 使用指南
|
||||
|
||||
### 开始使用
|
||||
|
||||
1. 查看对应组件的详细文档
|
||||
2. 了解组件的参数配置
|
||||
3. 参考示例代码
|
||||
4. 根据实际需求调整参数
|
||||
|
||||
### 设计原则
|
||||
|
||||
- **一致性** - 所有组件使用统一的设计语言
|
||||
- **可复用** - 组件高度封装,易于复用
|
||||
- **可配置** - 提供丰富的配置选项
|
||||
- **易用性** - API 设计简洁直观
|
||||
|
||||
### 技术栈
|
||||
|
||||
- React 18
|
||||
- Ant Design 5.x
|
||||
- CSS Modules
|
||||
|
||||
## 更新记录
|
||||
|
||||
- 2025-11-04: 初始版本,包含 8 个核心组件文档
|
||||
|
|
@ -0,0 +1,398 @@
|
|||
# Toast 组件
|
||||
|
||||
## 组件说明
|
||||
|
||||
通知反馈组件,基于 Ant Design notification 封装,用于操作完成后向用户展示反馈信息。通知从右上角滑出,默认 3 秒后自动消失,最多同时显示 3 条通知。
|
||||
|
||||
## 组件位置
|
||||
|
||||
```
|
||||
src/components/Toast/Toast.jsx
|
||||
```
|
||||
|
||||
## 全局配置
|
||||
|
||||
```javascript
|
||||
notification.config({
|
||||
placement: 'topRight', // 通知位置:右上角
|
||||
top: 24, // 距离顶部 24px
|
||||
duration: 3, // 默认显示 3 秒
|
||||
maxCount: 3, // 最多同时显示 3 条
|
||||
})
|
||||
```
|
||||
|
||||
## API 方法
|
||||
|
||||
组件以静态方法的形式提供,无需实例化,直接调用即可。
|
||||
|
||||
### Toast.success()
|
||||
|
||||
显示成功通知(绿色图标)。
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| message | string | 是 | - | 主要消息内容 |
|
||||
| description | string | 否 | '' | 详细描述 |
|
||||
| duration | number | 否 | 3 | 显示时长(秒),0 表示不自动关闭 |
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```jsx
|
||||
import Toast from '../components/Toast/Toast'
|
||||
|
||||
// 简单成功提示
|
||||
Toast.success('操作成功')
|
||||
|
||||
// 带详细描述
|
||||
Toast.success('删除成功', '用户 "admin" 已成功删除')
|
||||
|
||||
// 自定义显示时长(5秒)
|
||||
Toast.success('保存成功', '您的设置已保存', 5)
|
||||
|
||||
// 不自动关闭
|
||||
Toast.success('操作成功', '请注意后续操作', 0)
|
||||
```
|
||||
|
||||
### Toast.error()
|
||||
|
||||
显示错误通知(红色图标)。
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| message | string | 是 | - | 主要消息内容 |
|
||||
| description | string | 否 | '' | 详细描述 |
|
||||
| duration | number | 否 | 3 | 显示时长(秒) |
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```jsx
|
||||
// 简单错误提示
|
||||
Toast.error('操作失败')
|
||||
|
||||
// 带错误详情
|
||||
Toast.error('删除失败', '该用户下还有关联数据,无法删除')
|
||||
|
||||
// API 错误处理
|
||||
try {
|
||||
await api.deleteUser(userId)
|
||||
Toast.success('删除成功')
|
||||
} catch (error) {
|
||||
Toast.error('删除失败', error.message)
|
||||
}
|
||||
```
|
||||
|
||||
### Toast.warning()
|
||||
|
||||
显示警告通知(橙色图标)。
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| message | string | 是 | - | 主要消息内容 |
|
||||
| description | string | 否 | '' | 详细描述 |
|
||||
| duration | number | 否 | 3 | 显示时长(秒) |
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```jsx
|
||||
// 警告提示
|
||||
Toast.warning('操作警告', '此操作可能影响系统稳定性')
|
||||
|
||||
// 权限警告
|
||||
Toast.warning('权限不足', '您没有执行此操作的权限')
|
||||
|
||||
// 数据警告
|
||||
Toast.warning('数据异常', '检测到部分数据可能不完整')
|
||||
```
|
||||
|
||||
### Toast.info()
|
||||
|
||||
显示信息通知(蓝色图标)。
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| message | string | 是 | - | 主要消息内容 |
|
||||
| description | string | 否 | '' | 详细描述 |
|
||||
| duration | number | 否 | 3 | 显示时长(秒) |
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```jsx
|
||||
// 信息提示
|
||||
Toast.info('系统提示', '系统将在5分钟后进行维护')
|
||||
|
||||
// 操作提示
|
||||
Toast.info('导入中', '正在导入数据,请稍候...')
|
||||
|
||||
// 功能提示
|
||||
Toast.info('新功能上线', '我们上线了新的数据导出功能')
|
||||
```
|
||||
|
||||
### Toast.custom()
|
||||
|
||||
显示自定义通知,支持所有 Ant Design notification 配置。
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| config | Object | 是 | 完整的 notification 配置对象 |
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```jsx
|
||||
import { Button } from 'antd'
|
||||
|
||||
// 带操作按钮的通知
|
||||
Toast.custom({
|
||||
message: '新版本可用',
|
||||
description: '发现新版本 v2.0.0,是否立即更新?',
|
||||
duration: 0,
|
||||
btn: (
|
||||
<Button type="primary" size="small" onClick={() => {
|
||||
console.log('更新')
|
||||
notification.close('update-key')
|
||||
}}>
|
||||
立即更新
|
||||
</Button>
|
||||
),
|
||||
key: 'update-key',
|
||||
})
|
||||
|
||||
// 自定义图标和样式
|
||||
Toast.custom({
|
||||
message: '自定义通知',
|
||||
description: '这是一个自定义样式的通知',
|
||||
icon: <StarOutlined style={{ color: '#faad14' }} />,
|
||||
style: {
|
||||
background: '#fffbe6',
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## 完整使用示例
|
||||
|
||||
### 配合 ConfirmDialog 使用
|
||||
|
||||
```jsx
|
||||
import ConfirmDialog from '../components/ConfirmDialog/ConfirmDialog'
|
||||
import Toast from '../components/Toast/Toast'
|
||||
|
||||
const handleDeleteUser = (record) => {
|
||||
ConfirmDialog.delete({
|
||||
itemName: `用户名:${record.userName}`,
|
||||
itemInfo: `姓名:${record.name}`,
|
||||
onOk() {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
const newUsers = filteredUsers.filter((u) => u.id !== record.id)
|
||||
setFilteredUsers(newUsers)
|
||||
resolve()
|
||||
Toast.success('删除成功', `用户 "${record.userName}" 已成功删除`)
|
||||
}, 1000)
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 批量操作反馈
|
||||
|
||||
```jsx
|
||||
const handleBatchDelete = () => {
|
||||
ConfirmDialog.batchDelete({
|
||||
count: selectedRowKeys.length,
|
||||
items: selectedUsers,
|
||||
onOk() {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
const count = selectedRowKeys.length
|
||||
const newUsers = filteredUsers.filter((u) => !selectedRowKeys.includes(u.id))
|
||||
setFilteredUsers(newUsers)
|
||||
setSelectedRowKeys([])
|
||||
resolve()
|
||||
Toast.success('批量删除成功', `已成功删除 ${count} 个用户`)
|
||||
}, 1000)
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 异步操作反馈
|
||||
|
||||
```jsx
|
||||
const handleSaveUser = async (userData) => {
|
||||
try {
|
||||
setLoading(true)
|
||||
await api.updateUser(userData)
|
||||
setShowEditDrawer(false)
|
||||
Toast.success('保存成功', '用户信息已更新')
|
||||
fetchUsers() // 重新加载列表
|
||||
} catch (error) {
|
||||
Toast.error('保存失败', error.message)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 多步骤操作反馈
|
||||
|
||||
```jsx
|
||||
const handleImportData = async () => {
|
||||
try {
|
||||
Toast.info('导入开始', '正在验证数据格式...')
|
||||
|
||||
await api.validateData()
|
||||
Toast.info('验证通过', '正在导入数据...')
|
||||
|
||||
const result = await api.importData()
|
||||
Toast.success('导入完成', `成功导入 ${result.count} 条数据`)
|
||||
|
||||
} catch (error) {
|
||||
if (error.type === 'validation') {
|
||||
Toast.warning('验证失败', error.message)
|
||||
} else {
|
||||
Toast.error('导入失败', error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 表单提交反馈
|
||||
|
||||
```jsx
|
||||
const handleSubmit = async (values) => {
|
||||
try {
|
||||
await api.createUser(values)
|
||||
Toast.success('创建成功', `用户 "${values.userName}" 已成功创建`)
|
||||
setShowEditDrawer(false)
|
||||
fetchUsers()
|
||||
} catch (error) {
|
||||
if (error.code === 'DUPLICATE') {
|
||||
Toast.warning('用户名已存在', '请使用其他用户名')
|
||||
} else {
|
||||
Toast.error('创建失败', error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 带按钮的持久通知
|
||||
|
||||
```jsx
|
||||
const showUpdateNotification = () => {
|
||||
const key = `update-${Date.now()}`
|
||||
|
||||
Toast.custom({
|
||||
message: '发现新版本',
|
||||
description: '系统发现新版本 v2.0.0,建议立即更新',
|
||||
duration: 0, // 不自动关闭
|
||||
key,
|
||||
btn: (
|
||||
<Space>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => notification.close(key)}
|
||||
>
|
||||
稍后
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
notification.close(key)
|
||||
handleUpdate()
|
||||
}}
|
||||
>
|
||||
立即更新
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 使用场景
|
||||
|
||||
1. **操作成功反馈** - 增删改操作成功后的提示
|
||||
2. **操作失败反馈** - 操作失败时显示错误信息
|
||||
3. **警告提示** - 权限不足、数据异常等警告
|
||||
4. **信息通知** - 系统公告、功能提示等
|
||||
5. **进度通知** - 多步骤操作的进度反馈
|
||||
6. **版本更新通知** - 带操作按钮的持久通知
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 消息文案
|
||||
|
||||
```jsx
|
||||
// 好的做法:简洁明了
|
||||
Toast.success('删除成功', '用户已删除')
|
||||
|
||||
// 避免:冗余重复
|
||||
Toast.success('删除成功', '删除用户成功') // ❌
|
||||
```
|
||||
|
||||
### 显示时长
|
||||
|
||||
```jsx
|
||||
// 简单提示:3秒(默认)
|
||||
Toast.success('保存成功')
|
||||
|
||||
// 重要信息:5秒
|
||||
Toast.warning('权限不足', '请联系管理员', 5)
|
||||
|
||||
// 需要用户操作:不自动关闭
|
||||
Toast.custom({
|
||||
message: '需要您的确认',
|
||||
description: '...',
|
||||
duration: 0,
|
||||
btn: <Button>...</Button>,
|
||||
})
|
||||
```
|
||||
|
||||
### 类型选择
|
||||
|
||||
```jsx
|
||||
// 成功:操作完成
|
||||
Toast.success('保存成功')
|
||||
|
||||
// 错误:操作失败、异常
|
||||
Toast.error('保存失败', error.message)
|
||||
|
||||
// 警告:权限、数据问题
|
||||
Toast.warning('权限不足')
|
||||
|
||||
// 信息:提示、公告
|
||||
Toast.info('系统维护通知')
|
||||
```
|
||||
|
||||
## 样式特性
|
||||
|
||||
- **圆角设计**:8px 圆角,现代化视觉效果
|
||||
- **阴影效果**:0 4px 12px rgba(0, 0, 0, 0.15)
|
||||
- **彩色图标**:
|
||||
- 成功:绿色 (#52c41a)
|
||||
- 错误:红色 (#ff4d4f)
|
||||
- 警告:橙色 (#faad14)
|
||||
- 信息:蓝色 (#1677ff)
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 通知从右上角滑出,默认 3 秒后自动消失
|
||||
2. 最多同时显示 3 条通知,超出的会排队等待
|
||||
3. `duration: 0` 表示通知不自动关闭,需手动关闭或调用 API 关闭
|
||||
4. 通知内容不宜过长,建议 message 不超过 20 字,description 不超过 50 字
|
||||
5. 频繁操作时避免连续显示大量通知,可以考虑合并提示
|
||||
6. 配合 ConfirmDialog 使用时,在 `onOk` 回调中显示操作结果
|
||||
7. 错误通知建议显示具体错误信息,帮助用户排查问题
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
# TreeFilterPanel 组件
|
||||
|
||||
## 组件说明
|
||||
|
||||
树形筛选面板组件,用于在弹出层中展示树形结构的筛选选项,支持树形选择、搜索、确认和清除操作。常配合 ListActionBar 组件使用。
|
||||
|
||||
## 组件位置
|
||||
|
||||
```
|
||||
src/components/TreeFilterPanel/TreeFilterPanel.jsx
|
||||
src/components/TreeFilterPanel/TreeFilterPanel.css
|
||||
```
|
||||
|
||||
## 参数说明
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| treeData | Array<TreeNode> | 是 | - | 树形数据数组 |
|
||||
| selectedKey | string | 否 | - | 当前选中的节点 key(已确认的选择) |
|
||||
| tempSelectedKey | string | 否 | - | 临时选中的节点 key(未确认) |
|
||||
| treeTitle | string | 否 | '分组筛选' | 树形选择器的标题 |
|
||||
| onSelect | function(key: string) | 否 | - | 节点选择变化回调 |
|
||||
| onConfirm | function | 否 | - | 确认筛选按钮回调 |
|
||||
| onClear | function | 否 | - | 清除筛选按钮回调 |
|
||||
| placeholder | string | 否 | '请选择分组进行筛选' | 未选择时的占位提示文本 |
|
||||
|
||||
### TreeNode 数据结构
|
||||
|
||||
| 属性名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| key | string | 是 | 节点唯一标识 |
|
||||
| title | string | 是 | 节点显示文本 |
|
||||
| children | Array<TreeNode> | 否 | 子节点数组 |
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础用法
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react'
|
||||
import TreeFilterPanel from '../components/TreeFilterPanel/TreeFilterPanel'
|
||||
|
||||
function MyPage() {
|
||||
const [selectedGroup, setSelectedGroup] = useState(null)
|
||||
const [tempSelectedGroup, setTempSelectedGroup] = useState(null)
|
||||
|
||||
const treeData = [
|
||||
{
|
||||
key: '1',
|
||||
title: '全部用户',
|
||||
children: [
|
||||
{
|
||||
key: '1-1',
|
||||
title: '管理员组',
|
||||
children: [
|
||||
{ key: '1-1-1', title: '系统管理员' },
|
||||
{ key: '1-1-2', title: '安全管理员' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '1-2',
|
||||
title: '部门用户',
|
||||
children: [
|
||||
{ key: '1-2-1', title: '研发部' },
|
||||
{ key: '1-2-2', title: '产品部' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const handleConfirm = () => {
|
||||
setSelectedGroup(tempSelectedGroup)
|
||||
// 执行筛选逻辑
|
||||
filterData(tempSelectedGroup)
|
||||
}
|
||||
|
||||
const handleClear = () => {
|
||||
setTempSelectedGroup(null)
|
||||
setSelectedGroup(null)
|
||||
// 清除筛选
|
||||
filterData(null)
|
||||
}
|
||||
|
||||
return (
|
||||
<TreeFilterPanel
|
||||
treeData={treeData}
|
||||
selectedKey={selectedGroup}
|
||||
tempSelectedKey={tempSelectedGroup}
|
||||
treeTitle="用户分组"
|
||||
onSelect={setTempSelectedGroup}
|
||||
onConfirm={handleConfirm}
|
||||
onClear={handleClear}
|
||||
placeholder="请选择用户分组进行筛选"
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 配合 ListActionBar 使用
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react'
|
||||
import ListActionBar from '../components/ListActionBar/ListActionBar'
|
||||
import TreeFilterPanel from '../components/TreeFilterPanel/TreeFilterPanel'
|
||||
|
||||
function UserListPage() {
|
||||
const [showFilterPopover, setShowFilterPopover] = useState(false)
|
||||
const [selectedGroup, setSelectedGroup] = useState(null)
|
||||
const [tempSelectedGroup, setTempSelectedGroup] = useState(null)
|
||||
const [selectedGroupName, setSelectedGroupName] = useState('')
|
||||
|
||||
// 将原始数据转换为树形数据格式
|
||||
const convertTreeData = (nodes) => {
|
||||
return nodes.map((node) => ({
|
||||
title: node.name,
|
||||
key: node.id,
|
||||
children: node.children ? convertTreeData(node.children) : undefined,
|
||||
}))
|
||||
}
|
||||
|
||||
const treeData = convertTreeData(userData.userGroups)
|
||||
|
||||
const handleConfirmFilter = () => {
|
||||
setSelectedGroup(tempSelectedGroup)
|
||||
|
||||
// 查找节点名称
|
||||
const findGroupName = (nodes, id) => {
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) return node.name
|
||||
if (node.children) {
|
||||
const found = findGroupName(node.children, id)
|
||||
if (found) return found
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const groupName = tempSelectedGroup
|
||||
? findGroupName(userData.userGroups, tempSelectedGroup)
|
||||
: ''
|
||||
setSelectedGroupName(groupName)
|
||||
|
||||
// 执行筛选
|
||||
filterUsers(searchKeyword, tempSelectedGroup)
|
||||
setShowFilterPopover(false)
|
||||
}
|
||||
|
||||
const handleClearFilter = () => {
|
||||
setTempSelectedGroup(null)
|
||||
setSelectedGroup(null)
|
||||
setSelectedGroupName('')
|
||||
filterUsers(searchKeyword, null)
|
||||
setShowFilterPopover(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<ListActionBar
|
||||
actions={[
|
||||
{
|
||||
key: 'add',
|
||||
label: '新增用户',
|
||||
type: 'primary',
|
||||
onClick: handleAdd,
|
||||
},
|
||||
]}
|
||||
search={{
|
||||
placeholder: '搜索用户',
|
||||
onSearch: handleSearch,
|
||||
}}
|
||||
filter={{
|
||||
content: (
|
||||
<TreeFilterPanel
|
||||
treeData={treeData}
|
||||
selectedKey={selectedGroup}
|
||||
tempSelectedKey={tempSelectedGroup}
|
||||
treeTitle="用户分组"
|
||||
onSelect={setTempSelectedGroup}
|
||||
onConfirm={handleConfirmFilter}
|
||||
onClear={handleClearFilter}
|
||||
placeholder="请选择用户分组进行筛选"
|
||||
/>
|
||||
),
|
||||
title: '高级筛选',
|
||||
visible: showFilterPopover,
|
||||
onVisibleChange: (visible) => {
|
||||
setShowFilterPopover(visible)
|
||||
if (visible) {
|
||||
// 打开时同步临时选择
|
||||
setTempSelectedGroup(selectedGroup)
|
||||
}
|
||||
},
|
||||
selectedLabel: selectedGroupName,
|
||||
isActive: !!selectedGroup,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 从 JSON 数据转换树形结构
|
||||
|
||||
```jsx
|
||||
// userData.json 中的数据格式
|
||||
{
|
||||
"userGroups": [
|
||||
{
|
||||
"id": "1",
|
||||
"name": "全部用户",
|
||||
"children": [
|
||||
{
|
||||
"id": "1-1",
|
||||
"name": "管理员组",
|
||||
"children": [
|
||||
{ "id": "1-1-1", "name": "系统管理员" },
|
||||
{ "id": "1-1-2", "name": "安全管理员" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 转换函数
|
||||
const convertTreeData = (nodes) => {
|
||||
return nodes.map((node) => ({
|
||||
title: node.name, // 映射 name 到 title
|
||||
key: node.id, // 映射 id 到 key
|
||||
children: node.children ? convertTreeData(node.children) : undefined,
|
||||
}))
|
||||
}
|
||||
|
||||
const treeData = convertTreeData(userData.userGroups)
|
||||
```
|
||||
|
||||
## 组件特性
|
||||
|
||||
### 自动展开所有节点
|
||||
|
||||
组件会在数据加载时自动展开所有树节点,方便用户查看完整的层级结构。
|
||||
|
||||
```javascript
|
||||
// 组件内部实现
|
||||
useEffect(() => {
|
||||
if (treeData && treeData.length > 0) {
|
||||
setExpandedKeys(getAllKeys(treeData))
|
||||
}
|
||||
}, [treeData])
|
||||
```
|
||||
|
||||
### 显示当前选择
|
||||
|
||||
组件顶部会显示当前选中的节点,并提供关闭按钮快速清除选择:
|
||||
|
||||
```jsx
|
||||
{tempSelectedKey ? (
|
||||
<div className="tree-filter-tag">
|
||||
<span className="tree-filter-label">已选择分组:</span>
|
||||
<Tag color="blue" closable onClose={() => onSelect?.(null)}>
|
||||
{findNodeName(treeData, tempSelectedKey)}
|
||||
</Tag>
|
||||
</div>
|
||||
) : (
|
||||
<div className="tree-filter-placeholder">
|
||||
<span>{placeholder}</span>
|
||||
</div>
|
||||
)}
|
||||
```
|
||||
|
||||
## 样式定制
|
||||
|
||||
组件提供以下 CSS 类名供自定义样式:
|
||||
|
||||
- `.tree-filter-panel` - 组件根容器
|
||||
- `.tree-filter-selected` - 已选择区域
|
||||
- `.tree-filter-tag` - 选中标签容器
|
||||
- `.tree-filter-label` - 标签文本
|
||||
- `.tree-filter-placeholder` - 占位提示
|
||||
- `.tree-filter-container` - 树形选择器容器
|
||||
- `.tree-filter-header` - 树形选择器标题
|
||||
- `.tree-filter-actions` - 操作按钮区域
|
||||
|
||||
## 使用场景
|
||||
|
||||
1. **用户分组筛选** - 按用户组织架构筛选用户列表
|
||||
2. **终端分组筛选** - 按终端分组筛选设备列表
|
||||
3. **部门筛选** - 按部门层级筛选数据
|
||||
4. **分类筛选** - 任何需要树形层级筛选的场景
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. `treeData` 必须符合 Ant Design Tree 组件的数据格式要求
|
||||
2. 需要区分 `selectedKey`(已确认)和 `tempSelectedKey`(临时选择),点击确认后才更新 `selectedKey`
|
||||
3. 建议在打开筛选面板时,将 `selectedKey` 同步到 `tempSelectedKey`,以便用户看到当前的选择状态
|
||||
4. 点击清除按钮会同时清除临时选择和已确认选择
|
||||
5. 组件会自动展开所有节点,如果数据量很大,可能需要考虑性能优化
|
||||
6. 树节点的 `key` 必须唯一,通常使用 ID 字段
|
||||
|
|
@ -0,0 +1,495 @@
|
|||
# 主框架页面设计规范
|
||||
|
||||
> Nex Design 主框架布局设计文档
|
||||
|
||||
## 概述
|
||||
|
||||
主框架页面是应用的基础布局结构,包含侧边菜单栏、顶部导航栏和内容区域。本文档详细说明了主框架的设计规范、交互逻辑和实现要点。
|
||||
|
||||
---
|
||||
|
||||
## 页面结构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ 顶部导航栏 (64px) │
|
||||
├──────────┬──────────────────────────────────────────┤
|
||||
│ │ │
|
||||
│ 侧边栏 │ 内容区域 │
|
||||
│ (200px) │ (可向下滚动) │
|
||||
│ │ │
|
||||
│ │ │
|
||||
└──────────┴──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. 侧边栏 (Sider)
|
||||
|
||||
### 1.1 基础规范
|
||||
|
||||
| 属性 | 展开状态 | 收起状态 |
|
||||
|------|---------|---------|
|
||||
| 宽度 | 200px | 64px |
|
||||
| 背景色 | #001529 (深色) | #001529 |
|
||||
| 位置 | 固定左侧 | 固定左侧 |
|
||||
| 层级 | z-index: 10 | z-index: 10 |
|
||||
|
||||
### 1.2 Logo 区域
|
||||
|
||||
**设计规范**:
|
||||
- 高度:64px
|
||||
- 背景:rgba(255, 255, 255, 0.05)
|
||||
- 底部边框:1px solid rgba(255, 255, 255, 0.05)
|
||||
- 内边距:12px 16px
|
||||
- 对齐:居中
|
||||
|
||||
**展开状态**:
|
||||
- 显示完整 Logo(logo-full.png)
|
||||
- Logo 高度:40px,宽度自适应
|
||||
|
||||
**收起状态**:
|
||||
- 显示方形 Logo(logo-small.png)
|
||||
- Logo 尺寸:40px × 40px
|
||||
- 圆角:8px
|
||||
|
||||
### 1.3 菜单系统
|
||||
|
||||
#### 菜单层级
|
||||
|
||||
支持**两级菜单**结构:
|
||||
- **一级菜单**:带图标,可展开/收起
|
||||
- **二级菜单**:文字列表,可带徽章标识
|
||||
|
||||
#### 菜单数据格式
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"key": "overview",
|
||||
"label": "概览",
|
||||
"icon": "DashboardOutlined",
|
||||
"path": "/overview"
|
||||
},
|
||||
{
|
||||
"key": "certificate",
|
||||
"label": "证书管理",
|
||||
"icon": "SafetyCertificateOutlined",
|
||||
"children": [
|
||||
{
|
||||
"key": "ssl-cert",
|
||||
"label": "SSL证书管理",
|
||||
"path": "/certificate/ssl"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### 菜单状态
|
||||
|
||||
**正常状态**:
|
||||
- 背景色:透明
|
||||
- 文字颜色:rgba(255, 255, 255, 0.65)
|
||||
|
||||
**悬停状态**:
|
||||
- 背景色:rgba(184, 23, 141, 0.2)
|
||||
- 文字颜色:#fff
|
||||
|
||||
**选中状态**:
|
||||
- 背景色:#b8178d (品牌主色)
|
||||
- 文字颜色:#fff
|
||||
- 左侧边框:3px solid #b8178d
|
||||
|
||||
**展开状态**:
|
||||
- 二级菜单背景:rgba(0, 0, 0, 0.15)
|
||||
- 二级菜单内边距:左侧 48px
|
||||
|
||||
#### 徽章系统
|
||||
|
||||
支持在菜单项上显示徽章标识:
|
||||
|
||||
- **HOT 徽章**:
|
||||
- 背景色:#ff4d4f (红色)
|
||||
- 文字:白色
|
||||
- 尺寸:18px 高度,圆角 9px
|
||||
|
||||
- **NEW 徽章**:
|
||||
- 背景色:#52c41a (绿色)
|
||||
- 文字:白色
|
||||
- 尺寸:18px 高度,圆角 9px
|
||||
|
||||
**注意**:收起状态下徽章自动隐藏。
|
||||
|
||||
#### 收起状态行为
|
||||
|
||||
- 仅显示一级菜单图标
|
||||
- 鼠标悬停时不展开子菜单
|
||||
- 点击跳转到该分类的默认页面
|
||||
|
||||
### 1.4 滚动条样式
|
||||
|
||||
```css
|
||||
宽度:6px
|
||||
轨道:透明
|
||||
滑块:rgba(255, 255, 255, 0.2)
|
||||
滑块悬停:rgba(255, 255, 255, 0.3)
|
||||
圆角:3px
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 顶部导航栏 (Header)
|
||||
|
||||
### 2.1 基础规范
|
||||
|
||||
| 属性 | 值 |
|
||||
|------|---|
|
||||
| 高度 | 64px |
|
||||
| 背景色 | #ffffff |
|
||||
| 阴影 | 0 1px 4px rgba(0, 21, 41, 0.08) |
|
||||
| 位置 | sticky,top: 0 |
|
||||
| 层级 | z-index: 9 |
|
||||
| 内边距 | 0 24px |
|
||||
|
||||
### 2.2 左侧区域
|
||||
|
||||
**折叠按钮**:
|
||||
- 图标尺寸:18px
|
||||
- 颜色:rgba(0, 0, 0, 0.65)
|
||||
- 悬停色:#b8178d (品牌主色)
|
||||
- 悬停背景:rgba(0, 0, 0, 0.03)
|
||||
- 内边距:8px
|
||||
- 圆角:4px
|
||||
- 功能:切换侧边栏展开/收起状态
|
||||
|
||||
**工作台标识**:
|
||||
- 字号:14px
|
||||
- 字重:500 (Medium)
|
||||
- 颜色:rgba(0, 0, 0, 0.88)
|
||||
- 左侧间距:16px
|
||||
|
||||
### 2.3 右侧区域
|
||||
|
||||
从左到右依次包含:
|
||||
|
||||
1. **搜索框**
|
||||
- 宽度:200px
|
||||
- 高度:32px
|
||||
- 圆角:16px (胶囊形)
|
||||
- 占位文字:"搜索..."
|
||||
- 前缀图标:SearchOutlined
|
||||
|
||||
2. **帮助图标**
|
||||
- 图标:QuestionCircleOutlined
|
||||
- 尺寸:16px
|
||||
- 颜色:rgba(0, 0, 0, 0.65)
|
||||
- 悬停色:#b8178d
|
||||
|
||||
3. **功能链接**(ICP 备案、企业、支持)
|
||||
- 字号:14px
|
||||
- 颜色:rgba(0, 0, 0, 0.65)
|
||||
- 悬停色:#b8178d
|
||||
- 悬停背景:rgba(0, 0, 0, 0.03)
|
||||
- 内边距:4px 8px
|
||||
- 圆角:4px
|
||||
|
||||
4. **工单图标**
|
||||
- 图标:SettingOutlined
|
||||
- 样式同帮助图标
|
||||
|
||||
5. **消息中心**
|
||||
- 图标:BellOutlined
|
||||
- 带徽章:Badge count={5}
|
||||
- 徽章位置:右上角,offset: [-3, 3]
|
||||
- 徽章尺寸:small
|
||||
|
||||
6. **用户信息**
|
||||
- 头像:32px × 32px 圆形
|
||||
- 用户名:14px,Medium 字重
|
||||
- 颜色:rgba(0, 0, 0, 0.88)
|
||||
- 整体内边距:4px 8px
|
||||
- 悬停背景:rgba(0, 0, 0, 0.03)
|
||||
- 点击显示下拉菜单
|
||||
|
||||
**间距**:各元素之间间距 16-20px
|
||||
|
||||
---
|
||||
|
||||
## 3. 内容区域 (Content)
|
||||
|
||||
### 3.1 基础规范
|
||||
|
||||
| 属性 | 值 |
|
||||
|------|---|
|
||||
| 背景色 | #f5f5f5 |
|
||||
| 内边距 | 24px |
|
||||
| 高度 | calc(100vh - 64px) |
|
||||
| 滚动 | overflow-y: auto |
|
||||
|
||||
### 3.2 内容容器
|
||||
|
||||
- 最大宽度:根据业务需求,建议 1200-1600px
|
||||
- 内边距:24px
|
||||
- 背景色:根据内容类型,卡片为 #fff
|
||||
|
||||
### 3.3 滚动行为
|
||||
|
||||
- 仅内容区域可滚动
|
||||
- 顶部导航栏和侧边栏保持固定
|
||||
- 滚动条样式与全局一致
|
||||
|
||||
---
|
||||
|
||||
## 4. 响应式适配
|
||||
|
||||
### 4.1 断点规则
|
||||
|
||||
| 断点 | 行为 |
|
||||
|------|------|
|
||||
| < 768px | 侧边栏默认收起,通过遮罩层展开 |
|
||||
| ≥ 768px | 侧边栏可正常展开/收起 |
|
||||
| ≥ 1200px | 建议默认展开侧边栏 |
|
||||
|
||||
### 4.2 移动端优化
|
||||
|
||||
- 侧边栏改为抽屉模式 (Drawer)
|
||||
- 顶部搜索框宽度减小或移至下拉菜单
|
||||
- 功能链接收起至"更多"菜单
|
||||
- 用户信息简化显示
|
||||
|
||||
---
|
||||
|
||||
## 5. 主题配色
|
||||
|
||||
### 5.1 品牌主色
|
||||
|
||||
基于 NEX Logo 的紫红色:
|
||||
|
||||
```css
|
||||
--primary-color: #b8178d;
|
||||
--primary-hover: #9c1477;
|
||||
--primary-active: #801161;
|
||||
```
|
||||
|
||||
### 5.2 辅助色
|
||||
|
||||
- **蓝色**(信息色):#1677ff
|
||||
- **绿色**(成功):#52c41a
|
||||
- **红色**(错误/警告):#ff4d4f
|
||||
- **橙色**(警告):#faad14
|
||||
|
||||
### 5.3 中性色
|
||||
|
||||
```css
|
||||
--text-primary: rgba(0, 0, 0, 0.88);
|
||||
--text-secondary: rgba(0, 0, 0, 0.65);
|
||||
--text-tertiary: rgba(0, 0, 0, 0.45);
|
||||
--bg-primary: #ffffff;
|
||||
--bg-secondary: #fafafa;
|
||||
--bg-tertiary: #f5f5f5;
|
||||
--border-color: #d9d9d9;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 交互规范
|
||||
|
||||
### 6.1 侧边栏折叠
|
||||
|
||||
**触发方式**:
|
||||
- 点击顶部折叠按钮
|
||||
- 可选:在设置中保存用户偏好
|
||||
|
||||
**动画**:
|
||||
- 过渡时间:200ms
|
||||
- 缓动函数:ease-in-out
|
||||
- 影响元素:侧边栏宽度、Logo、菜单文字
|
||||
|
||||
**状态保持**:
|
||||
- 使用 localStorage 保存用户折叠状态
|
||||
- 页面刷新后保持用户选择
|
||||
|
||||
### 6.2 菜单导航
|
||||
|
||||
**展开逻辑**:
|
||||
- 点击一级菜单展开/收起二级菜单
|
||||
- 默认展开当前路由所在的菜单组
|
||||
- 支持手风琴模式(可选)
|
||||
|
||||
**高亮逻辑**:
|
||||
- 根据当前路由自动高亮对应菜单项
|
||||
- 二级菜单选中时,一级菜单也显示激活状态
|
||||
|
||||
**跳转方式**:
|
||||
- 使用 React Router 进行路由跳转
|
||||
- 支持浏览器前进/后退
|
||||
|
||||
### 6.3 用户下拉菜单
|
||||
|
||||
**菜单项**:
|
||||
- 个人中心
|
||||
- 账户设置
|
||||
- 分割线
|
||||
- 退出登录
|
||||
|
||||
**交互**:
|
||||
- 点击用户信息区域展开
|
||||
- 点击菜单项执行对应操作
|
||||
- 点击外部区域关闭
|
||||
|
||||
---
|
||||
|
||||
## 7. 代码实现
|
||||
|
||||
### 7.1 组件结构
|
||||
|
||||
```
|
||||
MainLayout/
|
||||
├── MainLayout.jsx # 主布局组件
|
||||
├── MainLayout.css # 布局样式
|
||||
├── AppSider.jsx # 侧边栏组件
|
||||
├── AppSider.css # 侧边栏样式
|
||||
├── AppHeader.jsx # 顶部栏组件
|
||||
├── AppHeader.css # 顶部栏样式
|
||||
└── index.js # 导出文件
|
||||
```
|
||||
|
||||
### 7.2 菜单数据配置
|
||||
|
||||
菜单数据独立维护在 `src/constants/menuData.json`,便于更新和管理。
|
||||
|
||||
### 7.3 关键技术点
|
||||
|
||||
1. **状态管理**:
|
||||
- collapsed 状态通过 props 传递
|
||||
- 菜单展开状态 (openKeys) 在 AppSider 内部管理
|
||||
|
||||
2. **路由集成**:
|
||||
- 使用 useNavigate 进行路由跳转
|
||||
- 使用 useLocation 获取当前路由
|
||||
|
||||
3. **图标映射**:
|
||||
- 通过 iconMap 对象将字符串转换为图标组件
|
||||
|
||||
4. **主题定制**:
|
||||
- 在 src/main.jsx 中配置 Ant Design 主题
|
||||
- 使用 ConfigProvider 包裹应用
|
||||
|
||||
---
|
||||
|
||||
## 8. 示例页面
|
||||
|
||||
### 8.1 概览页 (Overview)
|
||||
|
||||
作为主框架的示例页面,展示了:
|
||||
- 统计卡片布局
|
||||
- 图表展示
|
||||
- 数据可视化
|
||||
- 响应式栅格系统
|
||||
|
||||
详细设计见:[概览页设计文档](./overview.md)
|
||||
|
||||
---
|
||||
|
||||
## 9. 可访问性
|
||||
|
||||
### 9.1 键盘导航
|
||||
|
||||
- 支持 Tab 键在可交互元素间切换
|
||||
- 支持 Enter 键激活菜单项
|
||||
- 支持方向键在菜单间导航
|
||||
|
||||
### 9.2 语义化标签
|
||||
|
||||
- 使用 nav 标签包裹导航菜单
|
||||
- 使用 header 标签包裹顶部栏
|
||||
- 使用 main 标签包裹主内容区
|
||||
|
||||
### 9.3 对比度
|
||||
|
||||
- 所有文本与背景对比度 ≥ 4.5:1
|
||||
- 图标与背景对比度 ≥ 3:1
|
||||
|
||||
---
|
||||
|
||||
## 10. 性能优化
|
||||
|
||||
### 10.1 懒加载
|
||||
|
||||
- 页面组件使用 React.lazy 懒加载
|
||||
- 减少首屏加载时间
|
||||
|
||||
### 10.2 防抖优化
|
||||
|
||||
- 搜索框输入使用防抖处理
|
||||
- 窗口大小变化使用节流处理
|
||||
|
||||
### 10.3 虚拟滚动
|
||||
|
||||
- 菜单项较多时考虑虚拟滚动
|
||||
- 长列表使用虚拟化技术
|
||||
|
||||
---
|
||||
|
||||
## 11. 开发指南
|
||||
|
||||
### 11.1 添加新菜单
|
||||
|
||||
1. 编辑 `src/constants/menuData.json`
|
||||
2. 添加菜单项配置
|
||||
3. 如需新图标,在 `AppSider.jsx` 的 iconMap 中添加映射
|
||||
4. 创建对应的页面组件
|
||||
5. 在 `App.jsx` 中添加路由
|
||||
|
||||
### 11.2 自定义主题
|
||||
|
||||
1. 编辑 `src/main.jsx` 中的 theme 配置
|
||||
2. 修改 `tailwind.config.js` 中的颜色系统
|
||||
3. 更新 `src/styles/globals.css` 中的 CSS 变量
|
||||
|
||||
### 11.3 扩展功能
|
||||
|
||||
- 添加面包屑导航
|
||||
- 添加页签 (Tabs) 功能
|
||||
- 添加全局设置抽屉
|
||||
- 添加主题切换(亮色/暗色)
|
||||
|
||||
---
|
||||
|
||||
## 12. 常见问题
|
||||
|
||||
### Q1: 如何修改侧边栏默认展开状态?
|
||||
|
||||
在 `MainLayout.jsx` 中修改 `collapsed` 的初始值:
|
||||
|
||||
```jsx
|
||||
const [collapsed, setCollapsed] = useState(false) // false 为展开
|
||||
```
|
||||
|
||||
### Q2: 如何添加三级菜单?
|
||||
|
||||
当前设计仅支持两级菜单。如需三级菜单,需要:
|
||||
1. 修改 menuData.json 数据结构
|
||||
2. 修改 AppSider.jsx 中的 getMenuItems 函数
|
||||
3. 考虑 UI 空间和用户体验
|
||||
|
||||
### Q3: 如何实现菜单权限控制?
|
||||
|
||||
建议:
|
||||
1. 在菜单数据中添加 `roles` 或 `permissions` 字段
|
||||
2. 在渲染菜单前根据用户权限过滤
|
||||
3. 在路由层面也要做权限校验
|
||||
|
||||
---
|
||||
|
||||
## 版本记录
|
||||
|
||||
| 版本 | 日期 | 说明 |
|
||||
|------|------|------|
|
||||
| 1.0.0 | 2024-11-04 | 初始版本,完成主框架设计 |
|
||||
|
||||
---
|
||||
|
||||
**维护者**: Nex Design Team
|
||||
**最后更新**: 2024-11-04
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# 告诉EditorConfig插件,这是根文件,不用继续往上查找
|
||||
root = true
|
||||
|
||||
# 匹配全部文件
|
||||
[*]
|
||||
# 设置字符集
|
||||
charset = utf-8
|
||||
# 缩进风格,可选space、tab
|
||||
indent_style = space
|
||||
# 缩进的空格数
|
||||
indent_size = 2
|
||||
# 结尾换行符,可选lf、cr、crlf
|
||||
end_of_line = lf
|
||||
# 在文件结尾插入新行
|
||||
insert_final_newline = true
|
||||
# 删除一行中的前后空格
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# 匹配md结尾的文件
|
||||
[*.md]
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = false
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# 页面标题
|
||||
VUE_APP_TITLE = UNISSENSE-OMS
|
||||
|
||||
# 开发环境配置
|
||||
ENV = 'development'
|
||||
|
||||
# 若依管理系统/开发环境
|
||||
VUE_APP_BASE_API = '/dev-api'
|
||||
|
||||
# 路由懒加载
|
||||
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# 页面标题
|
||||
VUE_APP_TITLE = UNISSENSE-OMS
|
||||
|
||||
# 生产环境配置
|
||||
ENV = 'production'
|
||||
|
||||
# 若依管理系统/生产环境
|
||||
VUE_APP_BASE_API = '/prod-api'
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# 页面标题
|
||||
VUE_APP_TITLE = UNISSENSE-OMS
|
||||
|
||||
BABEL_ENV = production
|
||||
|
||||
NODE_ENV = production
|
||||
|
||||
# 测试环境配置
|
||||
ENV = 'staging'
|
||||
|
||||
# 若依管理系统/测试环境
|
||||
VUE_APP_BASE_API = '/stage-api'
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
**/*.log
|
||||
|
||||
tests/**/coverage/
|
||||
tests/e2e/reports
|
||||
selenium-debug.log
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.local
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
## 开发
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone https://gitee.com/y_project/RuoYi-Vue
|
||||
|
||||
# 进入项目目录
|
||||
cd ruoyi-ui
|
||||
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
|
||||
npm install --registry=https://registry.npmmirror.com
|
||||
|
||||
# 启动服务
|
||||
npm run dev
|
||||
```
|
||||
|
||||
浏览器访问 http://localhost:80
|
||||
|
||||
## 发布
|
||||
|
||||
```bash
|
||||
# 构建测试环境
|
||||
npm run build:stage
|
||||
|
||||
# 构建生产环境
|
||||
npm run build:prod
|
||||
```
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
// https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
],
|
||||
'env': {
|
||||
'development': {
|
||||
// babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require().
|
||||
// This plugin can significantly increase the speed of hot updates, when you have a large number of pages.
|
||||
'plugins': ['dynamic-import-node']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
@echo off
|
||||
echo.
|
||||
echo [信息] 打包Web工程,生成dist文件。
|
||||
echo.
|
||||
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd ..
|
||||
npm run build:prod
|
||||
|
||||
pause
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
@echo off
|
||||
echo.
|
||||
echo [信息] 安装Web工程,生成node_modules文件。
|
||||
echo.
|
||||
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd ..
|
||||
npm install --registry=https://registry.npmmirror.com
|
||||
|
||||
pause
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
@echo off
|
||||
echo.
|
||||
echo [信息] 使用 Vue CLI 命令运行 Web 工程。
|
||||
echo.
|
||||
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd ..
|
||||
npm run dev
|
||||
|
||||
pause
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
const { run } = require('runjs')
|
||||
const chalk = require('chalk')
|
||||
const config = require('../vue.config.js')
|
||||
const rawArgv = process.argv.slice(2)
|
||||
const args = rawArgv.join(' ')
|
||||
|
||||
if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
|
||||
const report = rawArgv.includes('--report')
|
||||
|
||||
run(`vue-cli-service build ${args}`)
|
||||
|
||||
const port = 9526
|
||||
const publicPath = config.publicPath
|
||||
|
||||
var connect = require('connect')
|
||||
var serveStatic = require('serve-static')
|
||||
const app = connect()
|
||||
|
||||
app.use(
|
||||
publicPath,
|
||||
serveStatic('./dist', {
|
||||
index: ['index.html', '/']
|
||||
})
|
||||
)
|
||||
|
||||
app.listen(port, function () {
|
||||
console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`))
|
||||
if (report) {
|
||||
console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`))
|
||||
}
|
||||
|
||||
})
|
||||
} else {
|
||||
run(`vue-cli-service build ${args}`)
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
{
|
||||
"name": "ruoyi",
|
||||
"version": "3.9.0",
|
||||
"description": "若依管理系统",
|
||||
"author": "若依",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "vue-cli-service serve",
|
||||
"build:prod": "vue-cli-service build",
|
||||
"build:stage": "vue-cli-service build --mode staging",
|
||||
"preview": "node build/index.js --preview"
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
"admin",
|
||||
"dashboard",
|
||||
"element-ui",
|
||||
"boilerplate",
|
||||
"admin-template",
|
||||
"management-system"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gitee.com/y_project/RuoYi-Vue.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@riophae/vue-treeselect": "0.4.0",
|
||||
"axios": "0.28.1",
|
||||
"clipboard": "2.0.8",
|
||||
"core-js": "3.37.1",
|
||||
"echarts": "5.4.0",
|
||||
"element-ui": "2.15.14",
|
||||
"file-saver": "2.0.5",
|
||||
"fuse.js": "6.4.3",
|
||||
"highlight.js": "9.18.5",
|
||||
"js-beautify": "1.13.0",
|
||||
"js-cookie": "3.0.1",
|
||||
"jsencrypt": "3.0.0-rc.1",
|
||||
"nprogress": "0.2.0",
|
||||
"quill": "2.0.2",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jspdf": "^2.5.1",
|
||||
"screenfull": "5.0.2",
|
||||
"sortablejs": "1.10.2",
|
||||
"splitpanes": "2.4.1",
|
||||
"vue": "2.6.12",
|
||||
"vue-count-to": "1.0.13",
|
||||
"vue-cropper": "0.5.5",
|
||||
"vue-router": "3.4.9",
|
||||
"vuedraggable": "2.24.3",
|
||||
"vuex": "3.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "4.4.6",
|
||||
"@vue/cli-service": "4.4.6",
|
||||
"babel-plugin-dynamic-import-node": "2.3.3",
|
||||
"chalk": "4.1.0",
|
||||
"compression-webpack-plugin": "6.1.2",
|
||||
"connect": "3.6.6",
|
||||
"sass": "1.32.13",
|
||||
"sass-loader": "10.1.1",
|
||||
"script-ext-html-webpack-plugin": "2.1.5",
|
||||
"svg-sprite-loader": "5.1.1",
|
||||
"vue-template-compiler": "2.6.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9",
|
||||
"npm": ">= 3.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions"
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -0,0 +1,209 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<!-- <title><%= webpackConfig.name %></title>-->
|
||||
<title>汇智OMS订单管理系统</title>
|
||||
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
.chromeframe {
|
||||
margin: 0.2em 0;
|
||||
background: #ccc;
|
||||
color: #000;
|
||||
padding: 0.2em 0;
|
||||
}
|
||||
|
||||
#loader-wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999999;
|
||||
}
|
||||
|
||||
#loader {
|
||||
display: block;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin: -75px 0 0 -75px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #FFF;
|
||||
-webkit-animation: spin 2s linear infinite;
|
||||
-ms-animation: spin 2s linear infinite;
|
||||
-moz-animation: spin 2s linear infinite;
|
||||
-o-animation: spin 2s linear infinite;
|
||||
animation: spin 2s linear infinite;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
#loader:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #FFF;
|
||||
-webkit-animation: spin 3s linear infinite;
|
||||
-moz-animation: spin 3s linear infinite;
|
||||
-o-animation: spin 3s linear infinite;
|
||||
-ms-animation: spin 3s linear infinite;
|
||||
animation: spin 3s linear infinite;
|
||||
}
|
||||
|
||||
#loader:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
right: 15px;
|
||||
bottom: 15px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #FFF;
|
||||
-moz-animation: spin 1.5s linear infinite;
|
||||
-o-animation: spin 1.5s linear infinite;
|
||||
-ms-animation: spin 1.5s linear infinite;
|
||||
-webkit-animation: spin 1.5s linear infinite;
|
||||
animation: spin 1.5s linear infinite;
|
||||
}
|
||||
|
||||
|
||||
@-webkit-keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
-ms-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
-ms-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
-ms-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
-ms-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#loader-wrapper .loader-section {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 51%;
|
||||
height: 100%;
|
||||
background: #7171C6;
|
||||
z-index: 1000;
|
||||
-webkit-transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
#loader-wrapper .loader-section.section-left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#loader-wrapper .loader-section.section-right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
|
||||
.loaded #loader-wrapper .loader-section.section-left {
|
||||
-webkit-transform: translateX(-100%);
|
||||
-ms-transform: translateX(-100%);
|
||||
transform: translateX(-100%);
|
||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
}
|
||||
|
||||
.loaded #loader-wrapper .loader-section.section-right {
|
||||
-webkit-transform: translateX(100%);
|
||||
-ms-transform: translateX(100%);
|
||||
transform: translateX(100%);
|
||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
}
|
||||
|
||||
.loaded #loader {
|
||||
opacity: 0;
|
||||
-webkit-transition: all 0.3s ease-out;
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
|
||||
.loaded #loader-wrapper {
|
||||
visibility: hidden;
|
||||
-webkit-transform: translateY(-100%);
|
||||
-ms-transform: translateY(-100%);
|
||||
transform: translateY(-100%);
|
||||
-webkit-transition: all 0.3s 1s ease-out;
|
||||
transition: all 0.3s 1s ease-out;
|
||||
}
|
||||
|
||||
.no-js #loader-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-js h1 {
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
#loader-wrapper .load_title {
|
||||
font-family: 'Open Sans';
|
||||
color: #FFF;
|
||||
font-size: 19px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
z-index: 9999999999999;
|
||||
position: absolute;
|
||||
top: 60%;
|
||||
opacity: 1;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
#loader-wrapper .load_title span {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
font-size: 13px;
|
||||
color: #FFF;
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<div id="loader-wrapper">
|
||||
<div id="loader"></div>
|
||||
<div class="loader-section section-left"></div>
|
||||
<div class="loader-section section-right"></div>
|
||||
<div class="load_title">正在加载系统资源,请耐心等待</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
User-agent: *
|
||||
Disallow: /
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<router-view />
|
||||
<theme-picker />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ThemePicker from "@/components/ThemePicker"
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
components: { ThemePicker }
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
#app .theme-picker {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询订单列表
|
||||
export function listOrder(query) {
|
||||
return request({
|
||||
url: '/project/order/vue/approve/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 提交审批(同意或驳回)
|
||||
export function approveOrder(data) {
|
||||
return request({
|
||||
url: '/project/order/order/approve',
|
||||
method: 'post',
|
||||
data: data,
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
}
|
||||
// 查询订单管理详细信息
|
||||
export function getOrder(id) {
|
||||
return request({
|
||||
url: '/project/order/h5/approve/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import request from "@/utils/request";
|
||||
|
||||
export function listOrder(query) {
|
||||
return request({
|
||||
url: '/project/order/vue/approve/log/list',
|
||||
method: 'GET',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询制造商信息列表
|
||||
export function listVendor(query) {
|
||||
return request({
|
||||
url: '/system/vendor/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询制造商信息详细
|
||||
export function getVendor(vendorId) {
|
||||
return request({
|
||||
url: '/system/vendor/vue/' + vendorId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增制造商信息
|
||||
export function addVendor(data) {
|
||||
return request({
|
||||
url: '/system/vendor/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改制造商信息
|
||||
export function updateVendor(data) {
|
||||
return request({
|
||||
url: '/system/vendor/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除制造商信息
|
||||
export function delVendor(vendorId) {
|
||||
return request({
|
||||
url: '/system/vendor/vue/' + vendorId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出制造商信息
|
||||
export function exportVendor(query) {
|
||||
return request({
|
||||
url: '/system/vendor/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 获取所有制造商列表
|
||||
export function listAllVendor() {
|
||||
return request({
|
||||
url: '/system/vendor/vue/listAll',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 获取所有仓库列表
|
||||
export function listAllWarehouse() {
|
||||
return request({
|
||||
url: '/warehouse/info/vue/listAll',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 获取商机管理数据
|
||||
export function getProjectData(day) {
|
||||
return request({
|
||||
url: '/homepage/project',
|
||||
method: 'get',
|
||||
params: { day }
|
||||
})
|
||||
}
|
||||
|
||||
// 获取合同信息数据
|
||||
export function getOrderData(month) {
|
||||
return request({
|
||||
url: '/homepage/order',
|
||||
method: 'get',
|
||||
params: { month }
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询发货记录列表
|
||||
export function listDelivery(query) {
|
||||
return request({
|
||||
url: '/inventory/delivery/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询发货记录详细
|
||||
export function getDelivery(id) {
|
||||
return request({
|
||||
url: '/inventory/delivery/vue/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增发货记录
|
||||
export function addDelivery(data) {
|
||||
return request({
|
||||
url: '/inventory/delivery/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改发货记录
|
||||
export function updateDelivery(data) {
|
||||
return request({
|
||||
url: '/inventory/delivery/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除发货记录
|
||||
export function delDelivery(id) {
|
||||
return request({
|
||||
url: '/inventory/delivery/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 撤回发货记录
|
||||
export function recallDelivery(id) {
|
||||
return request({
|
||||
url: '/inventory/delivery/vue/recall',
|
||||
method: 'post',
|
||||
data: {id}
|
||||
})
|
||||
}
|
||||
|
||||
// 导出采购合同
|
||||
export function exportDelivery(query) {
|
||||
return request({
|
||||
url: '/inventory/delivery/vue/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 导出采购合同详情
|
||||
export function exportDeliveryDetail(id) {
|
||||
return request({
|
||||
url: '/inventory/delivery/vue/detail/export',
|
||||
method: 'post',
|
||||
data: {id}
|
||||
})
|
||||
}
|
||||
|
||||
// 查询关联的SN码列表
|
||||
export function listProductSn(query) {
|
||||
return request({
|
||||
url: '/inventory/info/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function removeDelivery(id) {
|
||||
return request({
|
||||
url: '/inventory/delivery/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function updateDeliveryStatus(data) {
|
||||
return request({
|
||||
url: '/inventory/delivery/vue/status' ,
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询订单执行列表
|
||||
export function listExecution(query) {
|
||||
return request({
|
||||
url: '/inventory/execution/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询订单执行详细
|
||||
export function getExecution(id) {
|
||||
return request({
|
||||
url: '/inventory/execution/vue/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增订单执行
|
||||
export function addExecution(data) {
|
||||
return request({
|
||||
url: '/inventory/execution/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改订单执行
|
||||
export function updateExecution(data) {
|
||||
return request({
|
||||
url: '/inventory/execution/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除订单执行
|
||||
export function delExecution(id) {
|
||||
return request({
|
||||
url: '/inventory/execution/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 订单执行签收
|
||||
export function signExecution(data) {
|
||||
return request({
|
||||
url: '/inventory/execution/vue/sign',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 下载签收文件
|
||||
export function downloadSignFile(orderId, versionCode) {
|
||||
return request({
|
||||
url: `/inventory/execution/vue/sign/download`,
|
||||
method: 'get',
|
||||
params: { orderId, versionCode },
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
// 撤单
|
||||
export function recallExecution(id) {
|
||||
return request({
|
||||
url: `/inventory/execution/vue/recall`,
|
||||
method: 'post',
|
||||
data: { id }
|
||||
})
|
||||
}
|
||||
|
||||
// 查询出库预览信息
|
||||
export function getCheckOutPreview(data) {
|
||||
return request({
|
||||
url: '/inventory/execution/vue/checkOut/preview',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 新增出库单
|
||||
export function addOuter(data) {
|
||||
return request({
|
||||
url: '/inventory/outer/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 撤销出库单
|
||||
export function removeOuter(id) {
|
||||
return request({
|
||||
url: '/inventory/outer/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 确认出库
|
||||
export function confirmOuterStatus(id, orderCode) {
|
||||
return request({
|
||||
url: '/inventory/outer/vue/status',
|
||||
method: 'post',
|
||||
data: { id, outerStatus: '2', orderCode }
|
||||
})
|
||||
}
|
||||
// 确认出库
|
||||
export function getOuter(id) {
|
||||
return request({
|
||||
url: '/inventory/outer/vue/view/'+id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询产品库存列表
|
||||
export function listInfo(query) {
|
||||
return request({
|
||||
url: '/inventory/info/vue/group/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询产品库存详细
|
||||
export function getInfo(id) {
|
||||
return request({
|
||||
url: '/inventory/info/vue/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增产品库存
|
||||
export function addInfo(data) {
|
||||
return request({
|
||||
url: '/inventory/info/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改产品库存
|
||||
export function updateInfo(data) {
|
||||
return request({
|
||||
url: '/inventory/info/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除产品库存
|
||||
export function delInfo(id) {
|
||||
return request({
|
||||
url: '/inventory/info/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出产品库存
|
||||
export function exportInfo(query) {
|
||||
return request({
|
||||
url: '/inventory/info/vue/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 获取所有制造商列表
|
||||
export function listAllVendors() {
|
||||
return request({
|
||||
url: '/inventory/info/vue/listAllVendors',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取所有正常状态的仓库列表
|
||||
export function listAllWarehouses() {
|
||||
return request({
|
||||
url: '/inventory/info/vue/listAllWarehouses',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询入库日志
|
||||
export function getInnerLog(query) {
|
||||
return request({
|
||||
url: '/inventory/info/vue/inner-log',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询出库日志
|
||||
export function getOuterLog(query) {
|
||||
return request({
|
||||
url: '/inventory/info/vue/outer-log',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
const VUE_APP_API_URL = '/inventory/inner/vue';
|
||||
|
||||
// 查询入库单信息列表
|
||||
export function listInner(query) {
|
||||
return request({
|
||||
url: `${VUE_APP_API_URL}/list`,
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询入库单信息详细
|
||||
export function getInner(id) {
|
||||
return request({
|
||||
url: `${VUE_APP_API_URL}/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增入库单信息
|
||||
export function addInner(data) {
|
||||
return request({
|
||||
url: VUE_APP_API_URL,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改入库单信息
|
||||
export function updateInner(data) {
|
||||
return request({
|
||||
url: VUE_APP_API_URL,
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除入库单信息
|
||||
export function delInner(id) {
|
||||
return request({
|
||||
url: `${VUE_APP_API_URL}/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出入库单信息
|
||||
export function exportInner(query) {
|
||||
return request({
|
||||
url: `${VUE_APP_API_URL}/export`,
|
||||
method: 'post',
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 导入产品数据
|
||||
export function importProductData(data) {
|
||||
return request({
|
||||
url: `${VUE_APP_API_URL}/importData`,
|
||||
method: 'post',
|
||||
data: data
|
||||
// Content-Type 会被自动设置为 multipart/form-data
|
||||
})
|
||||
}
|
||||
|
||||
// 下载导入模板
|
||||
export function downloadTemplate() {
|
||||
return request({
|
||||
url: `${VUE_APP_API_URL}/export/template`,
|
||||
method: 'post',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询出库单列表
|
||||
export function listOuter(query) {
|
||||
return request({
|
||||
url: '/inventory/outer/vue',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询出库单详细
|
||||
export function getOuter(id) {
|
||||
return request({
|
||||
url: '/inventory/outer/vue/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增出库单
|
||||
export function addOuter(data) {
|
||||
return request({
|
||||
url: '/inventory/outer/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改出库单
|
||||
export function updateOuter(data) {
|
||||
return request({
|
||||
url: '/inventory/outer/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除出库单
|
||||
export function delOuter(id) {
|
||||
return request({
|
||||
url: '/inventory/outer/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出出库单
|
||||
export function exportOuter(query) {
|
||||
return request({
|
||||
url: '/inventory/outer/vue/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 修改出库单状态
|
||||
export function changeOuterStatus(id, outerStatus, orderCode) {
|
||||
const data = {
|
||||
id,
|
||||
outerStatus,
|
||||
orderCode
|
||||
}
|
||||
return request({
|
||||
url: '/inventory/outer/vue/status',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询出库单详细(用于刷新产品和发货列表)
|
||||
export function queryInfo(id) {
|
||||
return request({
|
||||
url: '/inventory/outer/vue/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
export function importSnData(formData) {
|
||||
return request({
|
||||
url: '/inventory/outer/vue/importData',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data: formData
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询备货信息列表
|
||||
export function listStock(query) {
|
||||
return request({
|
||||
url: '/stock/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 获取备货信息详细信息
|
||||
export function getStock(id) {
|
||||
return request({
|
||||
url: '/stock/vue/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增备货信息
|
||||
export function addStock(data) {
|
||||
return request({
|
||||
url: '/stock/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改备货信息
|
||||
export function updateStock(data) {
|
||||
return request({
|
||||
url: '/stock/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除备货信息
|
||||
export function delStock(ids) {
|
||||
return request({
|
||||
url: '/stock/vue/' + ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出备货信息
|
||||
export function exportStock(query) {
|
||||
return request({
|
||||
url: '/stock/vue/export',
|
||||
method: 'post', // Assuming POST for export with query params based on how old one was set up.
|
||||
params: query,
|
||||
// The original export function used window.location.href, so the backend handles the file download.
|
||||
// We'll mimic the backend behavior for now and let it respond with a downloadable file.
|
||||
responseType: 'blob' // Important for file download
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 登录方法 - 基于 Session 认证,使用 FormData 格式
|
||||
export function login(username, password, code, rememberMe) {
|
||||
const formData = new FormData()
|
||||
formData.append('username', username)
|
||||
formData.append('password', password)
|
||||
formData.append('validateCode', code)
|
||||
formData.append('rememberMe', rememberMe || false)
|
||||
return request({
|
||||
url: '/login',
|
||||
headers: {
|
||||
isToken: false,
|
||||
'Content-Type': 'multipart/form-data',
|
||||
repeatSubmit: false
|
||||
},
|
||||
method: 'post',
|
||||
data: formData
|
||||
})
|
||||
}
|
||||
|
||||
// 注册方法
|
||||
export function register(data) {
|
||||
return request({
|
||||
url: '/register',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户详细信息
|
||||
export function getInfo() {
|
||||
return request({
|
||||
url: '/getInfo',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 退出方法 - 基于 Session 认证
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/logout',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询发货记录列表
|
||||
export function listDelivery(query) {
|
||||
return request({
|
||||
url: '/manage/delivery/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询发货记录详细
|
||||
export function getDelivery(id) {
|
||||
return request({
|
||||
url: '/manage/delivery/vue/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增发货记录
|
||||
export function addDelivery(data) {
|
||||
return request({
|
||||
url: '/manage/delivery/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改发货记录
|
||||
export function updateDelivery(data) {
|
||||
return request({
|
||||
url: '/manage/delivery/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除发货记录
|
||||
export function delDelivery(id) {
|
||||
return request({
|
||||
url: '/manage/delivery/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出发货记录
|
||||
export function exportDelivery(query) {
|
||||
return request({
|
||||
url: '/manage/delivery/vue/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询关联合同列表
|
||||
export function listOrder(query) {
|
||||
return request({
|
||||
url: '/manage/order/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询发货清单列表
|
||||
export function listDeliveryItems(query) {
|
||||
return request({
|
||||
url: '/sip/list/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 导入发货清单
|
||||
export function importDeliveryItems(data) {
|
||||
return request({
|
||||
url: '/sip/list/vue/importData',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 下载发货清单模板
|
||||
export function downloadTemplate() {
|
||||
return request({
|
||||
url: '/sip/list/vue/export',
|
||||
method: 'post',
|
||||
data: {
|
||||
deliveryId: -1,
|
||||
productCode: '',
|
||||
serialNumber: ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
const baseURL = '/manage/order/vue'
|
||||
|
||||
// 查询合同档案列表
|
||||
export function listOrder(query) {
|
||||
return request({
|
||||
url: `${baseURL}/list`,
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询合同档案详细
|
||||
export function getOrder(id) {
|
||||
return request({
|
||||
url: `${baseURL}/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增合同档案
|
||||
export function addOrder(data) {
|
||||
return request({
|
||||
url: baseURL,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改合同档案
|
||||
export function updateOrder(data) {
|
||||
return request({
|
||||
url: baseURL,
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除合同档案
|
||||
export function delOrder(id) {
|
||||
return request({
|
||||
url: `${baseURL}/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出合同档案
|
||||
export function exportOrder(query) {
|
||||
return request({
|
||||
url: `${baseURL}/export`,
|
||||
method: 'post',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
export function getOrderDetails(query) {
|
||||
return request({
|
||||
url: `/project/order/query/`+query,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询产品信息
|
||||
export function getProductInfo(serialNumber) {
|
||||
return request({
|
||||
url: '/manage/service/product',
|
||||
method: 'get',
|
||||
params: { serialNumber }
|
||||
})
|
||||
}
|
||||
|
||||
// 查询相关合同信息
|
||||
export function getOrderInfo(serialNumber) {
|
||||
return request({
|
||||
url: '/manage/service/order',
|
||||
method: 'get',
|
||||
params: { serialNumber }
|
||||
})
|
||||
}
|
||||
|
||||
// 查询标准保修信息
|
||||
export function getWarrantyInfo(serialNumber) {
|
||||
return request({
|
||||
url: '/manage/service/query',
|
||||
method: 'get',
|
||||
params: { serialNumber }
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 获取路由
|
||||
export const getRouters = () => {
|
||||
return request({
|
||||
url: '/getRouters',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询缓存详细
|
||||
export function getCache() {
|
||||
return request({
|
||||
url: '/monitor/cache',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询缓存名称列表
|
||||
export function listCacheName() {
|
||||
return request({
|
||||
url: '/monitor/cache/getNames',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询缓存键名列表
|
||||
export function listCacheKey(cacheName) {
|
||||
return request({
|
||||
url: '/monitor/cache/getKeys/' + cacheName,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询缓存内容
|
||||
export function getCacheValue(cacheName, cacheKey) {
|
||||
return request({
|
||||
url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 清理指定名称缓存
|
||||
export function clearCacheName(cacheName) {
|
||||
return request({
|
||||
url: '/monitor/cache/clearCacheName/' + cacheName,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 清理指定键名缓存
|
||||
export function clearCacheKey(cacheKey) {
|
||||
return request({
|
||||
url: '/monitor/cache/clearCacheKey/' + cacheKey,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 清理全部缓存
|
||||
export function clearCacheAll() {
|
||||
return request({
|
||||
url: '/monitor/cache/clearCacheAll',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询定时任务调度列表
|
||||
export function listJob(query) {
|
||||
return request({
|
||||
url: '/monitor/job/list/vue',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询定时任务调度详细
|
||||
export function getJob(jobId) {
|
||||
return request({
|
||||
url: '/monitor/job/vue/' + jobId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增定时任务调度
|
||||
export function addJob(data) {
|
||||
return request({
|
||||
url: '/monitor/job/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改定时任务调度
|
||||
export function updateJob(data) {
|
||||
return request({
|
||||
url: '/monitor/job/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除定时任务调度
|
||||
export function delJob(jobId) {
|
||||
return request({
|
||||
url: '/monitor/job/vue/' + jobId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 任务状态修改
|
||||
export function changeJobStatus(jobId, status) {
|
||||
const data = {
|
||||
jobId,
|
||||
status
|
||||
}
|
||||
return request({
|
||||
url: '/monitor/job/changeStatus/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 定时任务立即执行一次
|
||||
export function runJob(jobId, jobGroup) {
|
||||
const data = {
|
||||
jobId,
|
||||
jobGroup
|
||||
}
|
||||
return request({
|
||||
url: '/monitor/job/run/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询调度日志列表
|
||||
export function listJobLog(query) {
|
||||
return request({
|
||||
url: '/monitor/jobLog/list/vue',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 删除调度日志
|
||||
export function delJobLog(jobLogId) {
|
||||
return request({
|
||||
url: '/monitor/jobLog/vue/' + jobLogId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 清空调度日志
|
||||
export function cleanJobLog() {
|
||||
return request({
|
||||
url: '/monitor/jobLog/clean/vue',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询登录日志列表
|
||||
export function list(query) {
|
||||
return request({
|
||||
url: '/monitor/logininfor/list/vue',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 删除登录日志
|
||||
export function delLogininfor(infoId) {
|
||||
return request({
|
||||
url: '/monitor/logininfor/vue/' + infoId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 解锁用户登录状态
|
||||
export function unlockLogininfor(userName) {
|
||||
return request({
|
||||
url: '/monitor/logininfor/unlock/vue/' + userName,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 清空登录日志
|
||||
export function cleanLogininfor() {
|
||||
return request({
|
||||
url: '/monitor/logininfor/clean/vue',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询在线用户列表
|
||||
export function list(query) {
|
||||
return request({
|
||||
url: '/monitor/online/list/vue',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 强退用户
|
||||
export function forceLogout(tokenId) {
|
||||
return request({
|
||||
url: '/monitor/online/vue/' + tokenId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询操作日志列表
|
||||
export function list(query) {
|
||||
return request({
|
||||
url: '/monitor/operlog/list/vue',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 删除操作日志
|
||||
export function delOperlog(operId) {
|
||||
return request({
|
||||
url: '/monitor/operlog/vue/' + operId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 清空操作日志
|
||||
export function cleanOperlog() {
|
||||
return request({
|
||||
url: '/monitor/operlog/clean/vue',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 获取服务信息
|
||||
export function getServer() {
|
||||
return request({
|
||||
url: '/monitor/server/vue',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询项目管理列表
|
||||
export function listProject(query) {
|
||||
return request({
|
||||
url: '/sip/project/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询项目管理详细
|
||||
export function getProject(id) {
|
||||
return request({
|
||||
url: '/sip/project/vue/' + id,
|
||||
method: 'get',
|
||||
needLoading:true
|
||||
})
|
||||
}
|
||||
|
||||
// 新增项目管理
|
||||
export function addProject(data) {
|
||||
return request({
|
||||
url: '/sip/project/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改项目管理
|
||||
export function updateProject(data) {
|
||||
return request({
|
||||
url: '/sip/project/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除项目管理
|
||||
export function delProject(id) {
|
||||
return request({
|
||||
url: '/sip/project/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出项目管理
|
||||
export function exportProject(query) {
|
||||
return request({
|
||||
url: '/sip/project/vue/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询订单管理列表
|
||||
export function listOrder(query) {
|
||||
return request({
|
||||
url: '/project/order/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询订单管理详细信息
|
||||
export function getOrder(id) {
|
||||
return request({
|
||||
url: '/project/order/vue/' + id,
|
||||
method: 'get',
|
||||
needLoading:true
|
||||
})
|
||||
}
|
||||
|
||||
// 新增订单管理
|
||||
export function addOrder(data) {
|
||||
return request({
|
||||
url: '/project/order/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改订单管理
|
||||
export function updateOrder(data) {
|
||||
return request({
|
||||
url: '/project/order/vue',
|
||||
method: 'put',
|
||||
data: data,
|
||||
needLoading:true
|
||||
})
|
||||
}
|
||||
|
||||
// 删除订单管理
|
||||
export function delOrder(id) {
|
||||
return request({
|
||||
url: '/project/order/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出订单管理
|
||||
export function exportOrder(query) {
|
||||
return request({
|
||||
url: '/project/order/vue/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 删除合同文件
|
||||
export function delContractFile(ids) {
|
||||
return request({
|
||||
url: '/project/order/file/remove',
|
||||
method: 'post',
|
||||
data: { ids: ids }
|
||||
})
|
||||
}
|
||||
|
||||
// 上传合同文件
|
||||
export function uploadContractFile(data) {
|
||||
return request({
|
||||
url: '/project/order/importContractData',
|
||||
method: 'post',
|
||||
data: data,
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
}
|
||||
|
||||
// 更新订单财务状态
|
||||
export function updateFinanceStatus(id, status) {
|
||||
const data = {
|
||||
id,
|
||||
financeStatus: status
|
||||
}
|
||||
return request({
|
||||
url: '/project/order/finance/update',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询产品信息列表
|
||||
export function listProduct(query) {
|
||||
return request({
|
||||
url: '/system/product/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询产品信息详细
|
||||
export function getProduct(id) {
|
||||
return request({
|
||||
url: '/system/product/vue/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增产品信息
|
||||
export function addProduct(data) {
|
||||
return request({
|
||||
url: '/system/product/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改产品信息
|
||||
export function updateProduct(data) {
|
||||
return request({
|
||||
url: '/system/product/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除产品信息
|
||||
export function delProduct(id) {
|
||||
return request({
|
||||
url: '/system/product/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出产品信息
|
||||
export function exportProduct(query) {
|
||||
return request({
|
||||
url: '/system/product/vue/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询办事处信息列表
|
||||
export function listAgent(query) {
|
||||
return request({
|
||||
url: '/system/agent/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询办事处信息详细
|
||||
export function getAgent(id) {
|
||||
return request({
|
||||
url: '/system/agent/vue/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增办事处信息
|
||||
export function addAgent(data) {
|
||||
return request({
|
||||
url: '/system/agent/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改办事处信息
|
||||
export function updateAgent(data) {
|
||||
return request({
|
||||
url: '/system/agent/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除办事处信息
|
||||
export function delAgent(id) {
|
||||
return request({
|
||||
url: '/system/agent/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出办事处信息
|
||||
export function exportAgent(query) {
|
||||
return request({
|
||||
url: '/system/agent/vue/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询区域列表
|
||||
export function listAreas(parentId) {
|
||||
return request({
|
||||
url: '/cnarea/select',
|
||||
method: 'get',
|
||||
params: { parentId }
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询参数列表
|
||||
export function listConfig(query) {
|
||||
return request({
|
||||
url: '/system/config/list/vue',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询参数详细
|
||||
export function getConfig(configId) {
|
||||
return request({
|
||||
url: '/system/config/vue/' + configId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据参数键名查询参数值
|
||||
export function getConfigKey(configKey) {
|
||||
return request({
|
||||
url: '/system/config/configKey/vue/' + configKey,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增参数配置
|
||||
export function addConfig(data) {
|
||||
return request({
|
||||
url: '/system/config/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改参数配置
|
||||
export function updateConfig(data) {
|
||||
return request({
|
||||
url: '/system/config/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除参数配置
|
||||
export function delConfig(configId) {
|
||||
return request({
|
||||
url: '/system/config/vue/' + configId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 刷新参数缓存
|
||||
export function refreshCache() {
|
||||
return request({
|
||||
url: '/system/config/refreshCache/vue',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询客户信息列表
|
||||
export function listCustomer(query) {
|
||||
return request({
|
||||
url: '/system/customer/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询客户信息详细
|
||||
export function getCustomer(id) {
|
||||
return request({
|
||||
url: '/system/customer/vue/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增客户信息
|
||||
export function addCustomer(data) {
|
||||
return request({
|
||||
url: '/system/customer/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改客户信息
|
||||
export function updateCustomer(data) {
|
||||
return request({
|
||||
url: '/system/customer/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除客户信息
|
||||
export function delCustomer(id) {
|
||||
return request({
|
||||
url: '/system/customer/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出客户信息
|
||||
export function exportCustomer(query) {
|
||||
return request({
|
||||
url: '/system/customer/vue/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询部门列表
|
||||
export function listDept(query) {
|
||||
return request({
|
||||
url: '/system/dept/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询部门列表(排除节点)
|
||||
export function listDeptExcludeChild(deptId) {
|
||||
return request({
|
||||
url: '/system/dept/vue/list/exclude/' + deptId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询部门详细
|
||||
export function getDept(deptId) {
|
||||
return request({
|
||||
url: '/system/dept/vue/' + deptId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增部门
|
||||
export function addDept(data) {
|
||||
return request({
|
||||
url: '/system/dept/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改部门
|
||||
export function updateDept(data) {
|
||||
return request({
|
||||
url: '/system/dept/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除部门
|
||||
export function delDept(deptId) {
|
||||
return request({
|
||||
url: '/system/dept/vue/' + deptId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询字典数据列表
|
||||
export function listData(query) {
|
||||
return request({
|
||||
url: '/system/dict/data/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询字典数据详细
|
||||
export function getData(dictCode) {
|
||||
return request({
|
||||
url: '/system/dict/data/vue/' + dictCode,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据字典类型查询字典数据信息
|
||||
export function getDicts(dictType) {
|
||||
return request({
|
||||
url: '/system/dict/data/type/' + dictType,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增字典数据
|
||||
export function addData(data) {
|
||||
return request({
|
||||
url: '/system/dict/data/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改字典数据
|
||||
export function updateData(data) {
|
||||
return request({
|
||||
url: '/system/dict/data/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除字典数据
|
||||
export function delData(dictCode) {
|
||||
return request({
|
||||
url: '/system/dict/data/vue/' + dictCode,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询字典类型列表
|
||||
export function listType(query) {
|
||||
return request({
|
||||
url: '/system/dict/type/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询字典类型详细
|
||||
export function getType(dictId) {
|
||||
return request({
|
||||
url: '/system/dict/type/vue/' + dictId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增字典类型
|
||||
export function addType(data) {
|
||||
return request({
|
||||
url: '/system/dict/type/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改字典类型
|
||||
export function updateType(data) {
|
||||
return request({
|
||||
url: '/system/dict/type/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除字典类型
|
||||
export function delType(dictId) {
|
||||
return request({
|
||||
url: '/system/dict/type/vue/' + dictId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 刷新字典缓存
|
||||
export function refreshCache() {
|
||||
return request({
|
||||
url: '/system/dict/type/vue/refreshCache',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取字典选择框列表
|
||||
export function optionselect() {
|
||||
return request({
|
||||
url: '/system/dict/type/vue/optionselect',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询菜单列表
|
||||
export function listMenu(query) {
|
||||
return request({
|
||||
url: '/system/menu/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询菜单详细
|
||||
export function getMenu(menuId) {
|
||||
return request({
|
||||
url: '/system/menu/' + menuId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询菜单下拉树结构
|
||||
export function treeselect() {
|
||||
return request({
|
||||
url: '/system/menu/vue/treeselect',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据角色ID查询菜单下拉树结构
|
||||
export function roleMenuTreeselect(roleId) {
|
||||
return request({
|
||||
url: '/system/menu/roleMenuTreeselect/' + roleId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增菜单
|
||||
export function addMenu(data) {
|
||||
return request({
|
||||
url: '/system/menu',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改菜单
|
||||
export function updateMenu(data) {
|
||||
return request({
|
||||
url: '/system/menu',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除菜单
|
||||
export function delMenu(menuId) {
|
||||
return request({
|
||||
url: '/system/menu/' + menuId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询公告列表
|
||||
export function listNotice(query) {
|
||||
return request({
|
||||
url: '/system/notice/list/vue',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询公告详细
|
||||
export function getNotice(noticeId) {
|
||||
return request({
|
||||
url: '/system/notice/vue/' + noticeId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增公告
|
||||
export function addNotice(data) {
|
||||
return request({
|
||||
url: '/system/notice/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改公告
|
||||
export function updateNotice(data) {
|
||||
return request({
|
||||
url: '/system/notice/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除公告
|
||||
export function delNotice(noticeId) {
|
||||
return request({
|
||||
url: '/system/notice/vue/' + noticeId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询代理商管理列表
|
||||
export function listPartner(query) {
|
||||
return request({
|
||||
url: '/system/partner/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询代理商管理详细信息
|
||||
export function getPartner(id) {
|
||||
return request({
|
||||
url: '/system/partner/vue/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增代理商管理
|
||||
export function addPartner(data) {
|
||||
return request({
|
||||
url: '/system/partner/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改代理商管理
|
||||
export function updatePartner(data) {
|
||||
return request({
|
||||
url: '/system/partner/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除代理商管理
|
||||
export function delPartner(id) {
|
||||
return request({
|
||||
url: '/system/partner/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出代理商管理
|
||||
export function exportPartner(query) {
|
||||
return request({
|
||||
url: '/system/partner/vue/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询代理商信息(公开查询)
|
||||
export function queryPartner(query) {
|
||||
return request({
|
||||
url: '/system/partner/list/query',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询岗位列表
|
||||
export function listPost(query) {
|
||||
return request({
|
||||
url: '/system/post/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询岗位详细
|
||||
export function getPost(postId) {
|
||||
return request({
|
||||
url: '/system/post/vue/' + postId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增岗位
|
||||
export function addPost(data) {
|
||||
return request({
|
||||
url: '/system/post/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改岗位
|
||||
export function updatePost(data) {
|
||||
return request({
|
||||
url: '/system/post/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除岗位
|
||||
export function delPost(postId) {
|
||||
return request({
|
||||
url: '/system/post/vue/' + postId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询产品管理列表
|
||||
export function listProduct(query) {
|
||||
return request({
|
||||
url: '/system/product/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询产品管理详细
|
||||
export function getProduct(id) {
|
||||
return request({
|
||||
url: '/system/product/vue/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增产品管理
|
||||
export function addProduct(data) {
|
||||
return request({
|
||||
url: '/system/product/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改产品管理
|
||||
export function updateProduct(data) {
|
||||
return request({
|
||||
url: '/system/product/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除产品管理
|
||||
export function delProduct(id) {
|
||||
return request({
|
||||
url: '/system/product/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出产品管理
|
||||
export function exportProduct(query) {
|
||||
return request({
|
||||
url: '/system/product/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// a new function that queries a product by its code
|
||||
export function getProductByCode(code) {
|
||||
return request({
|
||||
url: '/system/product/vue/query/' + code,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询角色列表
|
||||
export function listRole(query) {
|
||||
return request({
|
||||
url: '/system/role/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询角色详细
|
||||
export function getRole(roleId) {
|
||||
return request({
|
||||
url: '/system/role/vue/' + roleId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增角色
|
||||
export function addRole(data) {
|
||||
return request({
|
||||
url: '/system/role/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改角色
|
||||
export function updateRole(data) {
|
||||
return request({
|
||||
url: '/system/role/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 角色数据权限
|
||||
export function dataScope(data) {
|
||||
return request({
|
||||
url: '/system/role/vue/dataScope',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 角色状态修改
|
||||
export function changeRoleStatus(roleId, status) {
|
||||
const data = {
|
||||
roleId,
|
||||
status
|
||||
}
|
||||
return request({
|
||||
url: '/system/role/vue/changeStatus',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除角色
|
||||
export function delRole(roleId) {
|
||||
return request({
|
||||
url: '/system/role/vue/' + roleId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询角色已授权用户列表
|
||||
export function allocatedUserList(query) {
|
||||
return request({
|
||||
url: '/system/role/vue/authUser/allocatedList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询角色未授权用户列表
|
||||
export function unallocatedUserList(query) {
|
||||
return request({
|
||||
url: '/system/role/vue/authUser/unallocatedList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 取消用户授权角色
|
||||
export function authUserCancel(data) {
|
||||
return request({
|
||||
url: '/system/role/vue/authUser/cancel',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 批量取消用户授权角色
|
||||
export function authUserCancelAll(data) {
|
||||
return request({
|
||||
url: '/system/role/vue/authUser/cancelAll',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 授权用户选择
|
||||
export function authUserSelectAll(data) {
|
||||
return request({
|
||||
url: '/system/role/vue/authUser/selectAll',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 根据角色ID查询部门树结构
|
||||
export function deptTreeSelect(roleId) {
|
||||
return request({
|
||||
url: '/system/role/vue/deptTree/' + roleId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
import request from '@/utils/request'
|
||||
import { parseStrEmpty } from "@/utils/ruoyi";
|
||||
|
||||
// 查询用户列表
|
||||
export function listUser(query) {
|
||||
return request({
|
||||
url: '/system/user/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询用户详细
|
||||
export function getUser(userId) {
|
||||
return request({
|
||||
url: '/system/user/' + parseStrEmpty(userId),
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增用户
|
||||
export function addUser(data) {
|
||||
return request({
|
||||
url: '/system/user',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用户
|
||||
export function updateUser(data) {
|
||||
return request({
|
||||
url: '/system/user',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
export function delUser(userId) {
|
||||
return request({
|
||||
url: '/system/user/' + userId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 用户密码重置
|
||||
export function resetUserPwd(userId, password) {
|
||||
const data = {
|
||||
userId,
|
||||
password
|
||||
}
|
||||
return request({
|
||||
url: '/system/user/resetPwd',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户状态修改
|
||||
export function changeUserStatus(userId, status) {
|
||||
const data = {
|
||||
userId,
|
||||
status
|
||||
}
|
||||
return request({
|
||||
url: '/system/user/changeStatus',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询用户个人信息
|
||||
export function getUserProfile() {
|
||||
return request({
|
||||
url: '/system/user/profile',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用户个人信息
|
||||
export function updateUserProfile(data) {
|
||||
return request({
|
||||
url: '/system/user/profile',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户密码重置
|
||||
export function updateUserPwd(oldPassword, newPassword) {
|
||||
const data = {
|
||||
oldPassword,
|
||||
newPassword
|
||||
}
|
||||
return request({
|
||||
url: '/system/user/profile/updatePwd',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户头像上传
|
||||
export function uploadAvatar(data) {
|
||||
return request({
|
||||
url: '/system/user/profile/avatar',
|
||||
method: 'post',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询授权角色
|
||||
export function getAuthRole(userId) {
|
||||
return request({
|
||||
url: '/system/user/authRole/' + userId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 保存授权角色
|
||||
export function updateAuthRole(data) {
|
||||
return request({
|
||||
url: '/system/user/authRole',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询部门下拉树结构
|
||||
export function deptTreeSelect() {
|
||||
return request({
|
||||
url: '/system/user/deptTree',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询生成表数据
|
||||
export function listTable(query) {
|
||||
return request({
|
||||
url: '/tool/gen/list/vue',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
// 查询db数据库列表
|
||||
export function listDbTable(query) {
|
||||
return request({
|
||||
url: '/tool/gen/db/list/vue',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询表详细信息
|
||||
export function getGenTable(tableId) {
|
||||
return request({
|
||||
url: '/tool/gen/vue/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改代码生成信息
|
||||
export function updateGenTable(data) {
|
||||
return request({
|
||||
url: '/tool/gen/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 导入表
|
||||
export function importTable(data) {
|
||||
return request({
|
||||
url: '/tool/gen/importTable/vue',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 创建表
|
||||
export function createTable(data) {
|
||||
return request({
|
||||
url: '/tool/gen/createTable/vue',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 预览生成代码
|
||||
export function previewTable(tableId) {
|
||||
return request({
|
||||
url: '/tool/gen/preview/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除表数据
|
||||
export function delTable(tableId) {
|
||||
return request({
|
||||
url: '/tool/gen/vue/' + tableId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 生成代码(自定义路径)
|
||||
export function genCode(tableName) {
|
||||
return request({
|
||||
url: '/tool/gen/genCode/' + tableName,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 同步数据库
|
||||
export function synchDb(tableName) {
|
||||
return request({
|
||||
url: '/tool/gen/synchDb/' + tableName,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询仓库基础信息列表
|
||||
export function listInfo(query) {
|
||||
return request({
|
||||
url: '/warehouse/info/vue/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询仓库基础信息详细
|
||||
export function getInfo(id) {
|
||||
return request({
|
||||
url: '/warehouse/info/vue/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增仓库基础信息
|
||||
export function addInfo(data) {
|
||||
return request({
|
||||
url: '/warehouse/info/vue',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改仓库基础信息
|
||||
export function updateInfo(data) {
|
||||
return request({
|
||||
url: '/warehouse/info/vue',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除仓库基础信息
|
||||
export function delInfo(id) {
|
||||
return request({
|
||||
url: '/warehouse/info/vue/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出仓库基础信息
|
||||
export function exportInfo(query) {
|
||||
return request({
|
||||
url: '/warehouse/info/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
import Vue from 'vue'
|
||||
import SvgIcon from '@/components/SvgIcon'// svg component
|
||||
|
||||
// register globally
|
||||
Vue.component('svg-icon', SvgIcon)
|
||||
|
||||
const req = require.context('./svg', false, /\.svg$/)
|
||||
const requireAll = requireContext => requireContext.keys().map(requireContext)
|
||||
requireAll(req)
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M121.718 73.272v9.953c3.957-7.584 6.199-16.05 6.199-24.995C127.917 26.079 99.273 0 63.958 0 28.644 0 0 26.079 0 58.23c0 .403.028.806.028 1.21l22.97-25.953h13.34l-19.76 27.187h6.42V53.77l13.728-19.477v49.361H22.998V73.272H2.158c5.951 20.284 23.608 36.208 45.998 41.399-1.44 3.3-5.618 11.263-12.565 12.674-8.607 1.764 23.358.428 46.163-13.178 17.519-4.611 31.938-15.849 39.77-30.513h-13.506V73.272H85.02V59.464l22.998-25.977h13.008l-19.429 27.187h6.421v-7.433l13.727-19.402v39.433h-.027zm-78.24 2.822a10.516 10.516 0 0 1-.996-4.535V44.548c0-1.613.332-3.124.996-4.535a11.66 11.66 0 0 1 2.713-3.68c1.134-1.032 2.49-1.864 4.04-2.468 1.55-.605 3.21-.908 4.982-.908h11.292c1.77 0 3.431.303 4.981.908 1.522.604 2.85 1.41 3.986 2.418l-12.26 16.303v-2.898a1.96 1.96 0 0 0-.665-1.512c-.443-.403-.996-.604-1.66-.604-.665 0-1.218.201-1.661.604a1.96 1.96 0 0 0-.664 1.512v9.071L44.364 77.606a10.556 10.556 0 0 1-.886-1.512zm35.73-4.535c0 1.613-.332 3.124-.997 4.535a11.66 11.66 0 0 1-2.712 3.68c-1.134 1.032-2.49 1.864-4.04 2.469-1.55.604-3.21.907-4.982.907H55.185c-1.77 0-3.431-.303-4.981-.907-1.55-.605-2.906-1.437-4.041-2.47a12.49 12.49 0 0 1-1.384-1.512l13.727-18.217v6.375c0 .605.222 1.109.665 1.512.442.403.996.604 1.66.604.664 0 1.218-.201 1.66-.604a1.96 1.96 0 0 0 .665-1.512V53.87L75.97 36.838c.913.932 1.66 1.99 2.214 3.175.664 1.41.996 2.922.996 4.535v27.011h.028z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M127.88 73.143c0 1.412-.506 2.635-1.518 3.669-1.011 1.033-2.209 1.55-3.592 1.55h-17.887c0 9.296-1.783 17.178-5.35 23.645l16.609 17.044c1.011 1.034 1.517 2.257 1.517 3.67 0 1.412-.506 2.635-1.517 3.668-.958 1.033-2.155 1.55-3.593 1.55-1.438 0-2.635-.517-3.593-1.55l-15.811-16.063a15.49 15.49 0 0 1-1.196 1.06c-.532.434-1.65 1.208-3.353 2.322a50.104 50.104 0 0 1-5.192 2.974c-1.758.87-3.94 1.658-6.546 2.364-2.607.706-5.189 1.06-7.748 1.06V47.044H58.89v73.062c-2.716 0-5.417-.367-8.106-1.102-2.688-.734-5.003-1.631-6.945-2.692a66.769 66.769 0 0 1-5.268-3.179c-1.571-1.057-2.73-1.94-3.476-2.65L33.9 109.34l-14.611 16.877c-1.066 1.14-2.344 1.711-3.833 1.711-1.277 0-2.422-.434-3.434-1.304-1.012-.978-1.557-2.187-1.635-3.627-.079-1.44.333-2.705 1.236-3.794l16.129-18.51c-3.087-6.197-4.63-13.644-4.63-22.342H5.235c-1.383 0-2.58-.517-3.592-1.55S.125 74.545.125 73.132c0-1.412.506-2.635 1.518-3.668 1.012-1.034 2.21-1.55 3.592-1.55h17.887V43.939L9.308 29.833c-1.012-1.033-1.517-2.256-1.517-3.669 0-1.412.505-2.635 1.517-3.668 1.012-1.034 2.21-1.55 3.593-1.55s2.58.516 3.593 1.55l13.813 14.106h67.396l13.814-14.106c1.012-1.034 2.21-1.55 3.592-1.55 1.384 0 2.581.516 3.593 1.55 1.012 1.033 1.518 2.256 1.518 3.668 0 1.413-.506 2.636-1.518 3.67l-13.814 14.105v23.975h17.887c1.383 0 2.58.516 3.593 1.55 1.011 1.033 1.517 2.256 1.517 3.668l-.005.01zM89.552 26.175H38.448c0-7.23 2.489-13.386 7.466-18.469C50.892 2.623 56.92.082 64 .082c7.08 0 13.108 2.541 18.086 7.624 4.977 5.083 7.466 11.24 7.466 18.469z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1568899741379" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2054" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M960 591.424V368.96c0-0.288 0.16-0.512 0.16-0.768S960 367.68 960 367.424V192a32 32 0 0 0-32-32H96a32 32 0 0 0-32 32v175.424c0 0.288-0.16 0.512-0.16 0.768s0.16 0.48 0.16 0.768v222.464c0 0.288-0.16 0.512-0.16 0.768s0.16 0.48 0.16 0.768V864a32 32 0 0 0 32 32h832a32 32 0 0 0 32-32v-271.04c0-0.288 0.16-0.512 0.16-0.768S960 591.68 960 591.424z m-560-31.232v-160H608v160h-208z m208 64V832h-208v-207.808H608z m-480-224h208v160H128v-160z m544 0h224v160h-224v-160zM896 224v112.192H128V224h768zM128 624.192h208V832H128v-207.808zM672 832v-207.808h224V832h-224z" p-id="2055"></path></svg>
|
||||
|
After Width: | Height: | Size: 954 B |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1588670460195" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1314" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M230.4 307.712c13.824 0 25.088-11.264 25.088-25.088 0-100.352 81.92-182.272 182.272-182.272s182.272 81.408 182.272 182.272c0 13.824 11.264 25.088 25.088 25.088s25.088-11.264 24.576-25.088c0-127.488-103.936-231.936-231.936-231.936S205.824 154.624 205.824 282.624c-0.512 14.336 10.752 25.088 24.576 25.088z m564.736 234.496c-11.264 0-21.504 2.048-31.232 6.144 0-44.544-40.448-81.92-88.064-81.92-14.848 0-28.16 3.584-39.936 10.24-13.824-28.16-44.544-48.128-78.848-48.128-12.288 0-24.576 2.56-35.328 7.68V284.16c0-45.568-37.888-81.92-84.48-81.92s-84.48 36.864-84.48 81.92v348.672l-69.12-112.64c-18.432-28.16-58.368-36.864-91.136-19.968-26.624 14.336-46.592 47.104-30.208 88.064 3.072 8.192 76.8 205.312 171.52 311.296 0 0 28.16 24.576 43.008 58.88 4.096 9.728 13.312 15.36 22.528 15.36 3.072 0 6.656-0.512 9.728-2.048 12.288-5.12 18.432-19.968 12.8-32.256-19.456-44.544-53.76-74.752-53.76-74.752C281.6 768 209.408 573.44 208.384 570.88c-5.12-12.8-2.56-20.992 7.168-26.112 9.216-4.608 21.504-4.608 26.112 2.56l113.152 184.32c4.096 8.704 12.8 14.336 22.528 14.336 13.824 0 25.088-10.752 25.088-25.088V284.16c0-17.92 15.36-32.256 34.816-32.256s34.816 14.336 34.816 32.256v284.16c0 13.824 10.24 25.088 24.576 25.088 13.824 0 25.088-11.264 25.088-25.088v-57.344c0-17.92 15.36-32.768 34.816-32.768 19.968 0 37.376 15.36 37.376 32.768v95.232c0 7.168 3.072 13.312 7.68 17.92 4.608 4.608 10.752 7.168 17.92 7.168 13.824 0 24.576-11.264 24.576-25.088V547.84c0-18.432 13.824-32.256 32.256-32.256 20.48 0 38.912 15.36 38.912 32.256v95.232c0 13.824 11.264 25.088 25.088 25.088s24.576-11.264 25.088-25.088v-18.944c0-18.944 12.8-32.256 30.72-32.256 18.432 0 22.528 18.944 22.528 31.744 0 1.024-11.776 99.84-50.688 173.056-30.72 58.368-45.056 112.128-51.2 146.944-2.56 13.312 6.656 26.112 19.968 28.672 1.536 0 3.072 0.512 4.608 0.512 11.776 0 22.016-8.192 24.064-20.48 5.632-31.232 18.432-79.36 46.08-132.608 43.52-81.92 55.808-186.88 56.32-193.536-0.512-50.688-29.696-83.968-72.704-83.968z"></path></path></svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1576153230908" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="971" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M772.87036133 734.06115723c-43.34106445 0-80.00793458 27.93273926-93.76831055 66.57714843H475.90991211c-56.60705567 0-102.66723633-46.06018067-102.66723633-102.66723633V600.82446289h305.859375c13.76037598 38.64440918 50.42724609 66.57714844 93.76831055 66.57714844 55.12390137 0 99.94812012-44.82421875 99.94812012-99.94812012S827.9942627 467.50537109 772.87036133 467.50537109c-43.34106445 0-80.00793458 27.93273926-93.76831055 66.57714844H373.24267578V401.01062011h321.92687989c55.12390137 0 99.94812012-44.82421875 99.94812011-99.94812011V190.07312011C795.11767578 134.94921875 750.29345703 90.125 695.16955567 90.125H251.12963867C196.0057373 90.125 151.18151855 134.94921875 151.18151855 190.07312011V301.0625c0 55.12390137 44.82421875 99.94812012 99.94812012 99.94812012h55.53588867v296.96044921c0 93.35632325 75.97045898 169.32678223 169.32678224 169.32678223h203.19213866c13.76037598 38.64440918 50.42724609 66.57714844 93.76831055 66.57714844 55.12390137 0 99.94812012-44.82421875 99.94812012-99.94812012s-44.90661622-99.86572266-100.03051758-99.86572265z m0-199.89624024c18.37463379 0 33.28857422 14.91394043 33.28857422 33.28857423s-14.91394043 33.28857422-33.28857422 33.28857421-33.28857422-14.91394043-33.28857422-33.28857421 14.91394043-33.28857422 33.28857422-33.28857422zM217.75866699 301.0625V190.07312011c0-18.37463379 14.91394043-33.28857422 33.28857423-33.28857421h444.03991698c18.37463379 0 33.28857422 14.91394043 33.28857422 33.28857422V301.0625c0 18.37463379-14.91394043 33.28857422-33.28857422 33.28857422H251.12963867c-18.37463379 0-33.37097168-14.91394043-33.37097168-33.28857422z m555.11169434 566.23535156c-18.37463379 0-33.28857422-14.91394043-33.28857422-33.28857422 0-18.37463379 14.91394043-33.28857422 33.28857422-33.28857422s33.28857422 14.91394043 33.28857422 33.28857422c0.08239747 18.29223633-14.91394043 33.28857422-33.28857422 33.28857422z" p-id="972"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h36.571V128H0V54.857zM91.429 27.43H128V128H91.429V27.429zM45.714 0h36.572v128H45.714V0z"/></svg>
|
||||
|
After Width: | Height: | Size: 179 B |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1575982282951" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="902" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M828.40625 90.125H195.59375C137.375 90.125 90.125 137.375 90.125 195.59375v632.8125c0 58.21875 47.25 105.46875 105.46875 105.46875h632.8125c58.21875 0 105.46875-47.25 105.46875-105.46875V195.59375c0-58.21875-47.25-105.46875-105.46875-105.46875z m52.734375 738.28125c0 29.16-23.57015625 52.734375-52.734375 52.734375H195.59375c-29.109375 0-52.734375-23.574375-52.734375-52.734375V195.59375c0-29.109375 23.625-52.734375 52.734375-52.734375h632.8125c29.16 0 52.734375 23.625 52.734375 52.734375v632.8125z" p-id="903"></path><path d="M421.52890625 709.55984375a36.28125 36.28125 0 0 1-27.55265625-12.66890625L205.17453125 476.613125a36.28546875 36.28546875 0 0 1 55.10109375-47.22890625l164.986875 192.4846875 342.16171875-298.48078125a36.2896875 36.2896875 0 0 1 47.70984375 54.68765625L445.3859375 700.6203125a36.3234375 36.3234375 0 0 1-23.85703125 8.93953125z" p-id="904"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.857 118.857h64V73.143H89.143c-1.902 0-3.52-.668-4.855-2.002-1.335-1.335-2.002-2.954-2.002-4.855V36.57H54.857v82.286zM73.143 16v-4.571a2.2 2.2 0 0 0-.677-1.61 2.198 2.198 0 0 0-1.609-.676H20.571c-.621 0-1.158.225-1.609.676a2.198 2.198 0 0 0-.676 1.61V16a2.2 2.2 0 0 0 .676 1.61c.451.45.988.676 1.61.676h50.285c.622 0 1.158-.226 1.61-.677.45-.45.676-.987.676-1.609zm18.286 48h21.357L91.43 42.642V64zM128 73.143v48c0 1.902-.667 3.52-2.002 4.855-1.335 1.335-2.953 2.002-4.855 2.002H52.57c-1.901 0-3.52-.667-4.854-2.002-1.335-1.335-2.003-2.953-2.003-4.855v-11.429H6.857c-1.902 0-3.52-.667-4.855-2.002C.667 106.377 0 104.759 0 102.857v-96c0-1.902.667-3.52 2.002-4.855C3.337.667 4.955 0 6.857 0h77.714c1.902 0 3.52.667 4.855 2.002 1.335 1.335 2.003 2.953 2.003 4.855V30.29c1 .622 1.856 1.29 2.569 2.003l29.147 29.147c1.335 1.335 2.478 3.145 3.429 5.43.95 2.287 1.426 4.383 1.426 6.291v-.018z"/></svg>
|
||||
|
After Width: | Height: | Size: 971 B |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1546567861908" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2422" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M318.577778 819.2L17.066667 512l301.511111-307.2 45.511111 45.511111L96.711111 512l267.377778 261.688889zM705.422222 819.2l-45.511111-45.511111L927.288889 512l-267.377778-261.688889 45.511111-45.511111L1006.933333 512zM540.785778 221.866667l55.751111 11.150222L483.157333 802.133333l-55.751111-11.093333z" p-id="2423"></path></svg>
|
||||
|
After Width: | Height: | Size: 717 B |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1577252187056" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2508" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M747.59340925 691.12859384c11.51396329 0.25305413 22.43746719-0.21087818 40.74171707-1.51832482 29.35428085-2.10878421 35.84933734-2.36183835 46.47761114-0.8856895 24.71495444 3.37405491 41.12129828 21.76265671 32.47528161 47.95376084-85.57447632 258.19957947-442.00123984 249.76444099-628.67084683 50.73735554-153.47733892-159.33976008-153.09775772-414.41833795 0.92786545-573.42069196 159.71934128-162.67163983 424.03439521-166.59397897 565.78689185 0.63263534 80.38686649 94.81095318 108.34934958 169.16669549 89.11723508 230.57450162-15.01454608 47.99593598-50.61082928 77.68762207-119.77896259 114.63352789-4.89237973 2.65706845-29.35428085 15.52065436-35.84933652 19.02123633-46.94154346 25.30541465-63.51659033 41.20565021-62.20914449 58.45550757 2.95229856 39.13904114 24.16667102 52.7196135 70.98168823 53.81618115z m44.41100207 50.10472101c-19.82257471 1.43397372-32.05352527 1.940082-45.63409763 1.6448519-70.34905207-1.60267593-115.98314969-30.91478165-121.38163769-101.64341492-3.45840683-46.05585397 24.7571304-73.13264758 89.24376132-107.96976837 6.7902866-3.66928501 31.37871396-16.57504688 36.06021551-19.06341229 57.69634516-30.83042972 85.15271997-53.73183005 94.76877722-84.47790866 12.77923398-40.78389304-9.10994898-98.94417051-79.24812286-181.6507002-121.17075953-142.97559219-350.14258521-139.60153647-489.2380134 2.06660824-134.49827774 138.84237405-134.79350784 362.12048163-0.42175717 501.637667 158.53842169 168.99799328 451.9968783 181.18676788 534.57688175-11.80919339-4.68150156 0.2952301-10.71262573 0.67481131-18.72600705 1.26527069z" p-id="2509"></path><path d="M346.03865637 637.18588562a78.82636652 78.82636652 0 0 0 78.32025825-79.29029883c0-43.69401562-35.005823-79.29029883-78.32025825-79.29029882a78.82636652 78.82636652 0 0 0-78.36243338 79.29029882c0 43.69401562 35.005823 79.29029883 78.36243338 79.29029883z m0-51.7495729a27.07679361 27.07679361 0 0 1-26.5706845-27.54072593c0-15.30977536 11.97789643-27.54072593 26.5706845-27.54072592 14.55061295 0 26.57068533 12.23095057 26.57068533 27.54072592a27.07679361 27.07679361 0 0 1-26.57068533 27.54072593zM475.7289063 807.11174353a78.82636652 78.82636652 0 0 0 78.3624334-79.29029882c0-43.69401562-34.96364785-79.29029883-78.32025825-79.29029883a78.82636652 78.82636652 0 0 0-78.32025742 79.29029883c0 43.69401562 34.96364785 79.29029883 78.32025742 79.29029882z m0-51.74957208a27.07679361 27.07679361 0 0 1-26.57068532-27.54072674c0-15.30977536 12.06224753-27.54072593 26.57068532-27.54072593 14.59278892 0 26.57068533 12.23095057 26.57068453 27.54072593a27.07679361 27.07679361 0 0 1-26.57068453 27.54072674zM601.24376214 377.21492718a78.82636652 78.82636652 0 0 0 78.32025742-79.29029883c0-43.69401562-34.96364785-79.29029883-78.32025742-79.29029882a78.82636652 78.82636652 0 0 0-78.32025823 79.29029883c0 43.69401562 34.96364785 79.29029883 78.32025824 79.29029883z m1e-8-51.74957208a27.07679361 27.07679361 0 0 1-26.57068534-27.54072675c0-15.30977536 11.97789643-27.54072593 26.57068534-27.54072591 14.55061295 0 26.57068533 12.23095057 26.57068451 27.54072592a27.07679361 27.07679361 0 0 1-26.57068451 27.54072674zM378.80916809 433.85687983a78.82636652 78.82636652 0 0 0 78.32025824-79.29029883c0-43.69401562-34.96364785-79.29029883-78.32025824-79.29029802a78.82636652 78.82636652 0 0 0-78.32025742 79.29029802c0 43.69401562 34.96364785 79.29029883 78.32025742 79.29029883z m0-51.74957209a27.07679361 27.07679361 0 0 1-26.57068451-27.54072674c0-15.30977536 11.97789643-27.54072593 26.57068451-27.54072593 14.55061295 0 26.57068533 12.23095057 26.57068533 27.54072593a27.07679361 27.07679361 0 0 1-26.57068533 27.54072674z" p-id="2510"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1575804206892" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3145" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M826.56 470.016c-32.896 0-64.384 12.288-89.984 35.52l0-104.96c0-62.208-50.496-112.832-112.64-113.088L623.936 287.04 519.552 287.104C541.824 262.72 554.56 230.72 554.56 197.12c0-73.536-59.904-133.44-133.504-133.44-73.472 0-133.376 59.904-133.376 133.44 0 32.896 12.224 64.256 35.52 89.984L175.232 287.104l0 0.576C113.728 288.704 64 338.88 64 400.576l0.32 0 0.32 116.48C60.864 544.896 70.592 577.728 100.8 588.48c12.736 4.608 37.632 7.488 60.864-25.28 12.992-18.368 34.24-29.248 56.64-29.248 38.336 0 69.504 31.104 69.504 69.312 0 38.4-31.168 69.504-69.504 69.504-22.656 0-44.032-11.264-57.344-30.4C138.688 610.112 112.576 615.36 102.464 619.136c-29.824 10.752-39.104 43.776-38.144 67.392l0 160.384L64 846.912C64 909.248 114.752 960 177.216 960l446.272 0c62.4 0 113.152-50.752 113.152-113.152l0-145.024c24.384 22.272 56.384 35.008 89.984 35.008 73.536 0 133.44-59.904 133.44-133.504C960 529.92 900.096 470.016 826.56 470.016zM826.56 672.896c-22.72 0-44.032-11.264-57.344-30.4-22.272-32.384-48.448-27.136-58.56-23.36-29.824 10.752-39.04 43.776-38.08 67.392l0 160.384c0 27.136-22.016 49.152-49.152 49.152L177.216 896.064C150.08 896 128 873.984 128 846.848l0.32 0 0-145.024c24.384 22.272 56.384 35.008 89.984 35.008 73.6 0 133.504-59.904 133.504-133.504 0-73.472-59.904-133.376-133.504-133.376-32.896 0-64.32 12.288-89.984 35.52l0-104.96L128 400.512c0-27.072 22.08-49.152 49.216-49.152L177.216 351.04 334.656 350.72c3.776 0.512 7.616 0.832 11.52 0.832 24.896 0 50.752-10.816 60.032-37.056 4.544-12.736 7.424-37.568-25.344-60.736C362.624 240.768 351.68 219.52 351.68 197.12c0-38.272 31.104-69.44 69.376-69.44 38.336 0 69.504 31.168 69.504 69.44 0 22.72-11.264 44.032-30.528 57.472C427.968 276.736 433.088 302.784 436.8 313.024c10.752 29.888 43.072 39.232 67.392 38.08l119.232 0 0 0.384c27.136 0 49.152 22.08 49.152 49.152l0.256 116.48c-3.776 27.84 6.016 60.736 36.224 71.488 12.736 4.608 37.632 7.488 60.8-25.28 13.056-18.368 34.24-29.248 56.704-29.248C864.832 534.016 896 565.12 896 603.392 896 641.728 864.832 672.896 826.56 672.896z" p-id="3146"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg width="128" height="100" xmlns="http://www.w3.org/2000/svg"><path d="M27.429 63.638c0-2.508-.893-4.65-2.679-6.424-1.786-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.465 2.662-1.785 1.774-2.678 3.916-2.678 6.424 0 2.508.893 4.65 2.678 6.424 1.786 1.775 3.94 2.662 6.465 2.662 2.524 0 4.678-.887 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm13.714-31.801c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM71.714 65.98l7.215-27.116c.285-1.23.107-2.378-.536-3.443-.643-1.064-1.56-1.762-2.75-2.094-1.19-.33-2.333-.177-3.429.462-1.095.639-1.81 1.573-2.143 2.804l-7.214 27.116c-2.857.237-5.405 1.266-7.643 3.088-2.238 1.822-3.738 4.152-4.5 6.992-.952 3.644-.476 7.098 1.429 10.364 1.905 3.265 4.69 5.37 8.357 6.317 3.667.947 7.143.474 10.429-1.42 3.285-1.892 5.404-4.66 6.357-8.305.762-2.84.619-5.607-.429-8.305-1.047-2.697-2.762-4.85-5.143-6.46zm47.143-2.342c0-2.508-.893-4.65-2.678-6.424-1.786-1.775-3.94-2.662-6.465-2.662-2.524 0-4.678.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.786 1.775 3.94 2.662 6.464 2.662 2.524 0 4.679-.887 6.465-2.662 1.785-1.775 2.678-3.916 2.678-6.424zm-45.714-45.43c0-2.509-.893-4.65-2.679-6.425C68.68 10.01 66.524 9.122 64 9.122c-2.524 0-4.679.887-6.464 2.661-1.786 1.775-2.679 3.916-2.679 6.425 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm32 13.629c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM128 63.638c0 12.351-3.357 23.78-10.071 34.286-.905 1.372-2.19 2.058-3.858 2.058H13.93c-1.667 0-2.953-.686-3.858-2.058C3.357 87.465 0 76.037 0 63.638c0-8.613 1.69-16.847 5.071-24.703C8.452 31.08 13 24.312 18.714 18.634c5.715-5.68 12.524-10.199 20.429-13.559C47.048 1.715 55.333.035 64 .035c8.667 0 16.952 1.68 24.857 5.04 7.905 3.36 14.714 7.88 20.429 13.559 5.714 5.678 10.262 12.446 13.643 20.301 3.38 7.856 5.071 16.09 5.071 24.703z"/></svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1579774833889" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1376" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M887.466667 192.853333h-100.693334V119.466667c0-10.24-6.826667-17.066667-17.066666-17.066667s-17.066667 6.826667-17.066667 17.066667v73.386666H303.786667V119.466667c0-10.24-6.826667-17.066667-17.066667-17.066667s-17.066667 6.826667-17.066667 17.066667v73.386666H168.96c-46.08 0-85.333333 37.546667-85.333333 85.333334V836.266667c0 46.08 37.546667 85.333333 85.333333 85.333333H887.466667c46.08 0 85.333333-37.546667 85.333333-85.333333V278.186667c0-47.786667-37.546667-85.333333-85.333333-85.333334z m-718.506667 34.133334h100.693333v66.56c0 10.24 6.826667 17.066667 17.066667 17.066666s17.066667-6.826667 17.066667-17.066666v-66.56h450.56v66.56c0 10.24 6.826667 17.066667 17.066666 17.066666s17.066667-6.826667 17.066667-17.066666v-66.56H887.466667c27.306667 0 51.2 22.186667 51.2 51.2v88.746666H117.76v-88.746666c0-29.013333 22.186667-51.2 51.2-51.2zM887.466667 887.466667H168.96c-27.306667 0-51.2-22.186667-51.2-51.2V401.066667H938.666667V836.266667c0 27.306667-22.186667 51.2-51.2 51.2z" p-id="1377"></path><path d="M858.453333 493.226667H327.68c-10.24 0-17.066667 6.826667-17.066667 17.066666v114.346667h-116.053333c-10.24 0-17.066667 6.826667-17.066667 17.066667v133.12c0 10.24 6.826667 17.066667 17.066667 17.066666H460.8c10.24 0 17.066667-6.826667 17.066667-17.066666v-114.346667h380.586666c10.24 0 17.066667-6.826667 17.066667-17.066667v-133.12c0-10.24-6.826667-17.066667-17.066667-17.066666z m-413.013333 34.133333v97.28h-98.986667v-97.28h98.986667z m-230.4 131.413333h98.986667v98.986667h-98.986667v-98.986667z m131.413333 97.28v-97.28h98.986667v97.28h-98.986667z m133.12-228.693333h97.28v98.986667h-97.28v-98.986667z m131.413334 0h98.986666v98.986667h-98.986666v-98.986667z m230.4 97.28h-98.986667v-98.986667h98.986667v98.986667z" p-id="1378"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1577186573535" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1068" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><defs><style type="text/css"></style></defs><path d="M479.85714249 608.42857168h64.28571502c19.28571417 0 32.14285751-12.85714249 32.14285664-32.14285751s-12.85714249-32.14285751-32.14285664-32.14285664h-64.28571504c-19.28571417 0-32.14285751 12.85714249-32.14285664 32.14285662s12.85714249 32.14285751 32.14285664 32.14285753z m-2e-8 122.14285665h64.28571504c19.28571417 0 32.14285751-12.85714249 32.14285664-32.14285665s-12.85714249-32.14285751-32.14285664-32.14285751h-64.28571504c-19.28571417 0-32.14285751 12.85714249-32.14285664 32.14285751s12.85714249 32.14285751 32.14285664 32.14285664z m353.57142921-559.28571416h-128.57142921v-32.14285664c0-19.28571417-12.85714249-32.14285751-32.14285664-32.14285753s-32.14285751 12.85714249-32.14285751 32.14285753v32.14285664h-257.14285665v-32.14285664c0-19.28571417-12.85714249-32.14285751-32.14285752-32.14285753s-32.14285751 12.85714249-32.14285664 32.14285753v32.14285664h-128.57142919c-70.71428585 0-128.57142832 57.85714249-128.57142832 122.14285751v501.42857081c0 70.71428585 57.85714249 128.57142832 128.57142832 122.14285751h642.85714335c70.71428585 0 128.57142832-57.85714249 128.57142833-122.14285751v-501.42857081c0-70.71428585-57.85714249-122.14285753-128.57142833-122.14285751z m64.28571415 623.57142832c0 32.14285751-32.14285751 64.28571415-64.28571416 64.28571504h-642.85714335c-32.14285751 0-64.28571415-25.71428583-64.28571417-64.28571504v-372.85714249h771.42857168v372.85714249z m0-437.14285664h-771.42857168v-64.28571417c0-32.14285751 32.14285751-64.28571415 64.28571417-64.28571415h128.57142919v32.14285664c0 19.28571417 12.85714249 32.14285751 32.14285664 32.14285751s32.14285751-12.85714249 32.14285753-32.14285751v-32.14285664h257.14285665v32.14285664c0 19.28571417 12.85714249 32.14285751 32.1428575 32.14285751s32.14285751-12.85714249 32.14285664-32.14285751v-32.14285664h128.57142921c32.14285751 0 64.28571415 25.71428583 64.28571415 64.28571415v64.28571417z m-610.71428583 372.85714247h64.28571415c19.28571417 0 32.14285751-12.85714249 32.14285753-32.14285664s-12.85714249-32.14285751-32.14285753-32.14285751h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285751 32.14285751s12.85714249 32.14285751 32.14285751 32.14285665z m385.71428583-122.14285664h64.28571417c19.28571417 0 32.14285751-12.85714249 32.14285751-32.14285751s-12.85714249-32.14285751-32.14285751-32.14285664h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285753 32.14285664s12.85714249 32.14285751 32.14285753 32.14285751z m-385.71428583 0h64.28571415c19.28571417 0 32.14285751-12.85714249 32.14285753-32.14285751s-12.85714249-32.14285751-32.14285753-32.14285664h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285751 32.14285664s12.85714249 32.14285751 32.14285751 32.14285751z m385.71428583 122.14285665h64.28571417c19.28571417 0 32.14285751-12.85714249 32.14285751-32.14285665s-12.85714249-32.14285751-32.14285751-32.14285751h-64.28571415c-19.28571417 0-32.14285751 12.85714249-32.14285753 32.14285751s12.85714249 32.14285751 32.14285753 32.14285665z" p-id="1069"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566035680909" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3601" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M1002.0848 744.672l-33.568 10.368c0.96 7.264 2.144 14.304 2.144 21.76 0 7.328-1.184 14.432-2.368 21.568l33.792 10.56c7.936 2.24 14.496 7.616 18.336 14.752 3.84 7.328 4.672 15.808 1.952 23.552-5.376 16-23.168 24.672-39.936 19.68l-34.176-10.624c-7.136 12.8-15.776 24.672-26.208 35.2l20.8 27.488a28.96 28.96 0 0 1 5.824 22.816 29.696 29.696 0 0 1-12.704 19.616 32.544 32.544 0 0 1-44.416-6.752l-20.8-27.552c-13.696 6.56-28.192 11.2-43.008 13.888v33.632c0 16.736-14.112 30.432-31.648 30.432-17.6 0-31.872-13.696-31.872-30.432v-33.632a167.616 167.616 0 0 1-42.88-13.888l-20.928 27.552c-10.72 13.76-30.08 16.64-44.288 6.752a29.632 29.632 0 0 1-12.704-19.616 29.28 29.28 0 0 1 5.696-22.816l20.896-27.808a166.72 166.72 0 0 1-27.008-34.688l-33.376 10.432c-16.8 5.184-34.56-3.552-39.936-19.616a29.824 29.824 0 0 1 20.224-38.24l33.472-10.432c-0.8-7.264-2.016-14.304-2.016-21.824 0-7.36 1.184-14.496 2.304-21.632l-33.792-10.368c-16.672-5.376-25.632-22.496-20.224-38.432 5.376-16 23.136-24.672 39.936-19.68l34.016 10.752c7.328-12.672 15.84-24.8 26.336-35.328l-20.8-27.552a29.44 29.44 0 0 1 6.944-42.432 32.704 32.704 0 0 1 44.384 6.752l20.832 27.616c13.696-6.432 28.224-11.2 43.104-13.952v-33.568c0-16.736 14.048-30.432 31.648-30.432 17.536 0 31.808 13.568 31.808 30.432v33.504c15.072 2.688 29.344 7.808 42.848 14.016l20.992-27.616a32.48 32.48 0 0 1 44.224-6.752 29.568 29.568 0 0 1 7.136 42.432l-21.024 27.808c10.432 10.432 19.872 21.888 27.04 34.752l33.376-10.432c16.768-5.12 34.56 3.68 39.936 19.68 5.536 15.936-3.712 33.056-20.32 38.304z m-206.016-74.432c-61.344 0-111.136 47.808-111.136 106.56 0 58.88 49.792 106.496 111.136 106.496 61.312 0 111.104-47.616 111.104-106.496 0-58.752-49.792-106.56-111.104-106.56z" p-id="3602"></path><path d="M802.7888 57.152h-76.448c0-22.08-21.024-38.24-42.848-38.24H39.3968a39.68 39.68 0 0 0-39.36 40.032v795.616s41.888 120.192 110.752 120.192H673.2848a227.488 227.488 0 0 1-107.04-97.44H117.6368s-40.608-13.696-40.608-41.248l470.304-0.256 1.664 3.36a227.68 227.68 0 0 1-12.64-73.632c0-60.576 24-118.624 66.88-161.44a228.352 228.352 0 0 1 123.552-63.392l-3.2 0.288 2.144-424.672h38.208l0.576 421.024c27.04 0 52.672 4.8 76.64 13.344V101.536c0.032 0-6.304-44.384-38.368-44.384zM149.7648 514.336H72.3888v-77.408H149.7648v77.408z m0-144.32H72.3888v-77.44H149.7648v77.44z m0-137.248H72.3888v-77.44H149.7648v77.44z m501.856 281.568H206.0848v-77.408h445.536v77.408z m0-144.32H206.0848v-77.44h445.536v77.44z m0-137.248H206.0848v-77.44h445.536v77.44z" p-id="3603"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M71.984 44.815H115.9L71.984 9.642v35.173zM16.094.05h63.875l47.906 38.37v76.74c0 3.392-1.682 6.645-4.677 9.044-2.995 2.399-7.056 3.746-11.292 3.746H16.094c-4.236 0-8.297-1.347-11.292-3.746-2.995-2.399-4.677-5.652-4.677-9.044V12.84C.125 5.742 7.23.05 16.094.05zm71.86 102.32V89.58h-71.86v12.79h71.86zm23.952-25.58V64H16.094v12.79h95.812z"/></svg>
|
||||
|
After Width: | Height: | Size: 418 B |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1569915748289" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3062" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M768.35456 416a256 256 0 1 0-512 0 192 192 0 1 0 0 384v64a256 256 0 0 1-58.88-505.216 320.128 320.128 0 0 1 629.76 0A256.128 256.128 0 0 1 768.35456 864v-64a192 192 0 0 0 0-384z m-512 384h64v64H256.35456v-64z m448 0h64v64h-64v-64z" fill="#333333" p-id="3063"></path><path d="M539.04256 845.248V512.192a32.448 32.448 0 0 0-32-32.192c-17.664 0-32 14.912-32 32.192v333.056l-36.096-36.096a32.192 32.192 0 0 0-45.056 0.192 31.616 31.616 0 0 0-0.192 45.056l90.88 90.944a31.36 31.36 0 0 0 22.528 9.088 30.08 30.08 0 0 0 22.4-9.088l90.88-90.88a32.192 32.192 0 0 0-0.192-45.12 31.616 31.616 0 0 0-45.056-0.192l-36.096 36.096z" fill="#333333" p-id="3064"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M73.137 29.08h-9.209 29.7L63.886.093 34.373 29.08h20.49v27.035H27.238v17.948h27.625v27.133h18.274V74.063h27.41V56.115h-27.41V29.08zm-9.245 98.827l27.518-26.711H36.59l27.302 26.71zM.042 64.982l27.196 27.029V38.167L.042 64.982zm100.505-26.815V92.01l27.41-27.029-27.41-26.815z"/></svg>
|
||||
|
After Width: | Height: | Size: 356 B |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566036347051" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5853" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M832 128H192a64.19 64.19 0 0 0-64 64v640a64.19 64.19 0 0 0 64 64h640a64.19 64.19 0 0 0 64-64V192a64.19 64.19 0 0 0-64-64z m0 703.89l-0.11 0.11H192.11l-0.11-0.11V768h640zM832 544H720L605.6 696.54 442.18 435.07 333.25 544H192v-64h114.75l147.07-147.07L610.4 583.46 688 480h144z m0-288H192v-63.89l0.11-0.11h639.78l0.11 0.11z" p-id="5854"></path></svg>
|
||||
|
After Width: | Height: | Size: 724 B |
|
|
@ -0,0 +1 @@
|
|||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M106.133 67.2a4.797 4.797 0 0 0-4.8 4.8c0 .187.014.36.027.533h-.027V118.4H9.6V26.667h50.133c2.654 0 4.8-2.147 4.8-4.8 0-2.654-2.146-4.8-4.8-4.8H9.6a9.594 9.594 0 0 0-9.6 9.6V118.4c0 5.307 4.293 9.6 9.6 9.6h91.733c5.307 0 9.6-4.293 9.6-9.6V72.533h-.026c.013-.173.026-.346.026-.533 0-2.653-2.146-4.8-4.8-4.8z"/><path d="M125.16 13.373L114.587 2.8c-3.747-3.747-9.854-3.72-13.6.027l-52.96 52.96a4.264 4.264 0 0 0-.907 1.36L33.813 88.533c-.746 1.76-.226 3.534.907 4.68 1.133 1.147 2.92 1.667 4.693.92l31.4-13.293c.507-.213.96-.52 1.36-.907l52.96-52.96c3.747-3.746 3.774-9.853.027-13.6zM66.107 72.4l-18.32 7.76 7.76-18.32L92.72 24.667l10.56 10.56L66.107 72.4zm52.226-52.227l-8.266 8.267-10.56-10.56 8.266-8.267.027-.026 10.56 10.56-.027.026z"/></svg>
|
||||
|
After Width: | Height: | Size: 818 B |
|
|
@ -0,0 +1 @@
|
|||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M88.883 119.565c-7.284 0-19.434 2.495-21.333 8.25v.127c-4.232.13-5.222 0-7.108 0-1.895-5.76-14.045-8.256-21.333-8.256H0V0h42.523c9.179 0 17.109 5.47 21.47 13.551C68.352 5.475 76.295 0 85.478 0H128v119.57l-39.113-.005h-.004zM60.442 24.763c0-9.651-8.978-16.507-17.777-16.507H7.108V111.43H39.11c7.054-.14 18.177.082 21.333 6.12v-4.628c-.134-5.722-.004-13.522 0-13.832V27.413l.004-2.655-.004.005zm60.442-16.517h-35.55c-8.802 0-17.78 6.856-17.78 16.493v74.259c.004.32.138 8.115 0 13.813v4.627c3.155-6.022 14.279-6.26 21.333-6.114h32V8.25l-.003-.005z"/></svg>
|
||||
|
After Width: | Height: | Size: 627 B |
|
|
@ -0,0 +1 @@
|
|||
<svg width="128" height="96" xmlns="http://www.w3.org/2000/svg"><path d="M64.125 56.975L120.188.912A12.476 12.476 0 0 0 115.5 0h-103c-1.588 0-3.113.3-4.513.838l56.138 56.137z"/><path d="M64.125 68.287l-62.3-62.3A12.42 12.42 0 0 0 0 12.5v71C0 90.4 5.6 96 12.5 96h103c6.9 0 12.5-5.6 12.5-12.5v-71a12.47 12.47 0 0 0-1.737-6.35L64.125 68.287z"/></svg>
|
||||
|
After Width: | Height: | Size: 347 B |
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1746590936918" class="icon" viewBox="0 0 1194 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5378" xmlns:xlink="http://www.w3.org/1999/xlink" width="233.203125" height="200"><path d="M1151.9144 325.11999969V89.12a57.04000031 57.04000031 0 0 0-28.8-49.44 58.15999969 58.15999969 0 0 0-57.76000031 0 57.04000031 57.04000031 0 0 0-28.8 49.44v235.99999969c0.24 84.31999969-33.6 152.56000031-94.08 212.00000062-60.07999969 59.83999969-141.84 80.64-227.04 80.4H225.91440031L388.07439969 457.11999969a56.80000031 56.80000031 0 0 0 12.40000031-62.16 57.76000031 57.76000031 0 0 0-94.00000031-18.63999938L48.8744 631.20000031a56.88 56.88 0 0 0 0 80.79999938l264.96 262.56a58.08 58.08 0 0 0 96.55999969-25.59999938 56.80000031 56.80000031 0 0 0-14.95999969-55.2L232.07439969 731.67999969h483.44000062c116.56000031 0 226.15999969-32.08000031 308.64-113.76 82.15999969-80.80000031 128.23999969-178.15999969 127.83999938-292.87999969" p-id="5379"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M96.258 57.462h31.421C124.794 27.323 100.426 2.956 70.287.07v31.422a32.856 32.856 0 0 1 25.971 25.97zm-38.796-25.97V.07C27.323 2.956 2.956 27.323.07 57.462h31.422a32.856 32.856 0 0 1 25.97-25.97zm12.825 64.766v31.421c30.46-2.885 54.507-27.253 57.713-57.712H96.579c-2.886 13.466-13.146 23.726-26.292 26.291zM31.492 70.287H.07c2.886 30.46 27.253 54.507 57.713 57.713V96.579c-13.466-2.886-23.726-13.146-26.291-26.292z"/></svg>
|
||||
|
After Width: | Height: | Size: 497 B |
|
|
@ -0,0 +1 @@
|
|||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M78.208 16.576v8.384h38.72v5.376h-38.72v8.704h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.576h38.72v5.376h-38.72v8.512h38.72v5.376h-38.72v11.136H128v-94.72H78.208zM0 114.368L72.128 128V0L0 13.632v100.736z"/><path d="M28.672 82.56h-11.2l14.784-23.488-14.08-22.592h11.52l8.192 14.976 8.448-14.976h11.136l-14.08 22.208L58.368 82.56H46.656l-8.768-15.68z"/></svg>
|
||||
|
After Width: | Height: | Size: 459 B |