|
|
@@ -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>
|