Ver Fonte

批量保存确认支持部分取消

Mochocyang há 3 meses atrás
pai
commit
86e4f83fc0
1 ficheiros alterados com 69 adições e 42 exclusões
  1. 69 42
      src/components/sources/outline-save-confirm-dialog.tsx

+ 69 - 42
src/components/sources/outline-save-confirm-dialog.tsx

@@ -48,6 +48,7 @@ export function OutlineSaveConfirmDialog({
 }: OutlineSaveConfirmDialogProps) {
   const [drafts, setDrafts] = useState<CharacterSaveDraft[]>(characterDrafts)
   const [normalRequests, setNormalRequests] = useState<OutlineSaveRequest[]>(requests)
+  const [deselectedFiles, setDeselectedFiles] = useState<Set<string>>(new Set())
   const selectedDrafts = useMemo(
     () => drafts.filter((draft) => draft.selected),
     [drafts],
@@ -61,6 +62,7 @@ export function OutlineSaveConfirmDialog({
     if (!open) return
     setDrafts(characterDrafts)
     setNormalRequests(requests)
+    setDeselectedFiles(new Set())
   }, [characterDrafts, open, requests])
 
   if (!open) return null
@@ -134,47 +136,66 @@ export function OutlineSaveConfirmDialog({
           ) : (
             <div className="space-y-2 text-sm">
               {normalRequests.map((request, index) => (
-                <div
+                <label
                   key={`${index}-${request.fileName}`}
-                  className="rounded-md border px-3 py-2"
+                  className="flex cursor-pointer items-start gap-3 rounded-md border px-3 py-2 hover:bg-accent"
                 >
-                  <div className="font-medium">{request.fileName}</div>
-                  <div className="mt-1 grid gap-0.5 text-xs text-muted-foreground">
-                    <div>类型:{request.fileType}</div>
-                    <div>写入方式:{request.writeMode}</div>
-                    <div>来源:{request.sourceIntent || "未标注"}</div>
-                    <div>
-                      引用 skill:{request.referencedSkills.length > 0
-                        ? request.referencedSkills.join("、")
-                        : "无"}
+                  <input
+                    aria-label={`保存 ${request.fileName}`}
+                    type="checkbox"
+                    checked={!deselectedFiles.has(request.fileName)}
+                    onChange={(e) => {
+                      setDeselectedFiles((prev) => {
+                        const next = new Set(prev)
+                        if (e.target.checked) {
+                          next.delete(request.fileName)
+                        } else {
+                          next.add(request.fileName)
+                        }
+                        return next
+                      })
+                    }}
+                    className="mt-1 h-4 w-4 shrink-0 accent-primary"
+                  />
+                  <span className="min-w-0 flex-1">
+                    <div className="font-medium">{request.fileName}</div>
+                    <div className="mt-1 grid gap-0.5 text-xs text-muted-foreground">
+                      <div>类型:{request.fileType}</div>
+                      <div>写入方式:{request.writeMode}</div>
+                      <div>来源:{request.sourceIntent || "未标注"}</div>
+                      <div>
+                        引用 skill:{request.referencedSkills.length > 0
+                          ? request.referencedSkills.join("、")
+                          : "无"}
+                      </div>
+                    </div>
+                    <div className="mt-2 grid gap-1.5 text-xs text-muted-foreground">
+                      <label htmlFor={`outline-save-folder-${index}`}>
+                        保存文件夹
+                      </label>
+                      <select
+                        id={`outline-save-folder-${index}`}
+                        aria-label={`选择 ${request.fileName} 的保存文件夹`}
+                        value={request.targetFolder}
+                        onChange={(event) => {
+                          const targetFolder = event.target.value
+                          setNormalRequests((items) =>
+                            items.map((item, itemIndex) =>
+                              itemIndex === index ? { ...item, targetFolder } : item,
+                            ),
+                          )
+                        }}
+                        className="h-8 rounded-md border bg-background px-2 text-xs text-foreground"
+                      >
+                        {folderOptions.map((folder) => (
+                          <option key={folder} value={folder}>
+                            {folder}
+                          </option>
+                        ))}
+                      </select>
                     </div>
-                  </div>
-                  <div className="mt-2 grid gap-1.5 text-xs text-muted-foreground">
-                    <label htmlFor={`outline-save-folder-${index}`}>
-                      保存文件夹
-                    </label>
-                    <select
-                      id={`outline-save-folder-${index}`}
-                      aria-label={`选择 ${request.fileName} 的保存文件夹`}
-                      value={request.targetFolder}
-                      onChange={(event) => {
-                        const targetFolder = event.target.value
-                        setNormalRequests((items) =>
-                          items.map((item, itemIndex) =>
-                            itemIndex === index ? { ...item, targetFolder } : item,
-                          ),
-                        )
-                      }}
-                      className="h-8 rounded-md border bg-background px-2 text-xs text-foreground"
-                    >
-                      {folderOptions.map((folder) => (
-                        <option key={folder} value={folder}>
-                          {folder}
-                        </option>
-                      ))}
-                    </select>
-                  </div>
-                </div>
+                  </span>
+                </label>
               ))}
             </div>
           )}
@@ -186,13 +207,19 @@ export function OutlineSaveConfirmDialog({
           </Button>
           <Button
             type="button"
-            disabled={mode === "character" && selectedDrafts.length === 0}
-            onClick={() =>
+            disabled={
+              (mode === "character" && selectedDrafts.length === 0) ||
+              (mode !== "character" && normalRequests.length - deselectedFiles.size === 0)
+            }
+            onClick={() => {
+              const filteredRequests = normalRequests.filter(
+                (r) => !deselectedFiles.has(r.fileName)
+              )
               onConfirm({
-                requests: mode === "character" ? requests : normalRequests,
+                requests: mode === "character" ? requests : filteredRequests,
                 characterDrafts: selectedDrafts,
               })
-            }
+            }}
           >
             确认保存
           </Button>