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

在这里插入图片描述

📌 模块概述

全部影片页面是MovieTracker应用的核心功能模块,用于展示用户收藏的所有影片。该页面采用表格或卡片列表的形式展示影片信息,支持排序、筛选、搜索等功能。用户可以在这个页面中查看、编辑、删除影片记录,以及进行批量操作。

该模块的主要功能包括:影片列表展示、分页加载、排序功能(按标题、年份、评分等)、快速搜索、批量选择、导出功能等。通过Cordova框架与OpenHarmony原生能力的结合,实现了高效的数据展示和交互。

全部影片页面需要处理大量的数据,因此性能优化至关重要。我们采用虚拟滚动、分页加载等技术来提高页面响应速度。同时,通过OpenHarmony原生插件提供文件操作、权限管理等功能。

🔗 完整流程

第一步:页面初始化与数据加载

当用户导航到全部影片页面时,首先需要从IndexedDB数据库中加载所有影片数据。这个过程需要考虑数据量的大小,采用分页加载的方式来避免一次性加载过多数据导致的性能问题。

初始化过程包括:设置分页参数(当前页码、每页数量)、从数据库查询数据、计算总页数、初始化排序和筛选条件。我们需要建立一个数据缓存机制,避免频繁的数据库查询。同时需要显示加载动画,告知用户数据正在加载中。

第二步:列表渲染与交互

完成数据加载后,需要将影片数据渲染到页面上。可以使用表格或卡片列表的形式展示。每个影片项需要包含基本信息(标题、年份、评分、分类等),以及操作按钮(查看详情、编辑、删除等)。

为了提高用户体验,需要为列表项添加hover效果、点击事件、右键菜单等交互。同时需要实现排序功能,用户可以点击列标题改变排序顺序。这些交互需要实时反馈,提供流畅的动画效果。

第三步:筛选与搜索

全部影片页面需要支持多种筛选和搜索功能。用户可以按分类、标签、评分等条件进行筛选。同时支持快速搜索,用户输入关键词可以实时搜索影片。

筛选和搜索的实现需要考虑性能,避免每次输入都进行全表扫描。可以使用防抖技术延迟搜索请求,同时在数据库中建立索引以加快查询速度。筛选条件需要保存在应用状态中,以便用户切换页面后返回时能恢复之前的筛选条件。

🔧 Web代码实现

影片列表HTML结构

<div id="all-movies-page" class="page">
    <div class="page-header">
        <h2>全部影片</h2>
        <div class="page-actions">
            <input type="text" id="search-input" placeholder="搜索影片..." class="search-box">
            <button class="btn btn-primary" onclick="app.navigateTo('add-movie')">➕ 添加影片</button>
        </div>
    </div>
    
    <div class="filters-bar">
        <select id="category-filter" onchange="filterMovies()">
            <option value="">所有分类</option>
        </select>
        <select id="sort-by" onchange="sortMovies()">
            <option value="title">按标题排序</option>
            <option value="year">按年份排序</option>
            <option value="rating">按评分排序</option>
        </select>
    </div>
    
    <div class="movies-list" id="movies-container"></div>
    
    <div class="pagination">
        <button onclick="previousPage()">上一页</button>
        <span id="page-info">第 1 页</span>
        <button onclick="nextPage()">下一页</button>
    </div>
</div>

这个HTML结构定义了全部影片页面的布局。页面头部包含标题和操作按钮,搜索框用于快速搜索。筛选栏包含分类和排序下拉菜单。影片列表是一个动态容器,将通过JavaScript填充影片数据。分页控件允许用户在不同页面之间导航。

影片列表数据加载

let currentPage = 1;
const pageSize = 10;
let allMovies = [];
let filteredMovies = [];

async function loadMoviesList() {
    try {
        allMovies = await db.getAllMovies();
        filteredMovies = [...allMovies];
        
        await loadCategories();
        renderMoviesList();
        updatePagination();
    } catch (error) {
        console.error('加载影片列表失败:', error);
        showError('加载影片列表失败');
    }
}

async function loadCategories() {
    const categories = await db.getAllCategories();
    const categorySelect = document.getElementById('category-filter');
    
    categories.forEach(cat => {
        const option = document.createElement('option');
        option.value = cat.id;
        option.textContent = cat.name;
        categorySelect.appendChild(option);
    });
}

这个函数实现了影片列表的数据加载。首先从数据库获取所有影片,然后加载分类信息填充筛选下拉菜单。使用filteredMovies变量存储当前筛选后的影片列表,这样可以在不重新查询数据库的情况下进行排序和分页操作。

影片列表渲染

function renderMoviesList() {
    const container = document.getElementById('movies-container');
    container.innerHTML = '';
    
    const start = (currentPage - 1) * pageSize;
    const end = start + pageSize;
    const pageMovies = filteredMovies.slice(start, end);
    
    pageMovies.forEach(movie => {
        const movieCard = document.createElement('div');
        movieCard.className = 'movie-card';
        movieCard.innerHTML = `
            <div class="movie-card-header">
                <h3>${movie.title}</h3>
                <span class="movie-year">${movie.year}</span>
            </div>
            <div class="movie-card-body">
                <p class="movie-category">分类: ${movie.category}</p>
                <p class="movie-rating">评分: ⭐ ${movie.rating || '未评分'}</p>
                <p class="movie-status">状态: ${movie.status === 'watched' ? '已看' : '想看'}</p>
            </div>
            <div class="movie-card-actions">
                <button onclick="app.navigateTo('movie-detail', ${movie.id})" class="btn btn-small">查看</button>
                <button onclick="app.navigateTo('edit-movie', ${movie.id})" class="btn btn-small">编辑</button>
                <button onclick="deleteMovie(${movie.id})" class="btn btn-small btn-danger">删除</button>
            </div>
        `;
        container.appendChild(movieCard);
    });
}

这个函数将影片列表渲染到页面上。首先根据当前页码和每页数量计算要显示的影片范围,然后为每个影片创建一个卡片元素。每个卡片包含影片的基本信息和操作按钮。通过onclick属性绑定事件处理器,实现查看、编辑、删除等功能。

搜索与筛选

function filterMovies() {
    const categoryId = document.getElementById('category-filter').value;
    const searchText = document.getElementById('search-input').value.toLowerCase();
    
    filteredMovies = allMovies.filter(movie => {
        const matchCategory = !categoryId || movie.categoryId === parseInt(categoryId);
        const matchSearch = !searchText || 
                           movie.title.toLowerCase().includes(searchText) ||
                           movie.director.toLowerCase().includes(searchText);
        return matchCategory && matchSearch;
    });
    
    currentPage = 1;
    renderMoviesList();
    updatePagination();
}

function sortMovies() {
    const sortBy = document.getElementById('sort-by').value;
    
    filteredMovies.sort((a, b) => {
        switch(sortBy) {
            case 'title':
                return a.title.localeCompare(b.title);
            case 'year':
                return b.year - a.year;
            case 'rating':
                return (b.rating || 0) - (a.rating || 0);
            default:
                return 0;
        }
    });
    
    renderMoviesList();
}

这个函数实现了搜索和筛选功能。filterMovies()函数根据选中的分类和搜索关键词过滤影片列表。sortMovies()函数根据选择的排序方式对影片进行排序。这两个函数都会重新渲染列表,并重置当前页码为第一页。

🔌 OpenHarmony原生代码

影片列表插件

// MovieListPlugin.ets
import { webview } from '@kit.ArkWeb';
import { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';

export class MovieListPlugin {
    private context: common.UIAbilityContext;
    
    constructor(context: common.UIAbilityContext) {
        this.context = context;
    }
    
    public registerMovieList(controller: webview.WebviewController): void {
        controller.registerJavaScriptProxy({
            object: new MovieListBridge(this.context),
            name: 'movieListNative',
            methodList: ['exportMovies', 'importMovies', 'getStorageInfo']
        });
    }
}

这个OpenHarmony原生插件为影片列表页面提供了文件操作和存储管理功能。通过registerJavaScriptProxy方法将原生对象暴露给Web层,使得JavaScript代码可以调用原生的文件操作方法。

影片数据导出

export class MovieListBridge {
    private context: common.UIAbilityContext;
    
    constructor(context: common.UIAbilityContext) {
        this.context = context;
    }
    
    public exportMovies(moviesJson: string): string {
        try {
            const movies = JSON.parse(moviesJson);
            const timestamp = new Date().getTime();
            const fileName = `movies_${timestamp}.json`;
            
            const cacheDir = this.context.cacheDir;
            const filePath = `${cacheDir}/${fileName}`;
            
            const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE);
            fileIo.writeSync(file.fd, moviesJson);
            fileIo.closeSync(file.fd);
            
            return JSON.stringify({
                success: true,
                filePath: filePath,
                fileName: fileName
            });
        } catch (error) {
            return JSON.stringify({
                success: false,
                error: error.message
            });
        }
    }
    
    public getStorageInfo(): string {
        try {
            const cacheDir = this.context.cacheDir;
            const filesDir = this.context.filesDir;
            
            return JSON.stringify({
                cacheDir: cacheDir,
                filesDir: filesDir,
                timestamp: Date.now()
            });
        } catch (error) {
            return JSON.stringify({
                error: error.message
            });
        }
    }
}

这个类实现了影片数据的导出功能。exportMovies()方法接收JSON格式的影片数据,将其写入到应用的缓存目录中。getStorageInfo()方法返回应用的存储路径信息,供Web层使用。

Web-Native通信

调用原生导出功能

async function exportMoviesToFile() {
    try {
        const moviesJson = JSON.stringify(filteredMovies);
        
        if (window.movieListNative) {
            const result = window.movieListNative.exportMovies(moviesJson);
            const exportResult = JSON.parse(result);
            
            if (exportResult.success) {
                showSuccess(`影片已导出到: ${exportResult.fileName}`);
            } else {
                showError(`导出失败: ${exportResult.error}`);
            }
        } else {
            // 降级方案:使用Web API导出
            downloadAsJson(moviesJson, 'movies.json');
        }
    } catch (error) {
        console.error('导出影片失败:', error);
        showError('导出影片失败');
    }
}

function downloadAsJson(data, filename) {
    const blob = new Blob([data], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = filename;
    link.click();
    URL.revokeObjectURL(url);
}

这个函数展示了如何调用OpenHarmony原生的导出功能。首先将影片数据转换为JSON字符串,然后调用原生的exportMovies()方法。如果原生方法不可用,提供了降级方案使用Web API进行导出。

📝 总结

全部影片页面展示了Cordova与OpenHarmony混合开发中的数据展示、交互处理和文件操作。通过Web层提供丰富的UI界面和用户交互,同时利用OpenHarmony原生能力进行文件操作和存储管理。

在实现这个模块时,需要注意大数据量的处理、搜索和筛选的性能优化、以及Web-Native通信的可靠性。通过合理的架构设计和代码优化,可以构建出高效、易用的影片管理功能。

Logo

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

更多推荐