+
}
+ label="总览"
+ active={currentPage === 'overview'}
+ onClick={() => switchPage('overview')}
+ />
}
label="我的技能"
@@ -153,4 +178,4 @@ function DeveloperPage() {
);
}
-export default DeveloperPage;
\ No newline at end of file
+export default DeveloperPage;
diff --git a/src/pages/developer/DevOverviewPage.jsx b/src/pages/developer/DevOverviewPage.jsx
new file mode 100644
index 0000000..003708d
--- /dev/null
+++ b/src/pages/developer/DevOverviewPage.jsx
@@ -0,0 +1,91 @@
+import { FiAlertTriangle, FiInfo } from 'react-icons/fi';
+import { api } from '../../services/api.js';
+
+function DevOverviewPage({ onSkillClick }) {
+ const data = api.developer.getOverview();
+
+ return (
+ <>
+
+
+
我的技能
+
{data.totalSkills}
+
+
+
已发布
+
{data.publishedCount}
+
+
+
草稿
+
{data.draftCount}
+
+
+
待审核
+
{data.pendingReview}
+
+
+
+
+
+
+
+ {data.pendingItems.map((item, index) => (
+
onSkillClick && onSkillClick(item.skillId)}
+ >
+
+ {item.status === 'rejected' ? : }
+
+
+ {item.skillName} {item.version}
+ {item.date}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+ | 时间 |
+ 操作 |
+ 状态 |
+
+
+
+ {data.recentActivity.map((item, index) => (
+
+ | {item.time} |
+ {item.action} |
+
+
+ {item.status}
+
+ |
+
+ ))}
+
+
+
+
+
+ >
+ );
+}
+
+export default DevOverviewPage;
diff --git a/src/pages/developer/MySkillsPage.jsx b/src/pages/developer/MySkillsPage.jsx
index 61eff19..e1248e2 100644
--- a/src/pages/developer/MySkillsPage.jsx
+++ b/src/pages/developer/MySkillsPage.jsx
@@ -1,50 +1,175 @@
-import { mySkills } from '../../data/developerData.js';
+import { useState } from 'react';
+import { api } from '../../services/api.js';
+import Modal from '../../components/common/Modal.jsx';
+import Toast from '../../components/common/Toast.jsx';
function MySkillsPage({ onSkillClick }) {
+ const sourceData = api.developer.getMySkills();
+ const categories = api.developer.getCategories();
+ const [filters, setFilters] = useState({ keyword: '', category: '', status: '' });
+ const [deleteTarget, setDeleteTarget] = useState(null);
+ const [toast, setToast] = useState({ visible: false, type: 'success', message: '' });
+
+ const handleFilterChange = (key, value) => {
+ setFilters(prev => ({ ...prev, [key]: value }));
+ };
+
+ const handleReset = () => {
+ setFilters({ keyword: '', category: '', status: '' });
+ };
+
+ const filteredList = sourceData.filter(skill => {
+ if (filters.keyword && !skill.name.includes(filters.keyword) && !skill.desc.includes(filters.keyword)) {
+ return false;
+ }
+ if (filters.category && skill.category !== filters.category) {
+ return false;
+ }
+ if (filters.status === 'published' && skill.status !== 'published') return false;
+ if (filters.status === 'draft' && skill.status !== 'draft') return false;
+ return true;
+ });
+
+ const handleUnpublish = (e, skill) => {
+ e.stopPropagation();
+ setToast({ visible: true, type: 'success', message: '已下架' });
+ };
+
+ const handleDelete = (e, skill) => {
+ e.stopPropagation();
+ setDeleteTarget(skill);
+ };
+
+ const confirmDelete = () => {
+ setDeleteTarget(null);
+ setToast({ visible: true, type: 'success', message: '已删除' });
+ };
+
return (
-
-
-
我的技能
+ <>
+
+
+
+
+
+ handleFilterChange('keyword', e.target.value)}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
- | 技能名称 |
- 分类 |
- 版本 |
- 状态 |
- 安装量 |
- 评分 |
- 操作 |
-
-
-
- {mySkills.map(skill => (
- onSkillClick(skill.id)} style={{ cursor: 'pointer' }}>
- |
- {skill.name}
- {skill.desc}
- |
- {skill.category} |
- {skill.version} |
-
- {skill.status === 'published' ? '已发布' : '草稿'}
- |
- {skill.installs} |
- {skill.rating || '-'} |
-
-
- |
-
- ))}
-
-
+
+
+
+
+
+
+
+ | 技能名称 |
+ 分类 |
+ 版本 |
+ 状态 |
+ 安装量 |
+ 评分 |
+ 操作 |
+
+
+
+ {filteredList.map(skill => (
+ onSkillClick(skill.id)} style={{ cursor: 'pointer' }}>
+ |
+ {skill.name}
+ {skill.desc}
+ |
+ {skill.category} |
+ {skill.version} |
+
+
+ {skill.status === 'published' ? '已发布' : '草稿'}
+
+ |
+ {skill.installs} |
+ {skill.rating || '-'} |
+
+
+
+ {skill.status === 'published' && (
+
+ )}
+
+
+ |
+
+ ))}
+
+
+
+
+
-
+
setDeleteTarget(null)}
+ confirmText="删除"
+ >
+ 确定要删除技能"{deleteTarget?.name}"吗?此操作不可撤销。
+
+
setToast(prev => ({ ...prev, visible: false }))}
+ />
+ >
);
}
-export default MySkillsPage;
\ No newline at end of file
+export default MySkillsPage;
diff --git a/src/pages/developer/NewVersionPage.jsx b/src/pages/developer/NewVersionPage.jsx
index c24a11f..d36f9b2 100644
--- a/src/pages/developer/NewVersionPage.jsx
+++ b/src/pages/developer/NewVersionPage.jsx
@@ -1,42 +1,56 @@
-import { FiUpload } from 'react-icons/fi';
+import { useState } from 'react';
+import { FiUpload, FiChevronLeft } from 'react-icons/fi';
+import Toast from '../../components/common/Toast.jsx';
function NewVersionPage({ skillName, onBack }) {
+ const [showToast, setShowToast] = useState(false);
+
+ const handleSubmit = () => {
+ setShowToast(true);
+ setTimeout(() => {
+ onBack();
+ }, 1000);
+ };
+
return (
-
-
-
上传新版本
+ <>
+
+ 返回技能详情
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
点击或拖拽文件到此处上传
-
支持 .zip 格式
+
+
+
+
+
+
+
+
+
+
点击或拖拽文件到此处上传
+
支持 .zip 格式
+
+
+
+
+
-
-
-
-
-
+
setShowToast(false)}
+ />
+ >
);
}
diff --git a/src/pages/developer/SkillEditorPage.jsx b/src/pages/developer/SkillEditorPage.jsx
index 5ffc401..d225c39 100644
--- a/src/pages/developer/SkillEditorPage.jsx
+++ b/src/pages/developer/SkillEditorPage.jsx
@@ -1,12 +1,34 @@
-import { FiChevronLeft, FiUpload, FiDownload } from 'react-icons/fi';
-import { mySkills } from '../../data/developerData.js';
+import { useState } from 'react';
+import { FiChevronLeft, FiUpload } from 'react-icons/fi';
+import { api } from '../../services/api.js';
+import Modal from '../../components/common/Modal.jsx';
+import Toast from '../../components/common/Toast.jsx';
+
+function SkillEditorPage({ skillId, onBack, onUploadNewVersion, onUpdateInfo }) {
+ const skill = api.developer.getSkillById(skillId);
+ const [deleteSkillModal, setDeleteSkillModal] = useState(false);
+ const [deleteVersionTarget, setDeleteVersionTarget] = useState(null);
+ const [toast, setToast] = useState({ visible: false, type: 'success', message: '' });
-function SkillEditorPage({ skillId, onBack, onUploadNewVersion }) {
- const skill = mySkills.find(s => s.id === skillId);
if (!skill) {
return Skill not found
;
}
+ const handleTogglePublish = () => {
+ const msg = skill.status === 'published' ? '已下架' : '已上架';
+ setToast({ visible: true, type: 'success', message: msg });
+ };
+
+ const handleDeleteSkill = () => {
+ setDeleteSkillModal(false);
+ setToast({ visible: true, type: 'success', message: '已删除' });
+ };
+
+ const handleDeleteVersion = () => {
+ setDeleteVersionTarget(null);
+ setToast({ visible: true, type: 'success', message: '已删除' });
+ };
+
return (
<>
@@ -18,7 +40,7 @@ function SkillEditorPage({ skillId, onBack, onUploadNewVersion }) {
-
{skill.name.charAt(0)}
+
{skill.icon || skill.name.charAt(0)}
{skill.name}
{skill.category}
@@ -44,6 +66,25 @@ function SkillEditorPage({ skillId, onBack, onUploadNewVersion }) {
技能描述
{skill.desc}
+
+ 技能分类
+ {skill.category}
+
+
+ 技能标签
+
+ {skill.tags.map(tag => (
+ {tag}
+ ))}
+
+
+
+
+
+
+
@@ -64,7 +105,7 @@ function SkillEditorPage({ skillId, onBack, onUploadNewVersion }) {
-
+
@@ -77,10 +118,15 @@ function SkillEditorPage({ skillId, onBack, onUploadNewVersion }) {
- {skill.versions.map(ver => (
-
+ {skill.versions.map((ver, index) => (
+
| {ver.version} |
- {ver.desc} |
+
+ {ver.desc}
+ {ver.status === 'rejected' && ver.rejectionReason && (
+ {ver.rejectionReason}
+ )}
+ |
{ver.status === 'pending' ? (
审核中
@@ -102,6 +148,11 @@ function SkillEditorPage({ skillId, onBack, onUploadNewVersion }) {
{!ver.enabled && }
+ {!ver.enabled && (
+
+ )}
|
@@ -111,8 +162,32 @@ function SkillEditorPage({ skillId, onBack, onUploadNewVersion }) {
+ setDeleteSkillModal(false)}
+ confirmText="删除"
+ >
+ 确定要删除技能"{skill.name}"吗?此操作不可撤销。
+
+ setDeleteVersionTarget(null)}
+ confirmText="删除"
+ >
+ 确定要删除此版本吗?此操作不可撤销。
+
+ setToast(prev => ({ ...prev, visible: false }))}
+ />
>
);
}
-export default SkillEditorPage;
\ No newline at end of file
+export default SkillEditorPage;
diff --git a/src/pages/developer/UpdateSkillInfoPage.jsx b/src/pages/developer/UpdateSkillInfoPage.jsx
new file mode 100644
index 0000000..a9884d4
--- /dev/null
+++ b/src/pages/developer/UpdateSkillInfoPage.jsx
@@ -0,0 +1,132 @@
+import { FiX, FiChevronLeft } from 'react-icons/fi';
+import { useState } from 'react';
+import { api } from '../../services/api.js';
+import Toast from '../../components/common/Toast.jsx';
+
+const ICON_OPTIONS = ['🌤️', '📊', '📝', '🔧', '💻', '📋', '🔍', '📈', '🎯', '⚡', '🌐', '🤖'];
+
+function UpdateSkillInfoPage({ skill, onBack }) {
+ const categories = api.developer.getCategories();
+ const [name, setName] = useState(skill?.name || '');
+ const [desc, setDesc] = useState(skill?.desc || '');
+ const [category, setCategory] = useState(skill?.category || categories[0]);
+ const [tags, setTags] = useState(skill?.tags || []);
+ const [tagInput, setTagInput] = useState('');
+ const [icon, setIcon] = useState(skill?.icon || ICON_OPTIONS[0]);
+ const [showToast, setShowToast] = useState(false);
+
+ const handleTagKeyDown = (e) => {
+ if (e.key === 'Enter' && tagInput.trim()) {
+ e.preventDefault();
+ if (!tags.includes(tagInput.trim()) && tags.length < 5) {
+ setTags([...tags, tagInput.trim()]);
+ }
+ setTagInput('');
+ }
+ };
+
+ const removeTag = (tagToRemove) => {
+ setTags(tags.filter(tag => tag !== tagToRemove));
+ };
+
+ const handleSave = () => {
+ setShowToast(true);
+ setTimeout(() => {
+ onBack();
+ }, 1000);
+ };
+
+ return (
+ <>
+
+ 返回技能详情
+
+
+
+
+
+
+ setName(e.target.value)}
+ />
+
+
+
+
+
+
+
+
+
+
+
+ {tags.map(tag => (
+
+ {tag}
+ removeTag(tag)}>
+
+ ))}
+ setTagInput(e.target.value)}
+ onKeyDown={handleTagKeyDown}
+ />
+
+
按回车添加标签,最多5个
+
+
+
+
+ {ICON_OPTIONS.map(emoji => (
+
setIcon(emoji)}
+ >
+ {emoji}
+
+ ))}
+
+
当前选择: {icon}
+
+
+
+
+
+
+
+ setShowToast(false)}
+ />
+ >
+ );
+}
+
+export default UpdateSkillInfoPage;
diff --git a/src/pages/developer/UploadSkillPage.jsx b/src/pages/developer/UploadSkillPage.jsx
index 5f23970..75bce74 100644
--- a/src/pages/developer/UploadSkillPage.jsx
+++ b/src/pages/developer/UploadSkillPage.jsx
@@ -1,14 +1,21 @@
import { FiUpload, FiX } from 'react-icons/fi';
import { useState } from 'react';
+import { api } from '../../services/api.js';
+import Toast from '../../components/common/Toast.jsx';
-function UploadSkillPage() {
+const ICON_OPTIONS = ['🌤️', '📊', '📝', '🔧', '💻', '📋', '🔍', '📈', '🎯', '⚡', '🌐', '🤖'];
+
+function UploadSkillPage({ onBack }) {
+ const categories = api.developer.getCategories();
const [tags, setTags] = useState([]);
const [tagInput, setTagInput] = useState('');
+ const [icon, setIcon] = useState(ICON_OPTIONS[0]);
+ const [showToast, setShowToast] = useState(false);
const handleTagKeyDown = (e) => {
if (e.key === 'Enter' && tagInput.trim()) {
e.preventDefault();
- if (!tags.includes(tagInput.trim())) {
+ if (!tags.includes(tagInput.trim()) && tags.length < 5) {
setTags([...tags, tagInput.trim()]);
}
setTagInput('');
@@ -19,6 +26,10 @@ function UploadSkillPage() {
setTags(tags.filter(tag => tag !== tagToRemove));
};
+ const handleCreate = () => {
+ setShowToast(true);
+ };
+
return (
@@ -27,18 +38,18 @@ function UploadSkillPage() {
+
+
+
+ {ICON_OPTIONS.map(emoji => (
+
setIcon(emoji)}
+ >
+ {emoji}
+
+ ))}
+
+
当前选择: {icon}
+
@@ -70,12 +96,18 @@ function UploadSkillPage() {
-
-
+
+
+
setShowToast(false)}
+ />
);
}
-export default UploadSkillPage;
\ No newline at end of file
+export default UploadSkillPage;
diff --git a/src/services/api.js b/src/services/api.js
index 75fb280..136383f 100644
--- a/src/services/api.js
+++ b/src/services/api.js
@@ -7,7 +7,7 @@
import { conversations, getChatScenes } from '../data/conversations.js';
import { skills, skillFiles, skillVersions, getSkillIcon } from '../data/skills.js';
import { logs } from '../data/logs.js';
-import { mySkills, skillCategories, supportedModels, devDocs } from '../data/developerData.js';
+import { mySkills, skillCategories, devDocs, developerOverview } from '../data/developerData.js';
import { projectMembers } from '../data/members.js';
import { scheduledTasks } from '../data/tasks.js';
import { adminDepartments, adminUsers, adminProjects, adminOverview, adminLogs } from '../data/adminData.js';
@@ -137,10 +137,10 @@ export const developerApi = {
getCategories: () => skillCategories,
/**
- * 获取支持的模型列表
- * @returns {Array} 模型列表
+ * 获取开发者总览数据
+ * @returns {Object} 总览数据
*/
- getSupportedModels: () => supportedModels,
+ getOverview: () => developerOverview,
/**
* 获取开发文档列表
diff --git a/src/styles/pages/_developer.scss b/src/styles/pages/_developer.scss
index be85e76..87dfa89 100644
--- a/src/styles/pages/_developer.scss
+++ b/src/styles/pages/_developer.scss
@@ -89,3 +89,46 @@
font-weight: 600;
margin-bottom: 16px;
}
+
+/* 技能图标选择器 */
+.dev-icon-picker {
+ display: grid;
+ grid-template-columns: repeat(6, 1fr);
+ gap: 8px;
+ max-width: 360px;
+}
+
+.dev-icon-option {
+ width: 48px;
+ height: 48px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 24px;
+ border: 2px solid #E2E8F0;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.2s;
+ background: #fff;
+
+ &:hover {
+ border-color: #3B82F6;
+ background: #EFF6FF;
+ }
+
+ &.selected {
+ border-color: #3B82F6;
+ background: #EFF6FF;
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
+ }
+}
+
+/* 版本拒绝原因 */
+.dev-rejection-reason {
+ font-size: 12px;
+ color: #EF4444;
+ margin-top: 4px;
+ padding: 4px 8px;
+ background: #FEF2F2;
+ border-radius: 4px;
+}