| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- import * as React from 'react';
- import { Dropdown, MenuProps, Button } from 'antd';
- import { Code, Pen, Trash2, Share2, Star, Eye, Heart, Calendar, BadgeCheck, Building, Menu, ArrowRight } from 'lucide-react';
- import AppCardApiDoc from './AppCardApiDoc';
- import './index.scss';
- export interface AppCardProps {
- // 基本信息
- id: string;
- name: string;
- description: string;
- icon?: string;
- iconImage?: string;
- iconBgColor?: 'blue' | 'indigo' | 'teal' | 'purple' | 'rose' | 'cyan' | 'amber' | 'green' | 'orange';
-
- // 创建者信息
- creator?: string;
- creatorId?: string;
- proName?: string; // 项目名称
-
- // 标签和认证
- tags?: Array<{
- label: string;
- color: 'slate' | 'blue' | 'indigo' | 'teal' | 'purple' | 'rose' | 'cyan' | 'amber';
- }>;
- certification?: string;
-
- // 状态标识
- isHot?: boolean;
- status?: 'draft' | 'pending' | 'approved' | 'rejected';
-
- // 统计数据
- viewCount?: number;
- favoriteCount?: number;
- createTime?: string;
-
- // 其他信息
- department?: string;
- visible?: 'public' | 'private' | 'vip';
- appId?: string;
- fieldId?: string; // 字段 ID
- isCollect?: boolean;
- isCreator?: boolean;
- isMaintainer?: boolean; // 是否为维护者(非创建者)
- // 显示控制
- showCreator?: boolean;
- showActions?: boolean;
- showCertification?: boolean;
- showTags?: boolean;
- showHot?: boolean;
- showViewCount?: boolean;
- showFavoriteCount?: boolean;
- showCreateTime?: boolean;
- showStatus?: boolean;
- showDepartment?: boolean;
- showVisible?: boolean;
- showFieldId?: boolean; // 是否显示字段 ID
- showOperations?: boolean;
- // 回调函数
- onPlay?: () => void;
- onShare?: () => void;
- onFavorite?: () => void;
- onEdit?: () => void;
- onDelete?: () => void;
- onView?: () => void;
- onApi?: () => void;
- onApiDirect?: () => void; // API 调用(非创建者显示)
- }
- const AppCard: React.FC<AppCardProps> = (props) => {
- const {
- // 基本信息
- id,
- name,
- description,
- icon,
- iconImage,
- iconBgColor = 'blue',
- // 创建者信息
- creator,
- proName,
- // 标签和认证
- tags = [],
- certification,
- // 状态标识
- isHot,
- status,
- // 统计数据
- viewCount,
- favoriteCount,
- createTime,
- // 其他信息
- department,
- visible,
- appId,
- fieldId,
- isCollect = false,
- isCreator = false,
- isMaintainer = false,
- // 显示控制
- showCreator = true,
- showActions = true,
- showCertification = true,
- showTags = true,
- showHot = true,
- showViewCount = false,
- showFavoriteCount = false,
- showCreateTime = false,
- showStatus = false,
- showDepartment = false,
- showVisible = false,
- showFieldId = false,
- showOperations = false,
- // 回调函数
- onShare,
- onFavorite,
- onEdit,
- onDelete,
- onView,
- onApi,
- onApiDirect,
- } = props;
- // API 文档弹窗状态
- const [apiDocOpen, setApiDocOpen] = React.useState(false);
- // 使用 appId 或 id 作为应用标识
- const applicationId = appId || id;
- // 配置对象
- const statusConfig = {
- draft: { label: '草稿', color: '#9CA3AF' },
- pending: { label: '审核中', color: '#F59E0B' },
- approved: { label: '已通过', color: '#10B981' },
- rejected: { label: '已拒绝', color: '#EF4444' },
- };
- const visibleConfig = {
- public: { label: '公开', color: '#005D80' },
- private: { label: '私有', color: '#6B7280' },
- vip: { label: 'VIP', color: '#F59E0B' },
- };
- // 构建操作菜单项
- const operationItems: MenuProps['items'] = React.useMemo(() => {
- const items: any[] = [];
- if (onApi) {
- items.push({
- key: 'api',
- label: 'API 调用',
- icon: <Code size={16} />,
- onClick: () => onApi(),
- });
- }
- // 创建者或维护者都可以编辑
- if ((isCreator || isMaintainer) && onEdit) {
- items.push({
- key: 'edit',
- label: '编辑',
- icon: <Pen size={16} />,
- onClick: () => onEdit(),
- });
- }
- // 只有创建者可以删除
- if (isCreator && onDelete) {
- items.push({
- key: 'delete',
- label: '删除',
- icon: <Trash2 size={16} />,
- danger: true,
- onClick: () => onDelete(),
- });
- }
- return items;
- }, [onApi, onEdit, onDelete, isCreator, isMaintainer]);
- // 事件处理
- const handleShareClick = () => {
- onShare?.();
- };
- const handleFavoriteClick = () => {
- onFavorite?.();
- };
- const handleViewClick = () => {
- onView?.();
- };
- return (
- <div className='app-card'>
- {/* Hot 标签 */}
- {showHot && isHot && (
- <span className='card-hot-badge'>Hot</span>
- )}
- {/* 状态标签 */}
- {showStatus && status && (
- <span
- className='card-status-badge'
- style={{ background: statusConfig[status].color }}
- >
- {statusConfig[status].label}
- </span>
- )}
- {/* 操作按钮 - 悬停显示 */}
- {showActions && (
- <div className='card-actions'>
- <button
- className='card-action-btn'
- onClick={(e) => {
- e.stopPropagation();
- handleShareClick();
- }}
- title='分享'
- >
- <Share2 size={18} />
- </button>
- <button
- className='card-action-btn'
- onClick={(e) => {
- e.stopPropagation();
- handleFavoriteClick();
- }}
- title={isCollect ? '取消收藏' : '收藏'}
- >
- {isCollect ? (
- <Star size={18} className="fill-yellow" style={{ color: '#F5E663' }} />
- ) : (
- <Star size={18} />
- )}
- </button>
- </div>
- )}
- {/* 卡片头部:图标 + 创建者信息 */}
- <div className='card-header'>
- <div className={`card-icon-wrapper bg-${iconBgColor}`}>
- {iconImage ? (
- <img alt={name} className='card-icon' src={iconImage} />
- ) : icon ? (
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 24, height: 24 }}>
- {/* 动态 icon 支持 - 如果传入的是 iconoir 组件名称则渲染,否则显示占位 */}
- <Star size={24} />
- </div>
- ) : null}
- </div>
- {showCreator && creator && (
- <div className='card-creator'>
- <div className='card-creator-info'>
- <span className='card-creator-label'>创建者</span>
- <span className='card-creator-name'>{creator}</span>
- {proName && (
- <span className='card-pro-name' title={proName}>{proName}</span>
- )}
- </div>
- </div>
- )}
- </div>
- {/* 应用名称 */}
- <h5 className='card-title'>{name}</h5>
- {/* 字段 ID */}
- {showFieldId && fieldId && (
- <div className='card-field-id'>ID: {fieldId}</div>
- )}
- {/* 应用描述 */}
- <p className='card-description'>{description}</p>
- {/* 标签 */}
- {showTags && tags.length > 0 && (
- <div className='card-tags'>
- {tags.map((tag, index) => (
- <span key={index} className={`card-tag tag-${tag.color}`}>
- {tag.label}
- </span>
- ))}
- </div>
- )}
- {/* 统计信息 */}
- {(showViewCount || showFavoriteCount || showCreateTime) && (
- <div className='card-meta'>
- <div className='card-meta-left'>
- {showViewCount && viewCount !== undefined && (
- <span className='card-meta-item'>
- <Eye size={14} />
- <span>{viewCount}</span>
- </span>
- )}
- {showFavoriteCount && favoriteCount !== undefined && (
- <span className='card-meta-item'>
- <Heart size={14} />
- <span>{favoriteCount}</span>
- </span>
- )}
- </div>
- {showCreateTime && createTime && (
- <div className='card-meta-right'>
- <span className='card-meta-item'>
- <Calendar size={14} />
- <span>{createTime}</span>
- </span>
- </div>
- )}
- </div>
- )}
- {/* 底部信息:认证/部门/可见性 - 至少有一个字段才渲染 */}
- {showCertification && (certification || department || visible) && (
- <div className='card-footer-info'>
- {certification && (
- <div className='card-certification'>
- <BadgeCheck size={14} />
- <span>{certification}</span>
- </div>
- )}
- {showDepartment && department && (
- <div className='card-department'>
- <Building size={14} />
- <span>{department}</span>
- </div>
- )}
- {showVisible && visible && (
- <span
- className='card-visible-tag'
- style={{ color: visibleConfig[visible].color }}
- >
- {visibleConfig[visible].label}
- </span>
- )}
- </div>
- )}
- {/* 悬停操作按钮层 */}
- {showOperations && (
- <div className='card-hover-actions'>
- {/* 创建者/维护者显示【更多操作】+【立即使用】,非创建者显示【API 接入】+【立即使用】 */}
- {(isCreator || isMaintainer) ? (
- <>
- <Dropdown
- menu={{ items: operationItems }}
- trigger={['click']}
- placement='topLeft'
- className='card-operation-dropdown'
- >
- <Button
- className='card-operation-btn'
- icon={<Menu size={18} />}
- size='large'
- type='default'
- >
- 更多操作
- </Button>
- </Dropdown>
- <Button
- className='card-use-btn'
- icon={<ArrowRight size={18} />}
- size='large'
- type='primary'
- onClick={(e) => {
- e.stopPropagation();
- handleViewClick();
- }}
- >
- 立即使用
- </Button>
- </>
- ) : (
- <>
- <Button
- className='card-operation-btn'
- icon={<Code size={18} />}
- size='large'
- type='default'
- onClick={(e) => {
- e.stopPropagation();
- setApiDocOpen(true);
- }}
- >
- API 接入
- </Button>
- <Button
- className='card-use-btn'
- icon={<ArrowRight size={18} />}
- size='large'
- type='primary'
- onClick={(e) => {
- e.stopPropagation();
- handleViewClick();
- }}
- >
- 立即使用
- </Button>
- </>
- )}
- </div>
- )}
- {/* API 文档抽屉 */}
- <AppCardApiDoc
- open={apiDocOpen}
- appId={applicationId || ''}
- onClose={() => setApiDocOpen(false)}
- />
- </div>
- );
- };
- export default AppCard;
|