diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts
index a119c68..c4b0ae4 100644
--- a/frontend/src/i18n.ts
+++ b/frontend/src/i18n.ts
@@ -156,8 +156,8 @@ export const i18nData = {
"Cloudflare Turnstile Secret Key": "Cloudflare Turnstile 密钥",
"If the cloudflare turnstile keys are not empty, the turnstile will be used for register and download.":
"如果设置了 Cloudflare Turnstile 密钥,将在注册和下载时启用验证",
- "The first image will be used as the cover image":
- "第一张图片将用作封面图片",
+ "You can select a cover image using the radio button in the Cover column":
+ "您可以使用封面列中的单选按钮选择封面图片",
"Please enter a search keyword": "请输入搜索关键词",
"Searching...": "搜索中...",
"Create Tag": "创建标签",
@@ -425,8 +425,8 @@ export const i18nData = {
"Cloudflare Turnstile Secret Key": "Cloudflare Turnstile 密鑰",
"If the cloudflare turnstile keys are not empty, the turnstile will be used for register and download.":
"如果設置了 Cloudflare Turnstile 密鑰,將在註冊和下載時啟用驗證",
- "The first image will be used as the cover image":
- "第一張圖片將用作封面圖片",
+ "You can select a cover image using the radio button in the Cover column":
+ "您可以使用封面列中的單選按鈕選擇封面圖片",
"Please enter a search keyword": "請輸入搜尋關鍵字",
"Searching...": "搜尋中...",
"Create Tag": "創建標籤",
diff --git a/frontend/src/network/models.ts b/frontend/src/network/models.ts
index 4a15852..1e6b59b 100644
--- a/frontend/src/network/models.ts
+++ b/frontend/src/network/models.ts
@@ -48,6 +48,7 @@ export interface CreateResourceParams {
tags: number[];
article: string;
images: number[];
+ cover_id?: number;
gallery: number[];
gallery_nsfw: number[];
characters: CharacterParams[];
@@ -94,6 +95,7 @@ export interface ResourceDetails {
releaseDate?: string;
tags: Tag[];
images: Image[];
+ coverId?: number;
files: RFile[];
author: User;
views: number;
diff --git a/frontend/src/pages/edit_resource_page.tsx b/frontend/src/pages/edit_resource_page.tsx
index 55714e9..31945fb 100644
--- a/frontend/src/pages/edit_resource_page.tsx
+++ b/frontend/src/pages/edit_resource_page.tsx
@@ -29,6 +29,7 @@ export default function EditResourcePage() {
const [tags, setTags] = useState([]);
const [article, setArticle] = useState("");
const [images, setImages] = useState([]);
+ const [coverId, setCoverId] = useState(undefined);
const [links, setLinks] = useState<{ label: string; url: string }[]>([]);
const [galleryImages, setGalleryImages] = useState([]);
const [galleryNsfw, setGalleryNsfw] = useState([]);
@@ -59,6 +60,7 @@ export default function EditResourcePage() {
setTags(data.tags);
setArticle(data.article);
setImages(data.images.map((i) => i.id));
+ setCoverId(data.coverId);
setLinks(data.links ?? []);
setGalleryImages(data.gallery ?? []);
setGalleryNsfw(data.galleryNsfw ?? []);
@@ -106,6 +108,7 @@ export default function EditResourcePage() {
tags: tags.map((tag) => tag.id),
article: article,
images: images,
+ cover_id: coverId,
links: links,
gallery: galleryImages,
gallery_nsfw: galleryNsfw,
@@ -328,7 +331,7 @@ export default function EditResourcePage() {
"Images will not be displayed automatically, you need to reference them in the description",
)}
-
| {t("Preview")} |
{"Markdown"} |
+
{t("Cover")} |
{t("Gallery")} |
{"Nsfw"} |
{t("Action")} |
@@ -368,6 +372,15 @@ export default function EditResourcePage() {
+
+ setCoverId(image)}
+ />
+ |
diff --git a/frontend/src/pages/publish_page.tsx b/frontend/src/pages/publish_page.tsx
index 61431ce..c4706cf 100644
--- a/frontend/src/pages/publish_page.tsx
+++ b/frontend/src/pages/publish_page.tsx
@@ -28,6 +28,7 @@ export default function PublishPage() {
const [tags, setTags] = useState([]);
const [article, setArticle] = useState("");
const [images, setImages] = useState([]);
+ const [coverId, setCoverId] = useState(undefined);
const [links, setLinks] = useState<{ label: string; url: string }[]>([]);
const [galleryImages, setGalleryImages] = useState([]);
const [galleryNsfw, setGalleryNsfw] = useState([]);
@@ -48,6 +49,7 @@ export default function PublishPage() {
setTags(data.tags || []);
setArticle(data.article || "");
setImages(data.images || []);
+ setCoverId(data.cover_id || undefined);
setLinks(data.links || []);
setGalleryImages(data.gallery || []);
setGalleryNsfw(data.gallery_nsfw || []);
@@ -64,6 +66,7 @@ export default function PublishPage() {
tags: tags,
article: article,
images: images,
+ cover_id: coverId,
links: links,
gallery: galleryImages,
gallery_nsfw: galleryNsfw,
@@ -73,7 +76,7 @@ export default function PublishPage() {
const dataString = JSON.stringify(data);
localStorage.setItem("publish_data", dataString);
}
- }, [altTitles, article, images, tags, title, links, galleryImages, galleryNsfw, characters, releaseDate]);
+ }, [altTitles, article, images, coverId, tags, title, links, galleryImages, galleryNsfw, characters, releaseDate]);
const navigate = useNavigate();
const { t } = useTranslation();
@@ -120,6 +123,7 @@ export default function PublishPage() {
tags: tags.map((tag) => tag.id),
article: article,
images: images,
+ cover_id: coverId,
links: links,
gallery: galleryImages,
gallery_nsfw: galleryNsfw,
@@ -344,7 +348,7 @@ export default function PublishPage() {
"Images will not be displayed automatically, you need to reference them in the description",
)}
- {t("The first image will be used as the cover image")}
+ {t("You can select a cover image using the radio button in the Cover column")}
{t("Preview")} |
{"Markdown"} |
- {"Gallery"} |
+ {t("Cover")} |
+ {t("Gallery")} |
{"Nsfw"} |
{t("Action")} |
@@ -384,6 +389,15 @@ export default function PublishPage() {
|
+
+ setCoverId(image)}
+ />
+ |
diff --git a/server/model/resource.go b/server/model/resource.go
index 230cb9d..58869d4 100644
--- a/server/model/resource.go
+++ b/server/model/resource.go
@@ -14,8 +14,9 @@ type Resource struct {
ReleaseDate *time.Time
Article string
Images []Image `gorm:"many2many:resource_images;"`
- Tags []Tag `gorm:"many2many:resource_tags;"`
- Files []File `gorm:"foreignKey:ResourceID"`
+ CoverID *uint
+ Tags []Tag `gorm:"many2many:resource_tags;"`
+ Files []File `gorm:"foreignKey:ResourceID"`
UserID uint
User User
Views uint
@@ -52,6 +53,7 @@ type ResourceDetailView struct {
ReleaseDate *time.Time `json:"releaseDate,omitempty"`
Tags []TagView `json:"tags"`
Images []ImageView `json:"images"`
+ CoverID *uint `json:"coverId,omitempty"`
Files []FileView `json:"files"`
Author UserView `json:"author"`
Views uint `json:"views"`
@@ -78,7 +80,18 @@ func (r *Resource) ToView() ResourceView {
}
var image *ImageView
- if len(r.Images) > 0 {
+ if r.CoverID != nil {
+ // Use the cover image if specified
+ for _, img := range r.Images {
+ if img.ID == *r.CoverID {
+ v := img.ToView()
+ image = &v
+ break
+ }
+ }
+ }
+ // If no cover is set or cover image not found, use the first image
+ if image == nil && len(r.Images) > 0 {
v := r.Images[0].ToView()
image = &v
}
@@ -122,6 +135,7 @@ func (r *Resource) ToDetailView() ResourceDetailView {
ReleaseDate: r.ReleaseDate,
Tags: tags,
Images: images,
+ CoverID: r.CoverID,
Files: files,
Author: r.User.ToView(),
Views: r.Views,
diff --git a/server/service/resource.go b/server/service/resource.go
index f917953..f9ac8b8 100644
--- a/server/service/resource.go
+++ b/server/service/resource.go
@@ -34,6 +34,7 @@ type ResourceParams struct {
Tags []uint `json:"tags"`
Article string `json:"article"`
Images []uint `json:"images"`
+ CoverID *uint `json:"cover_id"`
Gallery []uint `json:"gallery"`
GalleryNsfw []uint `json:"gallery_nsfw"`
Characters []CharacterParams `json:"characters"`
@@ -110,6 +111,14 @@ func CreateResource(uid uint, params *ResourceParams) (uint, error) {
}
date = &parsedDate
}
+ // Validate CoverID if provided
+ var coverID *uint
+ if params.CoverID != nil && *params.CoverID != 0 {
+ if !slices.Contains(params.Images, *params.CoverID) {
+ return 0, model.NewRequestError("Cover ID must be one of the resource images")
+ }
+ coverID = params.CoverID
+ }
r := model.Resource{
Title: params.Title,
AlternativeTitles: params.AlternativeTitles,
@@ -117,6 +126,7 @@ func CreateResource(uid uint, params *ResourceParams) (uint, error) {
Links: params.Links,
ReleaseDate: date,
Images: images,
+ CoverID: coverID,
Tags: tags,
UserID: uid,
Gallery: gallery,
@@ -548,11 +558,21 @@ func UpdateResource(uid, rid uint, params *ResourceParams) error {
date = &parsedDate
}
+ // Validate CoverID if provided
+ var coverID *uint
+ if params.CoverID != nil && *params.CoverID != 0 {
+ if !slices.Contains(params.Images, *params.CoverID) {
+ return model.NewRequestError("Cover ID must be one of the resource images")
+ }
+ coverID = params.CoverID
+ }
+
r.Title = params.Title
r.AlternativeTitles = params.AlternativeTitles
r.Article = params.Article
r.Links = params.Links
r.ReleaseDate = date
+ r.CoverID = coverID
r.Gallery = gallery
r.GalleryNsfw = nsfw
r.Characters = characters
|