Selaa lähdekoodia

✨ feat(avatar): 优化头像选择器UI和交互体验

- 将上传区域从单独卡片移至头像列表首位,减少视觉层级
- 为上传区域添加悬停效果和缩放动画,提升交互体验
- 优化空状态显示,当无头像时在上传按钮旁显示提示卡片
- 为头像列表容器添加内边距,优化视觉间距
- MinioUploader组件新增card显示模式,适配列表内上传按钮场景
- 调整对话框描述文本顺序,更符合用户操作流程
- 修复Eye图标缺少空格的格式问题
yourname 4 kuukautta sitten
vanhempi
sitoutus
9257884918

+ 41 - 31
src/client/admin-shadcn/components/AvatarSelector.tsx

@@ -193,30 +193,13 @@ const AvatarSelector: React.FC<AvatarSelectorProps> = ({
           <DialogHeader>
             <DialogTitle>选择头像</DialogTitle>
             <DialogDescription>
-              从已有头像中选择,或上传新头像
+              上传新头像或从已有头像中选择
             </DialogDescription>
           </DialogHeader>
 
           <div className="space-y-4">
-            {/* 上传区域 */}
-            <Card>
-              <CardContent className="pt-6">
-                <MinioUploader
-                  uploadPath={uploadPath}
-                  accept={accept}
-                  maxSize={maxSize}
-                  onUploadSuccess={handleUploadSuccess}
-                  buttonText={uploadButtonText}
-                  size="minimal"
-                />
-                <p className="text-sm text-gray-500 mt-2">
-                  上传后请从下方列表中选择新上传的头像
-                </p>
-              </CardContent>
-            </Card>
-
             {/* 头像列表 */}
-            <div className="space-y-2 max-h-96 overflow-y-auto">
+            <div className="space-y-2 max-h-96 overflow-y-auto p-1">
               {isLoading ? (
                 <Card>
                   <CardContent className="text-center py-8">
@@ -224,8 +207,30 @@ const AvatarSelector: React.FC<AvatarSelectorProps> = ({
                     <p className="text-gray-500 mt-2">加载中...</p>
                   </CardContent>
                 </Card>
-              ) : avatars.length > 0 ? (
+              ) : (
                 <div className="grid grid-cols-4 gap-3">
+                  {/* 上传区域 - 作为第一项 */}
+                  <div className="relative cursor-pointer transition-all duration-200">
+                    <div className="rounded-lg border-2 border-dashed border-gray-300 hover:border-primary transition-colors hover:scale-105">
+                      <div className="p-2 h-24 flex items-center justify-center">
+                        <MinioUploader
+                          uploadPath={uploadPath}
+                          accept={accept}
+                          maxSize={maxSize}
+                          onUploadSuccess={handleUploadSuccess}
+                          buttonText="上传"
+                          size="minimal"
+                          displayMode="card"
+                          showUploadList={false}
+                        />
+                      </div>
+                    </div>
+                    <p className="text-xs text-center mt-1 text-muted-foreground">
+                      上传新头像
+                    </p>
+                  </div>
+
+                  {/* 现有头像列表 */}
                   {avatars.map((file) => (
                     <div
                       key={file.id}
@@ -256,7 +261,7 @@ const AvatarSelector: React.FC<AvatarSelectorProps> = ({
                         )}
                         
                         <div className="absolute top-1 right-1">
-                          <Eye 
+                          <Eye
                             className="h-4 w-4 text-white bg-black/50 rounded-full p-0.5 cursor-pointer hover:bg-black/70"
                             onClick={(e) => {
                               e.stopPropagation();
@@ -271,17 +276,22 @@ const AvatarSelector: React.FC<AvatarSelectorProps> = ({
                       </p>
                     </div>
                   ))}
-                </div>
-              ) : (
-                <Card>
-                  <CardContent className="text-center py-8">
-                    <div className="flex flex-col items-center">
-                      <Upload className="h-12 w-12 text-gray-400 mb-4" />
-                      <p className="text-gray-600">暂无符合条件的头像</p>
-                      <p className="text-sm text-gray-500 mt-2">请先上传头像文件</p>
+                  
+                  {/* 空状态 - 当没有头像时显示 */}
+                  {avatars.length === 0 && (
+                    <div className="col-span-3">
+                      <Card>
+                        <CardContent className="text-center py-8">
+                          <div className="flex flex-col items-center">
+                            <Upload className="h-12 w-12 text-gray-400 mb-4" />
+                            <p className="text-gray-600">暂无头像</p>
+                            <p className="text-sm text-gray-500 mt-2">请上传头像文件</p>
+                          </div>
+                        </CardContent>
+                      </Card>
                     </div>
-                  </CardContent>
-                </Card>
+                  )}
+                </div>
               )}
             </div>
           </div>

+ 31 - 1
src/client/admin-shadcn/components/MinioUploader.tsx

@@ -32,6 +32,8 @@ interface MinioUploaderProps {
   uploadListTitle?: string;
   /** 组件尺寸模式 */
   size?: 'default' | 'compact' | 'minimal';
+  /** 显示模式:卡片模式或完整模式 */
+  displayMode?: 'full' | 'card';
 }
 
 // 定义上传文件状态
@@ -58,7 +60,8 @@ const MinioUploader: React.FC<MinioUploaderProps> = ({
   uploadMode = 'dragdrop',
   showUploadList = true,
   uploadListTitle = '上传进度',
-  size = 'default'
+  size = 'default',
+  displayMode = 'full'
 }) => {
   const [fileList, setFileList] = useState<UploadFile[]>([]);
   const [uploadingFiles, setUploadingFiles] = useState<Set<string>>(new Set());
@@ -315,6 +318,33 @@ const MinioUploader: React.FC<MinioUploaderProps> = ({
 
   const sizeConfig = getSizeConfig();
 
+  // 卡片模式渲染
+  if (displayMode === 'card') {
+    return (
+      <div className="h-full flex items-center justify-center">
+        <button
+          type="button"
+          className={`flex flex-col items-center justify-center w-full h-full text-muted-foreground hover:text-primary transition-colors cursor-pointer
+            ${size === 'minimal' ? 'text-xs' : 'text-sm'}`}
+          onClick={() => {
+            const input = document.createElement('input');
+            input.type = 'file';
+            input.accept = accept || '';
+            input.multiple = multiple;
+            input.onchange = (e) => {
+              const files = (e.target as HTMLInputElement).files;
+              if (files) handleFileSelect(files);
+            };
+            input.click();
+          }}
+        >
+          <Upload className={`${size === 'minimal' ? 'h-6 w-6 mb-1' : 'h-8 w-8 mb-2'}`} />
+          <span>{buttonText}</span>
+        </button>
+      </div>
+    );
+  }
+
   return (
     <div className={sizeConfig.spacing}>
       {/* 上传区域 - 根据模式显示不同界面 */}