feat: add image management functionality

- Implemented image folder repository with CRUD operations.
- Added image reference repository for managing image references to articles.
- Created image repository for handling image assets, including listing, inserting, updating, and deleting images.
- Introduced image usage repository to track storage usage and quotas for tenants.
- Added SQL queries for image assets, folders, references, and usage.
- Developed image handler for HTTP endpoints to manage images and folders.
- Created database migration scripts for image-related tables and structures.
This commit is contained in:
2026-04-16 20:40:41 +08:00
parent 0a3558fc51
commit 27389164b0
57 changed files with 8063 additions and 153 deletions
+63 -2
View File
@@ -52,6 +52,17 @@ const enUS = {
copySuccess: "Content copied",
yes: "Yes",
no: "No",
imageUploadProgress: {
title: "Processing image",
stages: {
idle: "Waiting to start",
validating: "Validating file",
decoding: "Decoding image",
encoding: "Converting to WebP",
uploading: "Uploading to server",
finishing: "Finishing",
},
},
},
nav: {
workspace: "Workspace",
@@ -66,6 +77,7 @@ const enUS = {
trackingDetail: "Data Details",
contentManagement: "Content Management",
knowledge: "Knowledge Base",
images: "Image Management",
},
auth: {
welcomeBack: "Welcome back",
@@ -135,6 +147,10 @@ const enUS = {
title: "Knowledge Base",
description: "No matching backend APIs exist in this repository yet.",
},
images: {
title: "Image Management",
description: "Manage image assets, including folders, reference analysis, and cover library usage.",
},
},
workspace: {
eyebrow: "Dashboard",
@@ -596,6 +612,11 @@ const enUS = {
resetSize: "Reset size",
delete: "Delete image",
},
imagePicker: {
title: "Select image",
emptyTitle: "Choose an image for the article body",
emptyHint: "Pick from the image library or upload locally. Local uploads are converted to WebP before upload.",
},
aiOptimize: {
title: "AI Optimize",
subtitle: "Supports contiguous multi-block selections and generates a rewrite from your instruction",
@@ -741,6 +762,8 @@ const enUS = {
local: "Body / Local Upload",
ai: "AI Cover",
},
dropzoneTitle: "Upload image",
dropzoneHint: "Supports PNG, JPG, GIF, and WebP up to 10MB. Uploads are converted to WebP in the browser first.",
previewTitle: "Cover crop preview",
previewHintLocked: "Baijiahao is selected, so the cover will be exported as 16:9 when you confirm.",
previewHintFree: "There is no forced cover ratio right now. You can upload the original image directly or adjust framing before confirming.",
@@ -754,7 +777,7 @@ const enUS = {
fileLabel: "Current cover",
emptyTitle: "No publish platform selected",
emptyPlatforms: "Choose publish platforms first and each platform's cover requirements will appear here.",
emptyHint: "Supports PNG, JPG, GIF, and WebP up to 10MB.",
emptyHint: "Supports PNG, JPG, JPEG, GIF, WebP, BMP, SVG, and AVIF up to 10MB.",
aiTitle: "AI cover is coming soon",
aiHint: "For now this flow supports local upload and cropping. AI cover generation can be plugged in here later.",
actions: {
@@ -768,7 +791,7 @@ const enUS = {
unsupported: "This platform's current integration does not consume a standalone cover yet and falls back to body content or the platform default.",
},
messages: {
invalidType: "Only PNG, JPG, GIF, and WebP images are supported.",
invalidType: "Only PNG, JPG, JPEG, GIF, WebP, BMP, SVG, and AVIF images are supported.",
tooLarge: "Image size cannot exceed 10MB.",
missingImage: "Select a cover image first.",
missingArticle: "The article is not ready yet. Refresh and try again.",
@@ -981,6 +1004,44 @@ const enUS = {
currentStatusText: "The page shell is ready with navigation and auth guard support.",
integrationStrategyText: "Keep the UI aligned with tenant-api envelopes and wire real read flows before write flows.",
},
images: {
title: "Image Management",
description: "Manage image library with folder organization and reference analysis.",
uploadBtn: "Upload Image",
folders: "Folders",
uncategorized: "Uncategorized",
searchPlaceholder: "Search image name...",
newFolder: "New Folder",
folderName: "Folder Name",
rename: "Rename",
move: "Move",
deleteImage: "Delete Image",
deleteFolder: "Delete Folder",
forceDelete: "Force Delete",
confirmDeleteTitle: "Confirm Delete",
confirmDeleteImage: "Are you sure you want to delete this image? This action cannot be undone.",
confirmDeleteFolder: "Confirm delete folder {name}?",
imageInUse: "Image is in use",
folderHasRefs: "This folder contains {count} images. They may already be referenced by articles. Deleting the folder will also delete these images permanently.",
refArticleBody: "Body reference",
refArticleCover: "Cover reference",
storageUsage: "Storage Usage",
storageUsageDetail: "{used} / {total} ({percent}%)",
quotaExceeded: "Storage is full, please clean up before uploading more.",
uploadSuccess: "Image uploaded successfully",
uploadReusedSuccess: "Matching image already exists in the library and was reused",
uploadFailed: "Image upload failed",
deleteSuccess: "Deleted successfully",
folderCreated: "Folder created successfully",
folderRenamed: "Folder renamed successfully",
folderDeleted: "Folder deleted",
imageRenamed: "Image renamed successfully",
imageMoved: "Image moved successfully",
noImages: "No images yet. Click upload to start.",
allFolders: "All Folders",
selectImage: "Select Image",
libraryTab: "Library",
},
} as const;
export default enUS;