评分系统 - Cordova 与 OpenHarmony 混合开发实战
本文介绍了开源鸿蒙跨平台开发者社区中的MovieTracker应用评分系统模块。该模块支持1-10分的影片评分,提供星级和数字评分方式,以及评分统计和分析功能。文章详细说明了评分系统的完整流程,包括评分输入验证、保存更新和统计分析三个步骤。同时提供了评分系统的HTML结构和JavaScript实现代码,展示了如何加载影片列表、设置评分值以及更新评分显示。该系统通过Cordova框架与OpenHar
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

📌 模块概述
评分系统模块是MovieTracker应用中用于管理影片评分的功能。用户可以为已看的影片评分,评分范围是1-10分。评分系统提供了多种评分方式,如星级评分、数字评分等。同时支持查看评分统计,如平均评分、评分分布等。
该模块的主要功能包括:添加评分、编辑评分、删除评分、查看评分统计、评分分布分析等。通过Cordova框架与OpenHarmony原生能力的结合,实现了完整的评分管理和统计分析。
评分系统需要处理评分数据的存储和查询,同时需要提供评分统计和分析功能。
🔗 完整流程
第一步:评分输入与验证
用户可以通过多种方式输入评分,如点击星级、输入数字等。评分输入需要进行验证,确保评分在1-10的范围内。
评分输入需要提供实时反馈,告知用户当前的评分值。同时需要支持清除评分,用户可以移除已有的评分。
第二步:评分保存与更新
评分输入完成后需要保存到数据库。如果影片已有评分,则更新现有评分;否则创建新的评分记录。
保存过程需要更新影片的评分字段,同时需要记录评分的时间。
第三步:评分统计与分析
评分系统需要提供评分统计功能,如计算平均评分、评分分布等。这些统计信息可以帮助用户了解自己的评分习惯。
同时需要提供评分排行,显示评分最高的影片、评分最低的影片等。
🔧 Web代码实现
评分系统HTML结构
<div id="rating-page" class="page">
<div class="page-header">
<h2>评分系统</h2>
</div>
<div class="rating-container">
<div class="rating-input-section">
<h3>为影片评分</h3>
<div class="form-group">
<label>选择影片:</label>
<select id="rating-movie-select" class="form-select" onchange="loadMovieForRating()">
<option value="">请选择影片</option>
</select>
</div>
<div class="form-group">
<label>评分:</label>
<div class="star-rating" id="star-rating">
<span class="star" onclick="setRating(1)">★</span>
<span class="star" onclick="setRating(2)">★</span>
<span class="star" onclick="setRating(3)">★</span>
<span class="star" onclick="setRating(4)">★</span>
<span class="star" onclick="setRating(5)">★</span>
<span class="star" onclick="setRating(6)">★</span>
<span class="star" onclick="setRating(7)">★</span>
<span class="star" onclick="setRating(8)">★</span>
<span class="star" onclick="setRating(9)">★</span>
<span class="star" onclick="setRating(10)">★</span>
</div>
<span id="rating-value" class="rating-value">0/10</span>
</div>
<div class="form-group">
<label>评论:</label>
<textarea id="rating-comment" placeholder="请输入评论(可选)" class="form-textarea"></textarea>
</div>
<div class="form-actions">
<button class="btn btn-primary" onclick="saveRating()">保存评分</button>
<button class="btn btn-secondary" onclick="clearRating()">清除评分</button>
</div>
</div>
<div class="rating-stats-section">
<h3>评分统计</h3>
<div class="stat-item">
<span class="label">已评分影片:</span>
<span id="rated-count">0</span>
</div>
<div class="stat-item">
<span class="label">平均评分:</span>
<span id="avg-rating">0.0</span>
</div>
<div class="stat-item">
<span class="label">最高评分:</span>
<span id="max-rating">0</span>
</div>
<div class="stat-item">
<span class="label">最低评分:</span>
<span id="min-rating">0</span>
</div>
<h4>评分分布</h4>
<div id="rating-distribution" class="rating-distribution"></div>
</div>
</div>
<div class="rating-list-section">
<h3>评分排行</h3>
<div id="rating-list" class="rating-list"></div>
</div>
</div>
这个HTML结构定义了评分系统页面的布局。包括评分输入、统计信息、评分排行等部分。
评分管理实现
let currentRatingMovieId = null;
let currentRating = 0;
async function loadMoviesForRating() {
try {
const movies = await db.getMoviesByStatus('watched');
const select = document.getElementById('rating-movie-select');
movies.forEach(movie => {
const option = document.createElement('option');
option.value = movie.id;
option.textContent = `${movie.title} (${movie.year})`;
select.appendChild(option);
});
} catch (error) {
console.error('加载影片失败:', error);
}
}
async function loadMovieForRating() {
const movieId = parseInt(document.getElementById('rating-movie-select').value);
if (!movieId) return;
try {
currentRatingMovieId = movieId;
const movie = await db.getMovie(movieId);
if (movie && movie.rating) {
currentRating = movie.rating;
updateStarDisplay(movie.rating);
document.getElementById('rating-value').textContent = `${movie.rating}/10`;
} else {
currentRating = 0;
updateStarDisplay(0);
document.getElementById('rating-value').textContent = '0/10';
}
} catch (error) {
console.error('加载影片失败:', error);
}
}
function setRating(rating) {
currentRating = rating;
updateStarDisplay(rating);
document.getElementById('rating-value').textContent = `${rating}/10`;
}
function updateStarDisplay(rating) {
const stars = document.querySelectorAll('.star-rating .star');
stars.forEach((star, index) => {
if (index < rating) {
star.classList.add('active');
} else {
star.classList.remove('active');
}
});
}
async function saveRating() {
if (!currentRatingMovieId) {
showError('请先选择影片');
return;
}
if (currentRating === 0) {
showError('请选择评分');
return;
}
try {
const comment = document.getElementById('rating-comment').value;
await db.updateMovie(currentRatingMovieId, {
rating: currentRating,
ratingComment: comment,
ratedDate: new Date().toISOString()
});
showSuccess('评分已保存');
loadRatingStats();
loadRatingList();
} catch (error) {
console.error('保存评分失败:', error);
showError('保存评分失败');
}
}
async function clearRating() {
if (!currentRatingMovieId) return;
if (confirm('确定要清除该影片的评分吗?')) {
try {
await db.updateMovie(currentRatingMovieId, {
rating: null,
ratingComment: null
});
currentRating = 0;
updateStarDisplay(0);
document.getElementById('rating-value').textContent = '0/10';
document.getElementById('rating-comment').value = '';
showSuccess('评分已清除');
loadRatingStats();
loadRatingList();
} catch (error) {
console.error('清除评分失败:', error);
showError('清除评分失败');
}
}
}
这个函数实现了评分的输入、保存和清除功能。
评分统计
async function loadRatingStats() {
try {
const movies = await db.getAllMovies();
const ratedMovies = movies.filter(m => m.rating);
if (ratedMovies.length === 0) {
document.getElementById('rated-count').textContent = '0';
document.getElementById('avg-rating').textContent = '0.0';
document.getElementById('max-rating').textContent = '0';
document.getElementById('min-rating').textContent = '0';
return;
}
const ratings = ratedMovies.map(m => m.rating);
const avgRating = (ratings.reduce((a, b) => a + b, 0) / ratings.length).toFixed(1);
const maxRating = Math.max(...ratings);
const minRating = Math.min(...ratings);
document.getElementById('rated-count').textContent = ratedMovies.length;
document.getElementById('avg-rating').textContent = avgRating;
document.getElementById('max-rating').textContent = maxRating;
document.getElementById('min-rating').textContent = minRating;
// 显示评分分布
displayRatingDistribution(ratings);
} catch (error) {
console.error('加载评分统计失败:', error);
}
}
function displayRatingDistribution(ratings) {
const distribution = {};
for (let i = 1; i <= 10; i++) {
distribution[i] = ratings.filter(r => r === i).length;
}
const container = document.getElementById('rating-distribution');
container.innerHTML = '';
for (let i = 1; i <= 10; i++) {
const count = distribution[i];
const percentage = (count / ratings.length * 100).toFixed(0);
const item = document.createElement('div');
item.className = 'distribution-item';
item.innerHTML = `
<span class="rating-label">${i}分</span>
<div class="bar">
<div class="fill" style="width: ${percentage}%"></div>
</div>
<span class="count">${count}</span>
`;
container.appendChild(item);
}
}
async function loadRatingList() {
try {
const movies = await db.getAllMovies();
const ratedMovies = movies.filter(m => m.rating).sort((a, b) => b.rating - a.rating);
const container = document.getElementById('rating-list');
container.innerHTML = '';
if (ratedMovies.length === 0) {
container.innerHTML = '<p class="empty-message">暂无评分</p>';
return;
}
ratedMovies.slice(0, 20).forEach((movie, index) => {
const item = document.createElement('div');
item.className = 'rating-item';
item.innerHTML = `
<span class="rank">#${index + 1}</span>
<span class="title">${movie.title}</span>
<span class="rating">⭐ ${movie.rating}/10</span>
`;
container.appendChild(item);
});
} catch (error) {
console.error('加载评分列表失败:', error);
}
}
这个函数实现了评分统计和排行功能。
🔌 OpenHarmony原生代码
评分系统插件
// RatingPlugin.ets
import { webview } from '@kit.ArkWeb';
import { common } from '@kit.AbilityKit';
export class RatingPlugin {
private context: common.UIAbilityContext;
constructor(context: common.UIAbilityContext) {
this.context = context;
}
public registerRating(controller: webview.WebviewController): void {
controller.registerJavaScriptProxy({
object: new RatingBridge(),
name: 'ratingNative',
methodList: ['calculateStats', 'validateRating']
});
}
}
评分统计实现
export class RatingBridge {
public calculateStats(moviesJson: string): string {
try {
const movies = JSON.parse(moviesJson);
const ratedMovies = movies.filter((m: any) => m.rating);
if (ratedMovies.length === 0) {
return JSON.stringify({
count: 0,
average: 0,
max: 0,
min: 0
});
}
const ratings = ratedMovies.map((m: any) => m.rating);
const sum = ratings.reduce((a: number, b: number) => a + b, 0);
const average = parseFloat((sum / ratings.length).toFixed(1));
const max = Math.max(...ratings);
const min = Math.min(...ratings);
return JSON.stringify({
count: ratedMovies.length,
average: average,
max: max,
min: min
});
} catch (error) {
return JSON.stringify({
error: error.message
});
}
}
public validateRating(rating: number): string {
try {
if (rating < 1 || rating > 10) {
return JSON.stringify({
valid: false,
message: '评分必须在1-10之间'
});
}
return JSON.stringify({
valid: true,
message: '评分有效'
});
} catch (error) {
return JSON.stringify({
valid: false,
error: error.message
});
}
}
}
Web-Native通信
调用原生统计功能
async function calculateRatingStats() {
try {
const movies = await db.getAllMovies();
if (window.ratingNative) {
const statsResult = window.ratingNative.calculateStats(
JSON.stringify(movies)
);
const stats = JSON.parse(statsResult);
console.log('评分统计:', stats);
}
} catch (error) {
console.error('计算统计失败:', error);
}
}
📝 总结
评分系统模块展示了Cordova与OpenHarmony混合开发中的评分管理和统计分析功能。通过Web层提供完整的评分界面和统计展示,同时利用OpenHarmony原生能力进行复杂的统计计算。
在实现这个模块时,需要注意评分输入的便利性、统计分析的准确性、以及用户体验的流畅性。通过合理的架构设计,可以构建出高效、易用的评分系统。
更多推荐

所有评论(0)