Role for character

This commit is contained in:
2025-11-16 18:37:33 +08:00
parent 0a3e255dfe
commit 9d9a2545f9
6 changed files with 39 additions and 14 deletions

View File

@@ -1,11 +1,11 @@
import { useState } from "react"; import { useState } from "react";
import { CharacterParams } from "../network/models"; import { CharacterParams, CharacterRole } from "../network/models";
import { network } from "../network/network"; import { network } from "../network/network";
import showToast from "./toast"; import showToast from "./toast";
import { useTranslation } from "../utils/i18n"; import { useTranslation } from "../utils/i18n";
import Button from "./button"; import Button from "./button";
export default function CharactorEditor({charactor, setCharactor, onDelete}: { export default function CharacterEditer({charactor, setCharactor, onDelete}: {
charactor: CharacterParams; charactor: CharacterParams;
setCharactor: (charactor: CharacterParams) => void; setCharactor: (charactor: CharacterParams) => void;
onDelete: () => void; onDelete: () => void;
@@ -82,6 +82,15 @@ export default function CharactorEditor({charactor, setCharactor, onDelete}: {
onChange={(e) => setCharactor({ ...charactor, cv: e.target.value })} onChange={(e) => setCharactor({ ...charactor, cv: e.target.value })}
/> />
<select
className="select select-sm select-bordered"
value={charactor.role}
onChange={(e) => setCharactor({ ...charactor, role: e.target.value as CharacterRole })}
>
<option value="primary">{t("Primary Role")}</option>
<option value="side">{t("Side Role")}</option>
</select>
<div className="flex-1"> <div className="flex-1">
<textarea <textarea
className="textarea textarea-bordered w-full h-full resize-none text-xs" className="textarea textarea-bordered w-full h-full resize-none text-xs"

View File

@@ -52,11 +52,14 @@ export interface CreateResourceParams {
characters: CharacterParams[]; characters: CharacterParams[];
} }
export type CharacterRole = 'primary' | 'side';
export interface CharacterParams { export interface CharacterParams {
name: string; name: string;
alias: string[]; alias: string[];
cv: string; cv: string;
image: number; image: number;
role: CharacterRole;
} }
export interface Image { export interface Image {

View File

@@ -20,7 +20,7 @@ import {
SelectAndUploadImageButton, SelectAndUploadImageButton,
UploadClipboardImageButton, UploadClipboardImageButton,
} from "../components/image_selector.tsx"; } from "../components/image_selector.tsx";
import CharactorEditor, { FetchVndbCharactersButton } from "../components/charactor_edit.tsx"; import CharacterEditer, { FetchVndbCharactersButton } from "../components/character_edit.tsx";
export default function EditResourcePage() { export default function EditResourcePage() {
const [title, setTitle] = useState<string>(""); const [title, setTitle] = useState<string>("");
@@ -429,7 +429,7 @@ export default function EditResourcePage() {
<div className="grid grid-cols-1 md:grid-cols-2 my-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 my-2 gap-4">
{ {
charactors.map((charactor, index) => { charactors.map((charactor, index) => {
return <CharactorEditor return <CharacterEditer
charactor={charactor} charactor={charactor}
setCharactor={(newCharactor) => { setCharactor={(newCharactor) => {
const newCharactors = [...charactors]; const newCharactors = [...charactors];

View File

@@ -19,7 +19,7 @@ import {
SelectAndUploadImageButton, SelectAndUploadImageButton,
UploadClipboardImageButton, UploadClipboardImageButton,
} from "../components/image_selector.tsx"; } from "../components/image_selector.tsx";
import CharactorEditor from "../components/charactor_edit.tsx"; import CharacterEditer from "../components/character_edit.tsx";
export default function PublishPage() { export default function PublishPage() {
const [title, setTitle] = useState<string>(""); const [title, setTitle] = useState<string>("");
@@ -436,7 +436,7 @@ export default function PublishPage() {
<div className="grid grid-cols-1 md:grid-cols-2 my-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 my-2 gap-4">
{ {
charactors.map((charactor, index) => { charactors.map((charactor, index) => {
return <CharactorEditor return <CharacterEditer
charactor={charactor} charactor={charactor}
setCharactor={(newCharactor) => { setCharactor={(newCharactor) => {
const newCharactors = [...charactors]; const newCharactors = [...charactors];

View File

@@ -5,6 +5,7 @@ type Charactor struct {
Name string `gorm:"type:varchar(100);not null"` Name string `gorm:"type:varchar(100);not null"`
Alias []string `gorm:"serializer:json"` Alias []string `gorm:"serializer:json"`
CV string `gorm:"type:varchar(100)"` CV string `gorm:"type:varchar(100)"`
Role string `gorm:"type:varchar(20);default:primary"`
ImageID uint ImageID uint
ResourceID uint ResourceID uint
Image *Image `gorm:"foreignKey:ImageID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` Image *Image `gorm:"foreignKey:ImageID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
@@ -15,6 +16,7 @@ type CharactorView struct {
Name string `json:"name"` Name string `json:"name"`
Alias []string `json:"alias"` Alias []string `json:"alias"`
CV string `json:"cv"` CV string `json:"cv"`
Role string `json:"role"`
Image uint `json:"image"` Image uint `json:"image"`
} }
@@ -24,12 +26,13 @@ func (c *Charactor) ToView() *CharactorView {
Name: c.Name, Name: c.Name,
Alias: c.Alias, Alias: c.Alias,
CV: c.CV, CV: c.CV,
Role: c.Role,
Image: c.ImageID, Image: c.ImageID,
} }
} }
func (c *Charactor) Equal(other *Charactor) bool { func (c *Charactor) Equal(other *Charactor) bool {
if c.Name != other.Name || c.CV != other.CV || c.ImageID != other.ImageID { if c.Name != other.Name || c.CV != other.CV || c.Role != other.Role || c.ImageID != other.ImageID {
return false return false
} }
if len(c.Alias) != len(other.Alias) { if len(c.Alias) != len(other.Alias) {

View File

@@ -42,6 +42,7 @@ type CharactorParams struct {
Name string `json:"name" binding:"required"` Name string `json:"name" binding:"required"`
Alias []string `json:"alias"` Alias []string `json:"alias"`
CV string `json:"cv"` CV string `json:"cv"`
Role string `json:"role"`
Image uint `json:"image"` Image uint `json:"image"`
} }
@@ -84,10 +85,15 @@ func CreateResource(uid uint, params *ResourceParams) (uint, error) {
} }
charactors := make([]model.Charactor, len(params.Charactors)) charactors := make([]model.Charactor, len(params.Charactors))
for i, c := range params.Charactors { for i, c := range params.Charactors {
role := c.Role
if role == "" {
role = "primary"
}
charactors[i] = model.Charactor{ charactors[i] = model.Charactor{
Name: c.Name, Name: c.Name,
Alias: c.Alias, Alias: c.Alias,
CV: c.CV, CV: c.CV,
Role: role,
ImageID: c.Image, ImageID: c.Image,
} }
} }
@@ -502,10 +508,15 @@ func UpdateResource(uid, rid uint, params *ResourceParams) error {
} }
charactors := make([]model.Charactor, len(params.Charactors)) charactors := make([]model.Charactor, len(params.Charactors))
for i, c := range params.Charactors { for i, c := range params.Charactors {
role := c.Role
if role == "" {
role = "primary"
}
charactors[i] = model.Charactor{ charactors[i] = model.Charactor{
Name: c.Name, Name: c.Name,
Alias: c.Alias, Alias: c.Alias,
CV: c.CV, CV: c.CV,
Role: role,
ImageID: c.Image, ImageID: c.Image,
} }
} }
@@ -656,17 +667,15 @@ func GetCharactorsFromVndb(vnID string) ([]CharactorParams, error) {
// 遍历声优信息 // 遍历声优信息
for _, va := range result.VA { for _, va := range result.VA {
// 检查角色是否为主要角色 role := "Unknown"
isPrimary := false
for _, vn := range va.Character.VNS { for _, vn := range va.Character.VNS {
if vn.Role == "primary" { if vn.ID == vnID {
isPrimary = true role = vn.Role
break break
} }
} }
// 只处理主要角色 if role != "primary" && role != "side" {
if !isPrimary {
continue continue
} }
@@ -693,8 +702,9 @@ func GetCharactorsFromVndb(vnID string) ([]CharactorParams, error) {
charactor := CharactorParams{ charactor := CharactorParams{
Name: characterName, Name: characterName,
Alias: []string{}, // 按要求不添加别名 Alias: []string{},
CV: cvName, CV: cvName,
Role: role,
Image: 0, // 默认值,下面会下载图片 Image: 0, // 默认值,下面会下载图片
} }