欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

在这里插入图片描述

📌 模块概述

评分系统模块是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原生能力进行复杂的统计计算。

在实现这个模块时,需要注意评分输入的便利性、统计分析的准确性、以及用户体验的流畅性。通过合理的架构设计,可以构建出高效、易用的评分系统。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐