成功添加重名文件功能
This commit is contained in:
parent
a92d9837aa
commit
728ba79eb6
@ -10,6 +10,98 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 重命名电影的处理函数
|
||||||
|
func PostRename(c *gin.Context) {
|
||||||
|
response := gin.H{
|
||||||
|
"code": http.StatusInternalServerError,
|
||||||
|
"message": "An unexpected error occurred.",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义请求结构体
|
||||||
|
type RenameRequest struct {
|
||||||
|
OldName string `json:"old_name" binding:"required"`
|
||||||
|
NewName string `json:"new_name" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var req RenameRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
response["code"] = http.StatusBadRequest
|
||||||
|
response["message"] = "Invalid request body: " + err.Error()
|
||||||
|
c.JSON(http.StatusBadRequest, response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
oldName := req.OldName
|
||||||
|
newName := req.NewName
|
||||||
|
|
||||||
|
if oldName == "" || newName == "" {
|
||||||
|
response["code"] = http.StatusBadRequest
|
||||||
|
response["message"] = "Old name and new name are required."
|
||||||
|
c.JSON(http.StatusBadRequest, response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldName == newName {
|
||||||
|
response["code"] = http.StatusBadRequest
|
||||||
|
response["message"] = "Old name and new name cannot be the same."
|
||||||
|
c.JSON(http.StatusBadRequest, response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
MovieDictLock.Lock()
|
||||||
|
defer MovieDictLock.Unlock()
|
||||||
|
|
||||||
|
// 检查新名称是否已存在
|
||||||
|
if _, exists := MovieDict[newName]; exists {
|
||||||
|
response["code"] = http.StatusConflict
|
||||||
|
response["message"] = "New name already exists."
|
||||||
|
c.JSON(http.StatusConflict, response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找旧名称对应的电影
|
||||||
|
movie, exists := MovieDict[oldName]
|
||||||
|
if exists {
|
||||||
|
// 更新电影信息
|
||||||
|
err := movie.RenameVideo(newName) // 重命名视频文件
|
||||||
|
if err != nil {
|
||||||
|
response["code"] = http.StatusInternalServerError
|
||||||
|
response["message"] = "Failed to rename video file."
|
||||||
|
c.JSON(http.StatusInternalServerError, response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(MovieDict, oldName) // 删除旧名称的条目
|
||||||
|
MovieDict[newName] = movie // 添加新名称的条目
|
||||||
|
|
||||||
|
response["code"] = http.StatusOK
|
||||||
|
response["message"] = "Movie renamed successfully."
|
||||||
|
response["data"] = gin.H{"old_name": oldName, "new_name": newName}
|
||||||
|
c.JSON(http.StatusOK, response)
|
||||||
|
log.Printf("Movie renamed from %s to %s successfully", oldName, newName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果旧名称不存在,返回404错误
|
||||||
|
response["code"] = http.StatusNotFound
|
||||||
|
response["message"] = "Movie not found."
|
||||||
|
c.JSON(http.StatusNotFound, response)
|
||||||
|
log.Printf("Movie rename failed: %s not found", oldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCategoryList(c *gin.Context) {
|
||||||
|
response := gin.H{
|
||||||
|
"code": http.StatusInternalServerError,
|
||||||
|
"message": "An unexpected error occurred.",
|
||||||
|
"data": []string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
response["code"] = http.StatusOK
|
||||||
|
response["message"] = "Success"
|
||||||
|
response["data"] = Categories
|
||||||
|
c.JSON(http.StatusOK, response)
|
||||||
|
}
|
||||||
|
|
||||||
// MovieList 电影列表
|
// MovieList 电影列表
|
||||||
func MovieList(c *gin.Context) {
|
func MovieList(c *gin.Context) {
|
||||||
response := gin.H{
|
response := gin.H{
|
||||||
@ -18,27 +110,27 @@ func MovieList(c *gin.Context) {
|
|||||||
"data": gin.H{"items": []Movie{}, "total": 0},
|
"data": gin.H{"items": []Movie{}, "total": 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
var moviesToPaginate []Movie
|
var moviesToPaginate []*Movie
|
||||||
categoryName := c.Query("category")
|
categoryName := c.Query("category")
|
||||||
searchQuery := c.Query("search") // 获取搜索参数
|
searchQuery := c.Query("search") // 获取搜索参数
|
||||||
|
|
||||||
// === 第一步:基础过滤(类别)===
|
// === 第一步:基础过滤(类别)===
|
||||||
if categoryName != "" && categoryName != "最新添加" {
|
if categoryName != "" && categoryName != "最新添加" {
|
||||||
// 普通类别:直接过滤
|
// 普通类别:直接过滤
|
||||||
for _, m := range movies {
|
for _, m := range Movies {
|
||||||
if m.TimeCategory == categoryName {
|
if m.TimeCategory == categoryName {
|
||||||
moviesToPaginate = append(moviesToPaginate, m)
|
moviesToPaginate = append(moviesToPaginate, m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 最新添加/所有电影:使用全局movies
|
// 最新添加/所有电影:使用全局movies
|
||||||
moviesToPaginate = make([]Movie, len(movies))
|
moviesToPaginate = make([]*Movie, len(Movies))
|
||||||
copy(moviesToPaginate, movies)
|
copy(moviesToPaginate, Movies)
|
||||||
}
|
}
|
||||||
|
|
||||||
// === 第二步:搜索过滤 ===
|
// === 第二步:搜索过滤 ===
|
||||||
if searchQuery != "" {
|
if searchQuery != "" {
|
||||||
var searchResults []Movie
|
var searchResults []*Movie
|
||||||
lowerSearch := strings.ToLower(searchQuery)
|
lowerSearch := strings.ToLower(searchQuery)
|
||||||
|
|
||||||
for _, m := range moviesToPaginate {
|
for _, m := range moviesToPaginate {
|
||||||
|
@ -32,7 +32,9 @@ func main() {
|
|||||||
|
|
||||||
movie := eg.Group("movie")
|
movie := eg.Group("movie")
|
||||||
movie.Use(jwtMiddleware())
|
movie.Use(jwtMiddleware())
|
||||||
movie.GET("/", MovieList)
|
movie.GET("/list/category", GetCategoryList)
|
||||||
|
movie.GET("/list", MovieList)
|
||||||
|
movie.POST("/rename", PostRename)
|
||||||
|
|
||||||
eg.Run("0.0.0.0:4444")
|
eg.Run("0.0.0.0:4444")
|
||||||
}
|
}
|
||||||
|
112
server/movie.go
112
server/movie.go
@ -12,20 +12,71 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Movie 电影信息结构
|
// Movie 电影信息结构
|
||||||
type Movie struct {
|
type Movie struct {
|
||||||
FileName string `json:"filename"` // 原始文件名(如 "video1.mp4")
|
FileName string `json:"filename"` // 原始文件名(如 "video1.mp4")
|
||||||
Image string `json:"image"` // 对应的缩略图文件名(如 "video1.png")
|
Image string `json:"image"` // 对应的缩略图文件名(如 "video1.png")
|
||||||
Duration int `json:"duration"` // 视频时长(分钟)
|
Duration int `json:"duration"` // 视频时长(分钟)
|
||||||
TimeCategory string `json:"time_category"` // 时长分类(如 "15min", "30min")
|
TimeCategory string `json:"time_category"` // 时长分类(如 "15min", "30min")
|
||||||
VideoPath string `json:"-"` // 视频文件完整路径(用于ffmpeg)
|
VideoPath string `json:"-"` // 视频文件完整路径(用于ffmpeg)
|
||||||
CreatedTime int64 `json:"created_time"` // 文件创建时间戳
|
CreatedTime int64 `json:"created_time"` // 文件创建时间戳
|
||||||
|
mu sync.Mutex // 保护视频文件的并发访问
|
||||||
}
|
}
|
||||||
|
|
||||||
var movies []Movie // 存储所有电影信息的全局切片
|
func (m *Movie) RenameVideo(newName string) error {
|
||||||
var IsRemakePNG = false // 是否重新生成所有PNG缩略图
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
|
if newName == m.FileName {
|
||||||
|
return nil // 如果新名称与当前名称相同,则不需要重命名
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文件扩展名
|
||||||
|
videoExt := filepath.Ext(m.FileName)
|
||||||
|
imageExt := filepath.Ext(m.Image)
|
||||||
|
|
||||||
|
// 获取当前文件的目录
|
||||||
|
dir := filepath.Dir(m.VideoPath)
|
||||||
|
|
||||||
|
// 构建新的文件名(不带扩展名)
|
||||||
|
newBaseName := strings.TrimSuffix(newName, videoExt)
|
||||||
|
|
||||||
|
// 重命名视频文件
|
||||||
|
oldVideoPath := m.VideoPath
|
||||||
|
newVideoPath := filepath.Join(dir, newBaseName+videoExt)
|
||||||
|
if err := os.Rename(oldVideoPath, newVideoPath); err != nil {
|
||||||
|
return fmt.Errorf("重命名视频文件失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重命名缩略图文件
|
||||||
|
oldImagePath := filepath.Join(dir, m.Image)
|
||||||
|
newImagePath := filepath.Join(dir, newBaseName+imageExt)
|
||||||
|
if _, err := os.Stat(oldImagePath); err == nil {
|
||||||
|
if err := os.Rename(oldImagePath, newImagePath); err != nil {
|
||||||
|
// 如果缩略图重命名失败,尝试回滚视频文件重命名
|
||||||
|
_ = os.Rename(newVideoPath, oldVideoPath)
|
||||||
|
return fmt.Errorf("重命名缩略图失败: %w", err)
|
||||||
|
}
|
||||||
|
m.Image = newBaseName + imageExt
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新结构体信息
|
||||||
|
m.VideoPath = newVideoPath
|
||||||
|
m.FileName = newBaseName + videoExt
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var Movies []*Movie // 存储所有电影信息的全局切片
|
||||||
|
var MovieDict = make(map[string]*Movie) // 存储需要处理缩略图的视频字典
|
||||||
|
var MovieDictLock sync.Mutex // 保护MovieDict的并发访问
|
||||||
|
|
||||||
|
var IsRemakePNG = false // 是否重新生成所有PNG缩略图
|
||||||
|
var Categories = []string{ // 分类
|
||||||
|
"15min", "30min", "60min", "大于60min", "最新添加"}
|
||||||
|
|
||||||
// getVideoDuration 获取视频时长(秒)
|
// getVideoDuration 获取视频时长(秒)
|
||||||
func getVideoDuration(videoPath string) (float64, error) {
|
func getVideoDuration(videoPath string) (float64, error) {
|
||||||
@ -131,7 +182,7 @@ func processVideoFile(path string, movieDict map[string]*Movie) error {
|
|||||||
timeCat := getTimeCategory(durationMin)
|
timeCat := getTimeCategory(durationMin)
|
||||||
|
|
||||||
// 创建电影记录
|
// 创建电影记录
|
||||||
movie := Movie{
|
movie := &Movie{
|
||||||
FileName: filename,
|
FileName: filename,
|
||||||
Image: baseName + ".png",
|
Image: baseName + ".png",
|
||||||
Duration: durationMin,
|
Duration: durationMin,
|
||||||
@ -139,7 +190,8 @@ func processVideoFile(path string, movieDict map[string]*Movie) error {
|
|||||||
VideoPath: path,
|
VideoPath: path,
|
||||||
CreatedTime: createdTime,
|
CreatedTime: createdTime,
|
||||||
}
|
}
|
||||||
movies = append(movies, movie)
|
Movies = append(Movies, movie)
|
||||||
|
MovieDict[movie.FileName] = movie // 添加到全局字典
|
||||||
|
|
||||||
// 更新缩略图字典
|
// 更新缩略图字典
|
||||||
if entry, exists := movieDict[baseName]; exists {
|
if entry, exists := movieDict[baseName]; exists {
|
||||||
@ -166,34 +218,6 @@ func getTimeCategory(durationMin int) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sortMovies 对电影列表排序
|
|
||||||
func sortMovies() {
|
|
||||||
categoryOrder := map[string]int{
|
|
||||||
"15min": 0,
|
|
||||||
"30min": 1,
|
|
||||||
"60min": 2,
|
|
||||||
"大于60min": 3,
|
|
||||||
"最新添加": 4, // 新增的"最新添加"类别
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(movies, func(i, j int) bool {
|
|
||||||
catI := categoryOrder[movies[i].TimeCategory]
|
|
||||||
catJ := categoryOrder[movies[j].TimeCategory]
|
|
||||||
|
|
||||||
if catI != catJ {
|
|
||||||
return catI < catJ
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果是recent类别,按创建时间倒序排序
|
|
||||||
if movies[i].TimeCategory == "最新添加" {
|
|
||||||
return movies[i].CreatedTime > movies[j].CreatedTime
|
|
||||||
}
|
|
||||||
|
|
||||||
// 其他类别按时长排序
|
|
||||||
return movies[i].Duration < movies[j].Duration
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// generateThumbnail 生成单个视频缩略图
|
// generateThumbnail 生成单个视频缩略图
|
||||||
func generateThumbnail(movie *Movie) {
|
func generateThumbnail(movie *Movie) {
|
||||||
baseName := strings.TrimSuffix(movie.FileName, filepath.Ext(movie.FileName))
|
baseName := strings.TrimSuffix(movie.FileName, filepath.Ext(movie.FileName))
|
||||||
@ -244,7 +268,7 @@ func initMovie() {
|
|||||||
|
|
||||||
// 创建需要处理缩略图的视频字典
|
// 创建需要处理缩略图的视频字典
|
||||||
movieDict := createMovieDict(files)
|
movieDict := createMovieDict(files)
|
||||||
movies = make([]Movie, 0) // 重置全局电影列表
|
Movies = make([]*Movie, 0) // 重置全局电影列表
|
||||||
|
|
||||||
// 遍历处理每个视频文件
|
// 遍历处理每个视频文件
|
||||||
err = filepath.WalkDir("./movie", func(path string, d fs.DirEntry, err error) error {
|
err = filepath.WalkDir("./movie", func(path string, d fs.DirEntry, err error) error {
|
||||||
@ -274,17 +298,19 @@ func initMovie() {
|
|||||||
}
|
}
|
||||||
generateThumbnail(movie)
|
generateThumbnail(movie)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("成功初始化电影数据,共找到 %d 个视频文件", len(Movies))
|
||||||
}
|
}
|
||||||
|
|
||||||
// markRecentMovies 标记最近添加的电影
|
// markRecentMovies 标记最近添加的电影
|
||||||
func markRecentMovies() {
|
func markRecentMovies() {
|
||||||
// 首先按创建时间排序
|
// 首先按创建时间排序
|
||||||
sort.Slice(movies, func(i, j int) bool {
|
sort.Slice(Movies, func(i, j int) bool {
|
||||||
return movies[i].CreatedTime > movies[j].CreatedTime
|
return Movies[i].CreatedTime > Movies[j].CreatedTime
|
||||||
})
|
})
|
||||||
|
|
||||||
// 标记前20个最新文件为"最新添加"类别
|
// 标记前20个最新文件为"最新添加"类别
|
||||||
for i := 0; i < len(movies) && i < 20; i++ {
|
for i := 0; i < len(Movies) && i < 20; i++ {
|
||||||
movies[i].TimeCategory = "最新添加"
|
Movies[i].TimeCategory = "最新添加"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
33
src/Main.jsx
33
src/Main.jsx
@ -47,6 +47,9 @@ const Main = () => {
|
|||||||
const isFirstLoad = useRef(true);
|
const isFirstLoad = useRef(true);
|
||||||
const scrollListenerActive = useRef(false);
|
const scrollListenerActive = useRef(false);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 初始化状态 - 添加搜索相关状态
|
// 初始化状态 - 添加搜索相关状态
|
||||||
const [params, setParams] = usePersistedState('params', {
|
const [params, setParams] = usePersistedState('params', {
|
||||||
lastCategory: DEFAULT_CATEGORY,
|
lastCategory: DEFAULT_CATEGORY,
|
||||||
@ -119,7 +122,7 @@ const Main = () => {
|
|||||||
const isSearch = category === SEARCH_CATEGORY;
|
const isSearch = category === SEARCH_CATEGORY;
|
||||||
const isLatest = category === '最新添加';
|
const isLatest = category === '最新添加';
|
||||||
|
|
||||||
let apiUrl = `/movie?page=${page}&limit=${LIMIT}`;
|
let apiUrl = `/movie/list?page=${page}&limit=${LIMIT}`;
|
||||||
|
|
||||||
if (isSearch) {
|
if (isSearch) {
|
||||||
// 搜索请求
|
// 搜索请求
|
||||||
@ -326,6 +329,32 @@ const Main = () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleRename = useCallback(async (oldName, newName) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/movie/rename', {
|
||||||
|
old_name: oldName,
|
||||||
|
new_name: newName
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.code === 200) {
|
||||||
|
// 重命名成功后重新加载当前页面数据
|
||||||
|
const category = params.lastCategory;
|
||||||
|
const categoryHistory = params.history[category] || { lastPage: 1 };
|
||||||
|
|
||||||
|
if (category === SEARCH_CATEGORY) {
|
||||||
|
await fetchMovies(category, categoryHistory.lastPage, params.searchQuery);
|
||||||
|
} else {
|
||||||
|
await fetchMovies(category, categoryHistory.lastPage);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(response.data.message || '重命名失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('重命名错误:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}, [fetchMovies, params.lastCategory, params.history, params.searchQuery]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container style={{ marginTop: 20 }}>
|
<Container style={{ marginTop: 20 }}>
|
||||||
{/* 添加搜索框 */}
|
{/* 添加搜索框 */}
|
||||||
@ -379,7 +408,7 @@ const Main = () => {
|
|||||||
style={{ textDecoration: 'none', paddingBottom: 10 }}
|
style={{ textDecoration: 'none', paddingBottom: 10 }}
|
||||||
onClick={handleMovieCardClick}
|
onClick={handleMovieCardClick}
|
||||||
>
|
>
|
||||||
<MovieCard movie={item} config={config} />
|
<MovieCard movie={item} config={config} onRename={handleRename} />
|
||||||
</Link>
|
</Link>
|
||||||
</Grid>
|
</Grid>
|
||||||
))}
|
))}
|
||||||
|
@ -1,19 +1,78 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import {useEffect} from 'react';
|
import { Card, CardContent, CardMedia, Typography, IconButton, TextField, Dialog, DialogActions, DialogContent, DialogTitle, Button } from '@mui/material';
|
||||||
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
|
|
||||||
import { Card, CardContent, CardMedia, Typography } from '@mui/material';
|
|
||||||
import { styled } from '@mui/system';
|
import { styled } from '@mui/system';
|
||||||
|
|
||||||
const MovieCard = ({ movie, config }) => {
|
const MovieCard = ({ movie, config, onRename }) => {
|
||||||
|
const [newName, setNewName] = useState('');
|
||||||
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
|
||||||
const truncateFilename = (filename, maxLength) => {
|
const truncateFilename = (filename, maxLength) => {
|
||||||
return filename.length > maxLength
|
return filename.length > maxLength
|
||||||
? filename.substring(0, maxLength - 3) + '...'
|
? filename.substring(0, maxLength - 3) + '...'
|
||||||
: filename;
|
: filename;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRenameClick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault(); // 添加阻止默认行为
|
||||||
|
const lastDotIndex = movie.filename.lastIndexOf('.');
|
||||||
|
const nameWithoutExt = lastDotIndex === -1
|
||||||
|
? movie.filename
|
||||||
|
: movie.filename.substring(0, lastDotIndex);
|
||||||
|
setNewName(nameWithoutExt);
|
||||||
|
setOpenDialog(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDialogClose = (e) => {
|
||||||
|
if (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
setOpenDialog(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRenameSubmit = async (e) => {
|
||||||
|
if (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault(); // 添加阻止默认行为
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newName.trim()) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const lastDotIndex = movie.filename.lastIndexOf('.');
|
||||||
|
const extension = lastDotIndex === -1
|
||||||
|
? ''
|
||||||
|
: movie.filename.substring(lastDotIndex);
|
||||||
|
const fullNewName = newName.trim() + extension;
|
||||||
|
|
||||||
|
if (fullNewName === movie.filename) {
|
||||||
|
handleDialogClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await onRename(movie.filename, fullNewName);
|
||||||
|
handleDialogClose();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('重命名失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理输入框回车键提交
|
||||||
|
const handleKeyPress = (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
handleRenameSubmit(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 阻止对话框内容区域的点击事件冒泡
|
||||||
|
const handleDialogContentClick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
const StyledCard = styled(Card)({
|
const StyledCard = styled(Card)({
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
transform: 'scale(1.05)',
|
transform: 'scale(1.05)',
|
||||||
@ -23,31 +82,84 @@ const MovieCard = ({ movie, config }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledCard>
|
<>
|
||||||
<Card
|
<StyledCard>
|
||||||
style={{
|
<Card
|
||||||
width: '100%',
|
style={{
|
||||||
height: 200,
|
width: '100%',
|
||||||
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
|
height: 200,
|
||||||
transition: 'box-shadow 0.3s ease-in-out',
|
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
|
||||||
'&:hover': {
|
transition: 'box-shadow 0.3s ease-in-out',
|
||||||
boxShadow: '0 8px 12px rgba(0, 0, 0, 0.2)',
|
position: 'relative',
|
||||||
},
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<IconButton
|
||||||
<CardMedia
|
onClick={handleRenameClick}
|
||||||
component="img"
|
size="small"
|
||||||
height="120"
|
style={{
|
||||||
image={`${window.location.origin}/res/${movie.image}`}
|
position: 'absolute',
|
||||||
/>
|
top: 5,
|
||||||
<CardContent>
|
right: 5,
|
||||||
<Typography>{truncateFilename(movie.filename, 15)}</Typography>
|
backgroundColor: 'rgba(255, 255, 255, 0.7)',
|
||||||
<Typography variant="body2" color="textSecondary">
|
zIndex: 2,
|
||||||
时长: {movie.duration} min
|
}}
|
||||||
</Typography>
|
>
|
||||||
</CardContent>
|
<EditIcon fontSize="small" />
|
||||||
</Card>
|
</IconButton>
|
||||||
</StyledCard>
|
<CardMedia
|
||||||
|
component="img"
|
||||||
|
height="120"
|
||||||
|
image={`${window.location.origin}/res/${movie.image}`}
|
||||||
|
/>
|
||||||
|
<CardContent>
|
||||||
|
<Typography>{truncateFilename(movie.filename, 15)}</Typography>
|
||||||
|
<Typography variant="body2" color="textSecondary">
|
||||||
|
时长: {movie.duration} min
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</StyledCard>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
open={openDialog}
|
||||||
|
onClose={handleDialogClose}
|
||||||
|
onClick={handleDialogContentClick} // 阻止对话框点击事件冒泡
|
||||||
|
maxWidth="sm"
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
<DialogTitle>重命名文件</DialogTitle>
|
||||||
|
<DialogContent onClick={handleDialogContentClick}>
|
||||||
|
<TextField
|
||||||
|
autoFocus
|
||||||
|
margin="dense"
|
||||||
|
label="新文件名"
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
value={newName}
|
||||||
|
onChange={(e) => setNewName(e.target.value)}
|
||||||
|
onKeyPress={handleKeyPress} // 支持回车键提交
|
||||||
|
helperText="请不要修改文件后缀"
|
||||||
|
onClick={(e) => e.stopPropagation()} // 防止输入框点击冒泡
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions onClick={handleDialogContentClick}>
|
||||||
|
<Button
|
||||||
|
onClick={handleDialogClose}
|
||||||
|
onMouseDown={(e) => e.stopPropagation()} // 防止鼠标按下事件冒泡
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleRenameSubmit}
|
||||||
|
onMouseDown={(e) => e.stopPropagation()} // 防止鼠标按下事件冒泡
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user