|
<!DOCTYPE html> |
|
<html lang="zh-CN"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>电影信息管理系统</title> |
|
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet"> |
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> |
|
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> |
|
</head> |
|
<body class="bg-gray-100"> |
|
<div class="container mx-auto px-4 py-8"> |
|
<h1 class="text-4xl font-bold mb-8 text-center text-blue-600">电影信息管理系统</h1> |
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8"> |
|
<div class="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow duration-300"> |
|
<h2 class="text-xl font-semibold mb-4 text-blue-500">评分最高的电影</h2> |
|
<div id="highest-rated-movie" class="text-gray-700"></div> |
|
</div> |
|
|
|
<div class="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow duration-300"> |
|
<h2 class="text-xl font-semibold mb-4 text-blue-500">票房最高的电影</h2> |
|
<div id="highest-grossing-movie" class="text-gray-700"></div> |
|
</div> |
|
|
|
<div class="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow duration-300"> |
|
<h2 class="text-xl font-semibold mb-4 text-blue-500">电影年份分布</h2> |
|
<canvas id="movies-by-year-chart"></canvas> |
|
</div> |
|
</div> |
|
|
|
<div class="bg-white rounded-lg shadow-md p-6 mb-8 hover:shadow-lg transition-shadow duration-300"> |
|
<h2 class="text-2xl font-semibold mb-4 text-blue-500">电影数据分析</h2> |
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
<div class="flex flex-col items-center"> |
|
<h3 class="text-lg font-semibold mb-2 text-gray-700">类型分布</h3> |
|
<div style="width: 300px; height: 300px;"> |
|
<canvas id="genre-distribution-chart"></canvas> |
|
</div> |
|
</div> |
|
<div class="flex flex-col items-center"> |
|
<h3 class="text-lg font-semibold mb-2 text-gray-700">评分与票房关系</h3> |
|
<div style="width: 100%; height: 300px;"> |
|
<canvas id="rating-boxoffice-chart"></canvas> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="bg-white rounded-lg shadow-md p-6 mb-8 hover:shadow-lg transition-shadow duration-300"> |
|
<h2 class="text-2xl font-semibold mb-4 text-blue-500">电影搜索和筛选</h2> |
|
<div class="flex flex-wrap gap-4 mb-4"> |
|
<input id="search-input" type="text" placeholder="搜索电影..." class="p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-400"> |
|
<select id="genre-filter" class="p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-400"> |
|
<option value="">所有类型</option> |
|
</select> |
|
<select id="year-filter" class="p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-400"> |
|
<option value="">所有年份</option> |
|
</select> |
|
<select id="language-filter" class="p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-400"> |
|
<option value="">所有语言</option> |
|
</select> |
|
<select id="sort-by" class="p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-400"> |
|
<option value="year">年份</option> |
|
<option value="rating">评分</option> |
|
<option value="box_office">票房</option> |
|
</select> |
|
<select id="sort-order" class="p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-400"> |
|
<option value="desc">降序</option> |
|
<option value="asc">升序</option> |
|
</select> |
|
<button id="apply-filters" class="bg-blue-500 text-white p-2 rounded hover:bg-blue-600 transition-colors duration-300">应用筛选</button> |
|
</div> |
|
</div> |
|
|
|
<div class="mt-8"> |
|
<h2 class="text-2xl font-semibold mb-4 text-blue-500">电影列表</h2> |
|
<div id="movie-list" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"></div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
let allMovies = []; |
|
|
|
async function fetchData() { |
|
try { |
|
const [movies, highestRated, highestGrossing, genres, languages] = await Promise.all([ |
|
axios.get('/api/movies'), |
|
axios.get('/api/highest-rated'), |
|
axios.get('/api/highest-grossing'), |
|
axios.get('/api/genres'), |
|
axios.get('/api/languages') |
|
]); |
|
|
|
allMovies = movies.data; |
|
displayHighestRated(highestRated.data); |
|
displayHighestGrossing(highestGrossing.data); |
|
displayMoviesByYearChart(allMovies); |
|
displayGenreDistributionChart(allMovies); |
|
displayRatingBoxOfficeChart(allMovies); |
|
populateFilters(genres.data, languages.data); |
|
displayAllMovies(allMovies); |
|
} catch (error) { |
|
console.error('Error fetching data:', error); |
|
alert('加载数据时出错,请刷新页面重试。'); |
|
} |
|
} |
|
|
|
function displayHighestRated(movie) { |
|
document.getElementById('highest-rated-movie').innerHTML = ` |
|
<p class="font-semibold">${movie.title} (${movie.year}年)</p> |
|
<p>评分: ${movie.rating}</p> |
|
<p>导演: ${movie.director}</p> |
|
`; |
|
} |
|
|
|
function displayHighestGrossing(movie) { |
|
document.getElementById('highest-grossing-movie').innerHTML = ` |
|
<p class="font-semibold">${movie.title} (${movie.year}年)</p> |
|
<p>票房: ${movie.box_office}百万美元</p> |
|
<p>导演: ${movie.director}</p> |
|
`; |
|
} |
|
|
|
function displayMoviesByYearChart(movies) { |
|
const years = movies.map(movie => movie.year); |
|
const counts = {}; |
|
years.forEach(year => counts[year] = (counts[year] || 0) + 1); |
|
|
|
const ctx = document.getElementById('movies-by-year-chart').getContext('2d'); |
|
new Chart(ctx, { |
|
type: 'bar', |
|
data: { |
|
labels: Object.keys(counts).sort(), |
|
datasets: [{ |
|
label: '电影数量', |
|
data: Object.keys(counts).sort().map(year => counts[year]), |
|
backgroundColor: 'rgba(59, 130, 246, 0.6)' |
|
}] |
|
}, |
|
options: { |
|
scales: { |
|
y: { |
|
beginAtZero: true, |
|
ticks: { |
|
stepSize: 1 |
|
} |
|
} |
|
} |
|
} |
|
}); |
|
} |
|
|
|
function displayGenreDistributionChart(movies) { |
|
const genres = movies.map(movie => movie.genre); |
|
const counts = {}; |
|
genres.forEach(genre => counts[genre] = (counts[genre] || 0) + 1); |
|
|
|
const ctx = document.getElementById('genre-distribution-chart').getContext('2d'); |
|
new Chart(ctx, { |
|
type: 'pie', |
|
data: { |
|
labels: Object.keys(counts), |
|
datasets: [{ |
|
data: Object.values(counts), |
|
backgroundColor: [ |
|
'#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF', |
|
'#FF9F40', '#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0' |
|
] |
|
}] |
|
}, |
|
options: { |
|
responsive: true, |
|
maintainAspectRatio: false, |
|
plugins: { |
|
legend: { |
|
position: 'right', |
|
labels: { |
|
boxWidth: 12 |
|
} |
|
} |
|
} |
|
} |
|
}); |
|
} |
|
|
|
function displayRatingBoxOfficeChart(movies) { |
|
const ctx = document.getElementById('rating-boxoffice-chart').getContext('2d'); |
|
new Chart(ctx, { |
|
type: 'scatter', |
|
data: { |
|
datasets: [{ |
|
label: '评分 vs 票房', |
|
data: movies.map(movie => ({ |
|
x: movie.rating, |
|
y: movie.box_office |
|
})), |
|
backgroundColor: 'rgba(59, 130, 246, 0.6)' |
|
}] |
|
}, |
|
options: { |
|
responsive: true, |
|
maintainAspectRatio: false, |
|
scales: { |
|
x: { |
|
title: { |
|
display: true, |
|
text: '评分' |
|
}, |
|
min: 5, |
|
max: 10 |
|
}, |
|
y: { |
|
title: { |
|
display: true, |
|
text: '票房 (百万美元)' |
|
}, |
|
min: 0 |
|
} |
|
} |
|
} |
|
}); |
|
} |
|
|
|
function populateFilters(genres, languages) { |
|
const yearFilter = document.getElementById('year-filter'); |
|
const genreFilter = document.getElementById('genre-filter'); |
|
const languageFilter = document.getElementById('language-filter'); |
|
|
|
const years = [...new Set(allMovies.map(movie => movie.year))].sort((a, b) => b - a); |
|
years.forEach(year => { |
|
const option = document.createElement('option'); |
|
option.value = year; |
|
option.textContent = year; |
|
yearFilter.appendChild(option); |
|
}); |
|
genres.forEach(genre => { |
|
const option = document.createElement('option'); |
|
option.value = genre; |
|
option.textContent = genre; |
|
genreFilter.appendChild(option); |
|
}); |
|
|
|
languages.forEach(language => { |
|
const option = document.createElement('option'); |
|
option.value = language; |
|
option.textContent = language; |
|
languageFilter.appendChild(option); |
|
}); |
|
} |
|
|
|
function displayAllMovies(movies) { |
|
const movieList = document.getElementById('movie-list'); |
|
movieList.innerHTML = movies.map(movie => ` |
|
<div class="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow duration-300"> |
|
<h3 class="text-lg font-semibold mb-2 text-blue-500">${movie.title} (${movie.year}年)</h3> |
|
<p><span class="font-semibold">导演:</span> ${movie.director}</p> |
|
<p><span class="font-semibold">类型:</span> ${movie.genre}</p> |
|
<p><span class="font-semibold">评分:</span> ${movie.rating}</p> |
|
<p><span class="font-semibold">时长:</span> ${movie.duration}分钟</p> |
|
<p><span class="font-semibold">票房:</span> ${movie.box_office}百万美元</p> |
|
<p><span class="font-semibold">语言:</span> ${movie.language}</p> |
|
</div> |
|
`).join(''); |
|
} |
|
|
|
async function applyFilters() { |
|
const searchQuery = document.getElementById('search-input').value; |
|
const genre = document.getElementById('genre-filter').value; |
|
const year = document.getElementById('year-filter').value; |
|
const language = document.getElementById('language-filter').value; |
|
const sortBy = document.getElementById('sort-by').value; |
|
const sortOrder = document.getElementById('sort-order').value; |
|
|
|
try { |
|
const response = await axios.get('/api/movies/filter', { |
|
params: { genre, year, language } |
|
}); |
|
|
|
let filteredMovies = response.data; |
|
|
|
if (searchQuery) { |
|
filteredMovies = filteredMovies.filter(movie => |
|
movie.title.toLowerCase().includes(searchQuery.toLowerCase()) || |
|
movie.director.toLowerCase().includes(searchQuery.toLowerCase()) |
|
); |
|
} |
|
|
|
filteredMovies.sort((a, b) => { |
|
const valueA = a[sortBy]; |
|
const valueB = b[sortBy]; |
|
if (sortOrder === 'asc') { |
|
return valueA > valueB ? 1 : -1; |
|
} else { |
|
return valueA < valueB ? 1 : -1; |
|
} |
|
}); |
|
|
|
displayAllMovies(filteredMovies); |
|
} catch (error) { |
|
console.error('Error applying filters:', error); |
|
alert('应用筛选时出错,请重试。'); |
|
} |
|
} |
|
|
|
document.getElementById('apply-filters').addEventListener('click', applyFilters); |
|
|
|
fetchData(); |
|
</script> |
|
</body> |
|
</html> |