feat: 优化技能编辑页UI布局 - 新增概览卡片、信息卡片重构
重构技能编辑页面为四层结构:技能概览卡片(两行布局)→ 详细信息卡片 → 操作按钮区 → 版本管理 主要改进: - 技能概览卡片:左侧80x80图标,右侧两行结构(技能名称 + 关键指标) - 关键指标图标化:👥 订阅数、📦 当前版本、⭐ 评分 - 详细信息卡片:网格布局展示状态、分类、标签、技能描述、版本说明 - 操作按钮区:独立区域,视觉层次更清晰 - 响应式设计:移动端自适应布局
This commit is contained in:
52
README.md
52
README.md
@@ -813,6 +813,58 @@ pendingUnlistReviews = [{
|
||||
|
||||
#### 技能详情页(SkillEditorPage)
|
||||
|
||||
**优化后的页面结构(两行布局):**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 技能编辑页面优化 │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ 技能概览卡片 (两行结构) │ │
|
||||
│ ├───────────────────────────────────────────────────┤ │
|
||||
│ │ [图标] ┌─────────────────────────────────────┐ │ │
|
||||
│ │ │ 第一行:技能名称 │ │ │
|
||||
│ │ │ 第二行:👥 156 📦 v1.2.0 ⭐ 4.7 │ │ │
|
||||
│ │ └─────────────────────────────────────┘ │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ 详细信息卡片 (普通卡片) │ │
|
||||
│ ├───────────────────────────────────────────────────┤ │
|
||||
│ │ 状态:已上架 │ │
|
||||
│ │ 分类:信息查询 │ │
|
||||
│ │ 标签:天气 查询 生活 │ │
|
||||
│ │ 技能描述:根据城市名称查询当前天气和未来预报... │ │
|
||||
│ │ 版本说明:新增支持未来7天预报 │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ 操作按钮区 │ │
|
||||
│ ├───────────────────────────────────────────────────┤ │
|
||||
│ │ [更新基本信息] [下架技能] [删除技能] │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ 技能包管理区 (保持不变) │ │
|
||||
│ ├───────────────────────────────────────────────────┤ │
|
||||
│ │ [上传新版本] 按钮 │ │
|
||||
│ │ 版本历史表格 │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**关键改进:**
|
||||
1. **两行布局设计**:左侧技能图标,右侧两行信息结构
|
||||
2. **图标化指标**:👥 订阅数、📦 当前版本、⭐ 评分,使用图标库图标
|
||||
3. **信息分层**:
|
||||
- 第一行:技能名称
|
||||
- 第二行:关键指标(带图标)
|
||||
4. **详细信息卡片**:包含状态、分类、标签、技能描述和版本说明
|
||||
5. **优化视觉层次**:四层结构:技能概览 → 详细信息 → 操作按钮 → 版本管理
|
||||
6. **简化交互**:详细信息卡片为普通卡片,不可折叠
|
||||
|
||||
**版本历史表格:**
|
||||
|
||||
| 版本号 | 版本说明 | 状态 | 更新时间 | 操作 |
|
||||
|
||||
@@ -12,4 +12,4 @@ context: |
|
||||
- 不做安全防御性编程,eval/dangerouslySetInnerHTML等按需使用
|
||||
- README.md是项目的开发文档,记录代码结构和关键开发模式,优先读取获取上下文
|
||||
- 涉及页面/路由/组件/功能模块变更或技术栈调整时,同步更新README.md
|
||||
- Git提交: 仅中文; 格式为"类型: 简短描述",类型可选: feat(新功能)/fix(修复)/refactor(重构)/docs(文档)/style(格式)/test(测试)/chore(构建/工具); 多行描述空行后加详细说明
|
||||
- Git提交: 仅中文; 格式为"类型: 简短描述",类型可选: feat(新功能)/fix(修复)/refactor(重构)/docs(文档)/style(格式)/test(测试)/chore(构建/工具); 多行描述空行后加详细说明; 禁创建git操作task
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 基本信息编辑表单
|
||||
UpdateSkillInfoPage SHALL 提供技能基本信息的编辑表单,预填当前数据。
|
||||
UpdateSkillInfoPage SHALL 提供技能基本信息的编辑表单,预填当前数据。表单功能保持不变,但技能详情页的UI布局已更新。
|
||||
|
||||
#### Scenario: 表单预填展示
|
||||
- **WHEN** 用户从技能详情页点击"更新基本信息"进入 UpdateSkillInfoPage
|
||||
@@ -13,11 +13,11 @@ UpdateSkillInfoPage SHALL 提供技能基本信息的编辑表单,预填当前
|
||||
|
||||
#### Scenario: 提交基本信息修改
|
||||
- **WHEN** 用户填写完基本信息后点击"保存修改"按钮
|
||||
- **THEN** 页面展示成功提示"保存成功",并返回技能详情页
|
||||
- **THEN** 页面展示成功提示"保存成功",并返回技能详情页(使用新的UI布局)
|
||||
|
||||
#### Scenario: 取消编辑
|
||||
- **WHEN** 用户在基本信息编辑页面点击"取消"按钮
|
||||
- **THEN** 返回技能详情页,不保存任何修改
|
||||
- **THEN** 返回技能详情页(使用新的UI布局),不保存任何修改
|
||||
|
||||
### Requirement: 技能图标选择
|
||||
UpdateSkillInfoPage 和 UploadSkillPage SHALL 提供技能图标的 emoji 选择器。
|
||||
@@ -31,8 +31,12 @@ UpdateSkillInfoPage 和 UploadSkillPage SHALL 提供技能图标的 emoji 选择
|
||||
- **THEN** 该图标高亮选中,之前的选中项取消高亮
|
||||
|
||||
### Requirement: 技能图标显示
|
||||
技能详情页 SHALL 在头部区域展示技能图标。
|
||||
技能详情页 SHALL 在技能概览卡片中展示技能图标,并采用新的UI布局设计。
|
||||
|
||||
#### Scenario: 图标展示
|
||||
- **WHEN** 用户打开技能详情页
|
||||
- **THEN** 技能头部区域的图标位置显示该技能选择的 emoji 图标
|
||||
- **THEN** 技能概览卡片的图标位置显示该技能选择的 emoji 图标,图标尺寸为80x80像素,圆角16像素
|
||||
|
||||
#### Scenario: 图标背景样式
|
||||
- **WHEN** 用户查看技能概览卡片中的图标
|
||||
- **THEN** 图标具有渐变背景(从#8B5CF6到#EC4899),白色文字,与新的设计系统一致
|
||||
|
||||
35
openspec/specs/skill-info-collapsible-panel/spec.md
Normal file
35
openspec/specs/skill-info-collapsible-panel/spec.md
Normal file
@@ -0,0 +1,35 @@
|
||||
## Purpose
|
||||
技能信息折叠面板用于组织技能详细信息,解决信息重复显示问题,提供更好的信息组织和用户体验。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 技能信息折叠面板
|
||||
技能编辑页面 SHALL 提供可折叠的信息面板,用于组织技能详细信息,解决信息重复显示问题。
|
||||
|
||||
#### Scenario: 折叠面板默认状态
|
||||
- **WHEN** 用户打开技能编辑页面
|
||||
- **THEN** 基本信息折叠面板默认展开,显示技能分类、标签、当前版本、版本说明和技能描述
|
||||
|
||||
#### Scenario: 折叠面板收起/展开交互
|
||||
- **WHEN** 用户点击折叠面板的"收起"按钮
|
||||
- **THEN** 面板内容隐藏,按钮文字变为"展开"
|
||||
|
||||
#### Scenario: 折叠面板展开交互
|
||||
- **WHEN** 用户点击折叠面板的"展开"按钮
|
||||
- **THEN** 面板内容显示,按钮文字变为"收起"
|
||||
|
||||
#### Scenario: 信息网格布局
|
||||
- **WHEN** 用户查看展开的折叠面板
|
||||
- **THEN** 信息以网格布局展示,包含分类、标签、当前版本、版本说明和技能描述字段
|
||||
|
||||
#### Scenario: 标签显示格式
|
||||
- **WHEN** 用户查看折叠面板中的标签字段
|
||||
- **THEN** 标签以圆角标签样式显示,多个标签之间用空格分隔
|
||||
|
||||
#### Scenario: 版本说明显示
|
||||
- **WHEN** 用户查看折叠面板中的版本说明字段
|
||||
- **THEN** 显示当前版本的说明文字
|
||||
|
||||
#### Scenario: 技能描述显示
|
||||
- **WHEN** 用户查看折叠面板中的技能描述字段
|
||||
- **THEN** 显示完整的技能描述文字,支持多行显示
|
||||
35
openspec/specs/skill-overview-card/spec.md
Normal file
35
openspec/specs/skill-overview-card/spec.md
Normal file
@@ -0,0 +1,35 @@
|
||||
## Purpose
|
||||
技能概览卡片用于在技能编辑页面顶部集中展示技能核心信息,提供更好的信息组织和视觉体验。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 技能概览卡片
|
||||
技能编辑页面 SHALL 在页面顶部显示技能概览卡片,集中展示技能核心信息。
|
||||
|
||||
#### Scenario: 卡片布局结构
|
||||
- **WHEN** 用户打开技能编辑页面
|
||||
- **THEN** 页面顶部显示技能概览卡片,包含技能图标、名称、状态标签和关键指标
|
||||
|
||||
#### Scenario: 技能图标显示
|
||||
- **WHEN** 用户查看技能概览卡片
|
||||
- **THEN** 卡片左侧显示技能图标,图标尺寸为80x80像素,圆角16像素
|
||||
|
||||
#### Scenario: 技能名称显示
|
||||
- **WHEN** 用户查看技能概览卡片
|
||||
- **THEN** 卡片右侧顶部显示技能名称,字体大小为24px,字体加粗
|
||||
|
||||
#### Scenario: 状态标签显示
|
||||
- **WHEN** 用户查看技能概览卡片
|
||||
- **THEN** 技能名称下方显示状态标签,标签样式与现有状态标签系统保持一致
|
||||
|
||||
#### Scenario: 关键指标显示
|
||||
- **WHEN** 用户查看技能概览卡片
|
||||
- **THEN** 状态标签右侧显示关键指标,包括订阅数和评分,指标之间用分隔符分隔
|
||||
|
||||
#### Scenario: 卡片视觉样式
|
||||
- **WHEN** 用户查看技能概览卡片
|
||||
- **THEN** 卡片具有白色背景、圆角12像素、轻微阴影效果,与页面其他卡片样式一致
|
||||
|
||||
#### Scenario: 响应式布局
|
||||
- **WHEN** 用户在较小屏幕设备上查看技能概览卡片
|
||||
- **THEN** 卡片内容自动调整布局,确保信息清晰可读
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { FiChevronLeft, FiUpload } from 'react-icons/fi';
|
||||
import { FiChevronLeft, FiUpload, FiUsers, FiPackage, FiStar } from 'react-icons/fi';
|
||||
import { api } from '../../services/api.js';
|
||||
import Modal from '../../components/common/Modal.jsx';
|
||||
import Toast from '../../components/common/Toast.jsx';
|
||||
@@ -43,70 +43,93 @@ function SkillEditorPage({ skillId, onBack, onUploadNewVersion, onUpdateInfo })
|
||||
setToast({ visible: true, type: 'success', message: '已删除' });
|
||||
};
|
||||
|
||||
const currentVersion = skill.versions && skill.versions.length > 0
|
||||
? skill.versions.find(v => v.status === 'approved') || skill.versions[0]
|
||||
: null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="dev-back-btn" onClick={onBack}>
|
||||
<FiChevronLeft /> 返回我的技能
|
||||
</div>
|
||||
<div className="card">
|
||||
|
||||
{/* 1. 技能概览卡片(两行结构) */}
|
||||
<div className="skill-overview-card">
|
||||
<div className="skill-icon">{skill.icon || skill.name.charAt(0)}</div>
|
||||
<div className="skill-header">
|
||||
{/* 第一行:技能名称 */}
|
||||
<h2 className="skill-name">{skill.name}</h2>
|
||||
|
||||
{/* 第二行:关键指标(带图标) */}
|
||||
<div className="skill-metrics-row">
|
||||
<div className="metric-item">
|
||||
<FiUsers className="metric-icon" />
|
||||
<span className="metric-value">{skill.installs || 0}</span>
|
||||
</div>
|
||||
<div className="metric-item">
|
||||
<FiPackage className="metric-icon" />
|
||||
<span className="metric-value">{skill.version || 'v1.0.0'}</span>
|
||||
</div>
|
||||
<div className="metric-item">
|
||||
<FiStar className="metric-icon" />
|
||||
<span className="metric-value">{skill.rating || 0}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 2. 详细信息卡片(普通卡片,不可折叠) */}
|
||||
<div className="info-card">
|
||||
<div className="card-header">
|
||||
<div className="card-title">配置信息</div>
|
||||
<h3>详细信息</h3>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<div className="dev-detail-header">
|
||||
<div className="dev-detail-icon">{skill.icon || skill.name.charAt(0)}</div>
|
||||
<div className="dev-detail-main">
|
||||
<h2 style={{ marginBottom: '8px' }}>{skill.name}</h2>
|
||||
<div style={{ color: '#64748B', marginBottom: '12px' }}>{skill.category}</div>
|
||||
<div className="dev-detail-tags">
|
||||
<div className="info-grid">
|
||||
<div className="info-item">
|
||||
<label>状态</label>
|
||||
<span className={`status ${skillStatusMap[skill.status]?.className || 'status-stopped'}`}>
|
||||
{skillStatusMap[skill.status]?.text || skill.status}
|
||||
</span>
|
||||
</div>
|
||||
<div className="info-item">
|
||||
<label>分类</label>
|
||||
<span>{skill.category}</span>
|
||||
</div>
|
||||
<div className="info-item full-width">
|
||||
<label>标签</label>
|
||||
<div className="tags">
|
||||
{skill.tags.map(tag => (
|
||||
<span key={tag} className="dev-detail-tag">{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="dev-detail-stats">
|
||||
<span>当前版本: {skill.version}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dev-detail-section">
|
||||
<h3>基本信息</h3>
|
||||
<div className="dev-info-row">
|
||||
<span className="dev-info-label">技能名称</span>
|
||||
<span className="dev-info-value">{skill.name}</span>
|
||||
<div className="info-item full-width">
|
||||
<label>技能描述</label>
|
||||
<p>{skill.desc}</p>
|
||||
</div>
|
||||
<div className="dev-info-row">
|
||||
<span className="dev-info-label">技能描述</span>
|
||||
<span className="dev-info-value">{skill.desc}</span>
|
||||
<div className="info-item full-width">
|
||||
<label>版本说明</label>
|
||||
<span>{currentVersion?.desc || '暂无版本说明'}</span>
|
||||
</div>
|
||||
<div className="dev-info-row">
|
||||
<span className="dev-info-label">技能分类</span>
|
||||
<span className="dev-info-value">{skill.category}</span>
|
||||
</div>
|
||||
<div className="dev-info-row">
|
||||
<span className="dev-info-label">技能标签</span>
|
||||
<span className="dev-info-value">
|
||||
{skill.tags.map(tag => (
|
||||
<span key={tag} className="dev-detail-tag" style={{ marginRight: '6px' }}>{tag}</span>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '12px', marginBottom: '24px' }}>
|
||||
<button className="btn btn-primary" onClick={() => onUpdateInfo && onUpdateInfo(skill.id)}>更新基本信息</button>
|
||||
{skill.status === 'published' && (
|
||||
<button className="btn btn-danger" onClick={handleTogglePublish}>下架技能</button>
|
||||
)}
|
||||
<button
|
||||
className="btn btn-danger"
|
||||
onClick={() => setDeleteSkillModal(true)}
|
||||
disabled={skill.status === 'published'}
|
||||
title={skill.status === 'published' ? '已上架的技能需要先下架才能删除' : ''}
|
||||
>
|
||||
删除技能
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 3. 操作按钮区 */}
|
||||
<div className="action-buttons">
|
||||
<button className="btn btn-primary" onClick={() => onUpdateInfo && onUpdateInfo(skill.id)}>更新基本信息</button>
|
||||
{skill.status === 'published' && (
|
||||
<button className="btn btn-danger" onClick={handleTogglePublish}>下架技能</button>
|
||||
)}
|
||||
<button
|
||||
className="btn btn-danger"
|
||||
onClick={() => setDeleteSkillModal(true)}
|
||||
disabled={skill.status === 'published'}
|
||||
title={skill.status === 'published' ? '已上架的技能需要先下架才能删除' : ''}
|
||||
>
|
||||
删除技能
|
||||
</button>
|
||||
</div>
|
||||
<div className="card" style={{ marginTop: '24px' }}>
|
||||
<div className="card-header">
|
||||
<div className="card-title">技能包管理</div>
|
||||
|
||||
@@ -132,3 +132,174 @@
|
||||
background: #FEF2F2;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* ============ 技能编辑页面优化样式 ============ */
|
||||
|
||||
/* 技能概览卡片(两行结构) */
|
||||
.skill-overview-card {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 24px;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 16px;
|
||||
|
||||
align-items: center;
|
||||
|
||||
.skill-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, #8B5CF6 0%, #EC4899 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 36px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.skill-header {
|
||||
flex: 1;
|
||||
|
||||
/* 第一行:技能名称 */
|
||||
.skill-name {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #1E293B;
|
||||
}
|
||||
|
||||
/* 第二行:关键指标 */
|
||||
.skill-metrics-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
|
||||
.metric-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
.metric-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
opacity: 0.7;
|
||||
color: #64748B;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #1E293B;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 详细信息卡片(普通卡片) */
|
||||
.info-card {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
|
||||
.card-header {
|
||||
padding: 16px 24px;
|
||||
border-bottom: 1px solid #E2E8F0;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1E293B;
|
||||
}
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 24px;
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
|
||||
.info-item {
|
||||
&.full-width {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #64748B;
|
||||
margin-bottom: 6px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
span, p {
|
||||
font-size: 14px;
|
||||
color: #1E293B;
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 操作按钮区 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.skill-overview-card {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
|
||||
.skill-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.skill-header {
|
||||
.skill-name {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.skill-metrics-row {
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user