diff --git a/frontend/src/components/charactor_edit.tsx b/frontend/src/components/charactor_edit.tsx
index 8217917..cff457c 100644
--- a/frontend/src/components/charactor_edit.tsx
+++ b/frontend/src/components/charactor_edit.tsx
@@ -1,12 +1,13 @@
import { useState } from "react";
-import { CharactorParams } from "../network/models";
+import { CharacterParams } from "../network/models";
import { network } from "../network/network";
import showToast from "./toast";
import { useTranslation } from "../utils/i18n";
+import Button from "./button";
export default function CharactorEditor({charactor, setCharactor, onDelete}: {
- charactor: CharactorParams;
- setCharactor: (charactor: CharactorParams) => void;
+ charactor: CharacterParams;
+ setCharactor: (charactor: CharacterParams) => void;
onDelete: () => void;
}) {
const { t } = useTranslation();
@@ -39,7 +40,7 @@ export default function CharactorEditor({charactor, setCharactor, onDelete}: {
input.click();
}
- return
+ return
{
isUploading ?
@@ -94,4 +95,37 @@ export default function CharactorEditor({charactor, setCharactor, onDelete}: {
;
+}
+
+export function FetchVndbCharactersButton({vnID, onFetch}: {
+ vnID: string;
+ onFetch: (characters: CharacterParams[]) => void;
+}) {
+ const { t } = useTranslation();
+ const [isFetching, setFetching] = useState(false);
+ const fetchCharacters = async () => {
+ // validate vnID (v123456)
+ if (!/^v\d+$/.test(vnID)) {
+ showToast({
+ type: "error",
+ message: t("Invalid VNDB ID format"),
+ });
+ return;
+ }
+ setFetching(true);
+ const res = await network.getCharactersFromVNDB(vnID);
+ setFetching(false);
+ if (res.success && res.data) {
+ onFetch(res.data);
+ } else {
+ showToast({
+ type: "error",
+ message: t("Failed to fetch characters from VNDB"),
+ });
+ }
+ };
+
+ return ;
}
\ No newline at end of file
diff --git a/frontend/src/network/models.ts b/frontend/src/network/models.ts
index 2a33747..8ea005f 100644
--- a/frontend/src/network/models.ts
+++ b/frontend/src/network/models.ts
@@ -49,10 +49,10 @@ export interface CreateResourceParams {
images: number[];
gallery: number[];
gallery_nsfw: number[];
- charactors: CharactorParams[];
+ characters: CharacterParams[];
}
-export interface CharactorParams {
+export interface CharacterParams {
name: string;
alias: string[];
cv: string;
@@ -96,7 +96,7 @@ export interface ResourceDetails {
related: Resource[];
gallery: number[];
galleryNsfw: number[];
- charactors: CharactorParams[];
+ charactors: CharacterParams[];
}
export interface Storage {
diff --git a/frontend/src/network/network.ts b/frontend/src/network/network.ts
index 1a0cd9d..363ef98 100644
--- a/frontend/src/network/network.ts
+++ b/frontend/src/network/network.ts
@@ -21,6 +21,7 @@ import {
CommentWithRef,
Collection,
Statistics,
+ CharacterParams,
} from "./models.ts";
class Network {
@@ -802,6 +803,14 @@ class Network {
axios.get(`${this.apiBaseUrl}/config/statistics`),
);
}
+
+ async getCharactersFromVNDB(vnID: string): Promise> {
+ return this._callApi(() =>
+ axios.get(`${this.apiBaseUrl}/resource/vndb/characters`, {
+ params: { vnid: vnID },
+ }),
+ );
+ }
}
export const network = new Network();
diff --git a/frontend/src/pages/edit_resource_page.tsx b/frontend/src/pages/edit_resource_page.tsx
index 14b94a6..05bcba0 100644
--- a/frontend/src/pages/edit_resource_page.tsx
+++ b/frontend/src/pages/edit_resource_page.tsx
@@ -6,7 +6,7 @@ import {
MdDelete,
MdOutlineInfo,
} from "react-icons/md";
-import { CharactorParams, Tag } from "../network/models.ts";
+import { CharacterParams, Tag } from "../network/models.ts";
import { network } from "../network/network.ts";
import { useNavigate, useParams } from "react-router";
import showToast from "../components/toast.ts";
@@ -20,7 +20,7 @@ import {
SelectAndUploadImageButton,
UploadClipboardImageButton,
} from "../components/image_selector.tsx";
-import CharactorEditor from "../components/charactor_edit.tsx";
+import CharactorEditor, { FetchVndbCharactersButton } from "../components/charactor_edit.tsx";
export default function EditResourcePage() {
const [title, setTitle] = useState("");
@@ -31,7 +31,7 @@ export default function EditResourcePage() {
const [links, setLinks] = useState<{ label: string; url: string }[]>([]);
const [galleryImages, setGalleryImages] = useState([]);
const [galleryNsfw, setGalleryNsfw] = useState([]);
- const [charactors, setCharactors] = useState([]);
+ const [charactors, setCharactors] = useState([]);
const [error, setError] = useState(null);
const [isSubmitting, setSubmitting] = useState(false);
const [isLoading, setLoading] = useState(true);
@@ -107,7 +107,7 @@ export default function EditResourcePage() {
links: links,
gallery: galleryImages,
gallery_nsfw: galleryNsfw,
- charactors: charactors,
+ characters: charactors,
});
if (res.success) {
setSubmitting(false);
@@ -444,9 +444,9 @@ export default function EditResourcePage() {
})
}
-
+
+ {
+ links.find(link => link.label.toLowerCase() === "vndb") &&
+
+ link.label.toLowerCase() === "vndb")?.url.split("/").pop() ?? ""}
+ onFetch={(fetchedCharacters) => {
+ setCharactors(fetchedCharacters);
+ }}
+ />
+
+ }
{error && (
diff --git a/frontend/src/pages/publish_page.tsx b/frontend/src/pages/publish_page.tsx
index 34e1124..12186f3 100644
--- a/frontend/src/pages/publish_page.tsx
+++ b/frontend/src/pages/publish_page.tsx
@@ -6,7 +6,7 @@ import {
MdDelete,
MdOutlineInfo,
} from "react-icons/md";
-import { CharactorParams, Tag } from "../network/models.ts";
+import { CharacterParams, Tag } from "../network/models.ts";
import { network } from "../network/network.ts";
import { useNavigate } from "react-router";
import { useTranslation } from "../utils/i18n";
@@ -32,7 +32,7 @@ export default function PublishPage() {
const [galleryNsfw, setGalleryNsfw] = useState([]);
const [error, setError] = useState(null);
const [isSubmitting, setSubmitting] = useState(false);
- const [charactors, setCharactors] = useState([]);
+ const [charactors, setCharactors] = useState([]);
const isFirstLoad = useRef(true);
useEffect(() => {
@@ -111,7 +111,7 @@ export default function PublishPage() {
links: links,
gallery: galleryImages,
gallery_nsfw: galleryNsfw,
- charactors: charactors,
+ characters: charactors,
});
if (res.success) {
localStorage.removeItem("publish_data");
diff --git a/frontend/src/pages/resource_details_page.tsx b/frontend/src/pages/resource_details_page.tsx
index afd903c..e8aeedc 100644
--- a/frontend/src/pages/resource_details_page.tsx
+++ b/frontend/src/pages/resource_details_page.tsx
@@ -18,7 +18,7 @@ import {
Tag,
Resource,
Collection,
- CharactorParams,
+ CharacterParams,
} from "../network/models.ts";
import { network } from "../network/network.ts";
import showToast from "../components/toast.ts";
@@ -1931,6 +1931,9 @@ function Gallery({ images, nsfw }: { images: number[], nsfw: number[] }) {
nsfw = [];
}
+ // 如果图片数量超过8张,显示数字而不是圆点
+ const showDots = images.length <= 8;
+
return (
<>
)}
- {/* 底部圆点 */}
+ {/* 底部指示器 */}
{images.length > 1 && (
-
- {images.map((_, index) => (
-
@@ -2065,7 +2078,7 @@ function GalleryImage({src, nfsw}: {src: string, nfsw: boolean}) {
);
}
-function Characters({ charactors }: { charactors: CharactorParams[] }) {
+function Characters({ charactors }: { charactors: CharacterParams[] }) {
const { t } = useTranslation();
if (!charactors || charactors.length === 0) {
@@ -2084,7 +2097,7 @@ function Characters({ charactors }: { charactors: CharactorParams[] }) {
);
}
-function CharacterCard({ charactor }: { charactor: CharactorParams }) {
+function CharacterCard({ charactor }: { charactor: CharacterParams }) {
const navigate = useNavigate();
const handleCVClick = (e: React.MouseEvent) => {
diff --git a/server/service/resource.go b/server/service/resource.go
index 5ff9ad3..cb83739 100644
--- a/server/service/resource.go
+++ b/server/service/resource.go
@@ -35,7 +35,7 @@ type ResourceParams struct {
Images []uint `json:"images"`
Gallery []uint `json:"gallery"`
GalleryNsfw []uint `json:"gallery_nsfw"`
- Charactors []CharactorParams `json:"charactors"`
+ Charactors []CharactorParams `json:"characters"`
}
type CharactorParams struct {