鸿蒙系统专利管理系统

项目概述

这是一个基于Web的专利管理系统,专门为鸿蒙系统环境优化,支持专利信息管理、年费提醒、数据导入导出等功能。

技术架构

  • 前端: HTML5, CSS3, JavaScript ES6
  • 存储: 浏览器localStorage
  • 数据格式: CSV
  • 兼容性: 鸿蒙系统及主流浏览器

核心功能

1. 专利信息管理

  • 添加专利: 通过表单录入专利基本信息
  • 编辑专利: 修改现有专利信息
  • 删除专利: 移除不需要的专利记录
  • 搜索专利: 按名称、专利号、申请人等字段搜索

2. 年费管理

  • 缴费提醒: 自动计算年费缴纳时间
  • 颜色标识 :
    • 黄色背景:60天内到期(即将缴费)
    • 红色背景:已逾期未缴费
  • 状态显示: 显示距离缴费日的天数

3. 数据导入导出

  • CSV导出: 将所有专利数据导出为CSV文件
  • CSV导入: 从CSV文件批量导入专利数据
  • 数据备份: 支持数据备份和恢复

数据字段说明

表格

字段名 类型 说明
name String 专利名称(必填)
number String 专利号(必填,唯一)
inventor String 发明人
applicant String 申请人
contact String 联系电话
feeAmount Number 年费金额(元)
filingDate Date 申请日期
grantDate Date 授权日期
dueDate Date 年费缴纳日(必填)
type String 专利类型(发明/实用新型/外观设计)
status String 状态(申请中/已授权/失效)
notes String 备注信息

用户界面

主界面布局

text

编辑

复制代码
[统计卡片区] - 总专利数、即将缴费、已逾期数量
[添加表单区] - 新增/编辑专利信息
[操作工具栏] - 搜索框、导出导入、清空数据按钮
[数据表格区] - 展示所有专利信息

统计面板

  • 总专利数:当前系统中专利总数
  • 即将缴费:未来60天内需要缴费的专利数
  • 已逾期:已经超过缴费期限的专利数

操作指南

添加新专利

  1. 在表单中填写专利信息
  2. 确保专利号不重复
  3. 点击"保存专利"按钮

编辑专利

  1. 在表格中点击对应专利的"编辑"按钮
  2. 修改所需字段
  3. 提交保存

导入CSV数据

  1. 准备符合格式的CSV文件
  2. 点击"导入数据(CSV)"按钮
  3. 选择文件并确认导入
  4. 数据将覆盖现有记录

导出CSV数据

  1. 点击"导出数据(CSV)"按钮
  2. 自动下载包含所有专利信息的CSV文件

数据验证规则

  • 专利名称和专利号为必填项
  • 专利号需保持唯一性
  • 日期格式为YYYY-MM-DD
  • 年费金额为数字格式

鸿蒙系统特性

  • 鸿蒙系统环境自动检测
  • 离线缓存支持(Service Worker)
  • 鸿蒙字体优化显示
  • 系统级兼容性适配

文件结构

  • 单一HTML文件部署
  • 内嵌CSS和JavaScript
  • 无需服务器环境
  • 支持本地运行

错误处理

  • 重复专利号提示
  • CSV格式错误处理
  • 数据导入验证
  • 空数据保护

维护说明

  • 数据自动保存至localStorage
  • 支持数据清空重置
  • 支持批量数据操作
  • 提供数据完整性校验

系统要求

  • 支持localStorage的现代浏览器
  • 鸿蒙系统或主流操作系统
  • 无特殊硬件要求

版本信息

  • 基于HTML5标准构建
  • 零依赖,轻量级应用
  • 持续更新维护

该系统适用于个人和小型机构的专利管理工作,提供了完整的数据生命周期管理功能。


详细代码说明

html预览

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>鸿蒙系统专利管理系统</title>
    <style>
        :root {
            --primary-color: #007BFF;
            --warning-color: #FFC107;
            --danger-color: #DC3545;
            --success-color: #28A745;
            --border-color: #dee2e6;
        }

定义CSS变量,包括主色调、警告色、危险色、成功色和边框颜色。

css

编辑

复制代码
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

重置所有元素的默认边距和内边距,并设置盒模型为border-box。

css

编辑

复制代码
        body {
            font-family: 'HarmonyOS Sans', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            background-color: #f8f9fa;
            padding: 20px;
        }

设置页面字体(优先使用鸿蒙系统字体)、行高、文字颜色和背景色。

css

编辑

复制代码
        .container {
            max-width: 1400px;
            margin: 0 auto;
            background: #fff;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 0 15px rgba(0,0,0,0.1);
        }

定义主容器样式,居中显示,白色背景,圆角和阴影效果。

css

编辑

复制代码
        h1 {
            text-align: center;
            margin-bottom: 30px;
            color: #2c3e50;
            border-bottom: 3px solid var(--primary-color);
            padding-bottom: 10px;
        }

标题样式,居中对齐,底部添加蓝色边框。

css

编辑

复制代码
        .form-container {
            background: #f1f3f5;
            padding: 20px;
            border-radius: 6px;
            margin-bottom: 30px;
            border: 1px solid var(--border-color);
        }

表单容器样式,浅灰色背景,圆角和边框。

css

编辑

复制代码
        .form-row {
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
            margin-bottom: 15px;
        }

        .form-group {
            flex: 1 1 300px;
        }

表单行使用弹性布局,允许换行,每项最小宽度300px。

css

编辑

复制代码
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            color: #495057;
        }

标签样式,块级显示,加粗,深灰色文字。

css

编辑

复制代码
        input[type="text"],
        input[type="number"],
        input[type="date"],
        input[type="tel"],
        select,
        textarea {
            width: 100%;
            padding: 10px;
            border: 1px solid var(--border-color);
            border-radius: 4px;
            font-size: 14px;
        }

统一设置各种输入控件的样式,占满父容器宽度。

css

编辑

复制代码
        .btn {
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            font-weight: bold;
            transition: all 0.3s ease;
        }

按钮基础样式,无边框,圆角,悬停过渡效果。

css

编辑

复制代码
        .btn-primary { background-color: var(--primary-color); color: white; }
        .btn-success { background-color: var(--success-color); color: white; }
        .btn-danger { background-color: var(--danger-color); color: white; }
        .btn-warning { background-color: var(--warning-color); color: #000; }
        .btn-secondary { background-color: #6c757d; color: white; }
        .btn:disabled { background-color: #6c757d; cursor: not-allowed; }

定义不同类型的按钮颜色主题。

css

编辑

复制代码
        .actions-bar {
            display: flex;
            justify-content: space-between;
            margin-bottom: 20px;
            flex-wrap: wrap;
            gap: 10px;
        }

操作栏使用弹性布局,两端对齐,支持换行。

css

编辑

复制代码
        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
            box-shadow: 0 0 5px rgba(0,0,0,0.05);
        }

        th, td {
            padding: 12px 15px;
            text-align: left;
            border-bottom: 1px solid var(--border-color);
        }

        th {
            background-color: #f8f9fa;
            font-weight: bold;
            color: #495057;
            position: sticky;
            top: 0;
        }

        tr:hover {
            background-color: #f1f3f5;
            transition: background-color 0.2s;
        }

表格样式,合并边框,悬停效果,表头固定。

css

编辑

复制代码
        .status-soon {
            background-color: #fff3cd !important; /* 黄色背景:即将到期 */
            font-weight: bold;
        }

        .status-overdue {
            background-color: #f8d7da !important; /* 红色背景:已逾期 */
            color: #721c24;
            font-weight: bold;
        }

定义状态样式,即将到期用黄色背景,已逾期用红色背景。

css

编辑

复制代码
        .modal {
            display: none;
            position: fixed;
            z-index: 1000;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.5);
        }

        .modal-content {
            background-color: #fefefe;
            margin: 10% auto;
            padding: 20px;
            border-radius: 8px;
            width: 90%;
            max-width: 500px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.3);
        }

        .close {
            color: #aaa;
            float: right;
            font-size: 28px;
            font-weight: bold;
            cursor: pointer;
        }

模态框样式,半透明遮罩层,居中弹窗。

css

编辑

复制代码
        .stats {
            display: flex;
            gap: 20px;
            margin-bottom: 20px;
            flex-wrap: wrap;
        }

        .stat-card {
            flex: 1;
            min-width: 200px;
            padding: 15px;
            background: #f8f9fa;
            border-left: 4px solid var(--primary-color);
            border-radius: 4px;
        }

        .stat-card.warning { border-left-color: var(--warning-color); }
        .stat-card.danger { border-left-color: var(--danger-color); }

        .stat-number {
            font-size: 24px;
            font-weight: bold;
            margin: 5px 0;
        }

统计卡片样式,左侧不同颜色的装饰条表示不同状态。

css

编辑

复制代码
        @media (max-width: 768px) {
            .container { padding: 15px; }
            .form-row { flex-direction: column; }
            .form-group { flex: 1 1 100%; }
            .stats { flex-direction: column; }
            .actions-bar { flex-direction: column; }
            .action-buttons { flex-direction: column; }
            table { font-size: 14px; }
            th, td { padding: 8px 10px; }
        }

响应式设计,在小屏幕设备上调整布局。

html

预览

复制代码
</head>
<body>

<div class="container">
    <div class="harmony-logo">
        <h1>📝 专利管理系统</h1>
        <span class="harmony-badge">鸿蒙系统优化版</span>
    </div>

    <!-- 统计卡片 -->
    <div class="stats">
        <div class="stat-card">
            <div>总专利数</div>
            <div id="total-count" class="stat-number">0</div>
        </div>
        <div class="stat-card warning">
            <div>即将缴费 (60天内)</div>
            <div id="soon-count" class="stat-number">0</div>
        </div>
        <div class="stat-card danger">
            <div>已逾期</div>
            <div id="overdue-count" class="stat-number">0</div>
        </div>
    </div>

页面头部和统计卡片区域,显示总专利数、即将缴费和已逾期的数量。

html

预览

复制代码
    <!-- 添加专利表单 -->
    <div class="form-container">
        <h2>添加新专利</h2>
        <form id="patentForm">
            <div class="form-row">
                <div class="form-group">
                    <label>专利名称 *</label>
                    <input type="text" id="name" required>
                </div>
                <div class="form-group">
                    <label>专利号 *</label>
                    <input type="text" id="number" required>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group">
                    <label>发明人</label>
                    <input type="text" id="inventor">
                </div>
                <div class="form-group">
                    <label>申请人</label>
                    <input type="text" id="applicant">
                </div>
            </div>
            <div class="form-row">
                <div class="form-group">
                    <label>联系电话</label>
                    <input type="tel" id="contact">
                </div>
                <div class="form-group">
                    <label>年费金额 (元)</label>
                    <input type="number" id="feeAmount" min="0" step="0.01">
                </div>
            </div>
            <div class="form-row">
                <div class="form-group">
                    <label>申请日期</label>
                    <input type="date" id="filingDate">
                </div>
                <div class="form-group">
                    <label>授权日期</label>
                    <input type="date" id="grantDate">
                </div>
                <div class="form-group">
                    <label>年费缴纳日 *</label>
                    <input type="date" id="dueDate" required>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group">
                    <label>专利类型</label>
                    <select id="type">
                        <option value="发明专利">发明专利</option>
                        <option value="实用新型">实用新型</option>
                        <option value="外观设计">外观设计</option>
                    </select>
                </div>
                <div class="form-group">
                    <label>状态</label>
                    <select id="status">
                        <option value="申请中">申请中</option>
                        <option value="已授权">已授权</option>
                        <option value="失效">失效</option>
                    </select>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group">
                    <label>备注</label>
                    <textarea id="notes"></textarea>
                </div>
            </div>
            <button type="submit" class="btn btn-primary">保存专利</button>
            <button type="button" id="cancelEdit" class="btn btn-danger" style="display:none;">取消编辑</button>
        </form>
    </div>

添加专利的表单,包含专利的各种属性字段。

html

预览

复制代码
    <!-- 操作与表格 -->
    <div class="actions-bar">
        <input type="text" id="searchInput" class="search-box" placeholder="搜索专利名称或专利号...">
        <div class="action-buttons">
            <button id="exportBtn" class="btn btn-success">导出数据 (CSV)</button>
            <button id="importBtn" class="btn btn-secondary">导入数据 (CSV)</button>
            <button id="clearBtn" class="btn btn-danger">清空所有数据</button>
        </div>
    </div>

    <table id="patentTable">
        <thead>
            <tr>
                <th>专利名称</th>
                <th>专利号</th>
                <th>申请人</th>
                <th>联系方式</th>
                <th>年费金额</th>
                <th>类型</th>
                <th>年费缴纳日</th>
                <th>状态</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            <!-- 数据将通过 JS 动态插入 -->
        </tbody>
    </table>
</div>

操作栏包含搜索框和数据导入导出按钮,表格用于显示专利列表。

html

预览

复制代码
<!-- 编辑模态框 -->
<div id="editModal" class="modal">
    <div class="modal-content">
        <span class="close">&times;</span>
        <h2>编辑专利信息</h2>
        <form id="editForm">
            <input type="hidden" id="editIndex">
            <div class="form-row">
                <div class="form-group"><label>专利名称</label><input type="text" id="editName" required></div>
                <div class="form-group"><label>专利号</label><input type="text" id="editNumber" required></div>
            </div>
            <div class="form-row">
                <div class="form-group"><label>发明人</label><input type="text" id="editInventor"></div>
                <div class="form-group"><label>申请人</label><input type="text" id="editApplicant"></div>
            </div>
            <div class="form-row">
                <div class="form-group"><label>联系电话</label><input type="tel" id="editContact"></div>
                <div class="form-group"><label>年费金额 (元)</label><input type="number" id="editFeeAmount" min="0" step="0.01"></div>
            </div>
            <div class="form-row">
                <div class="form-group"><label>申请日期</label><input type="date" id="editFilingDate"></div>
                <div class="form-group"><label>授权日期</label><input type="date" id="editGrantDate"></div>
                <div class="form-group"><label>年费缴纳日</label><input type="date" id="editDueDate" required></div>
            </div>
            <div class="form-row">
                <div class="form-group">
                    <label>类型</label>
                    <select id="editType">
                        <option value="发明专利">发明专利</option>
                        <option value="实用新型">实用新型</option>
                        <option value="外观设计">外观设计</option>
                    </select>
                </div>
                <div class="form-group">
                    <label>状态</label>
                    <select id="editStatus">
                        <option value="申请中">申请中</option>
                        <option value="已授权">已授权</option>
                        <option value="失效">失效</option>
                    </select>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group"><label>备注</label><textarea id="editNotes"></textarea></div>
            </div>
            <button type="submit" class="btn btn-primary">更新专利</button>
        </form>
    </div>
</div>

编辑专利的模态框,包含与添加表单相同的字段。

javascript

编辑

复制代码
<script>
    // 模拟数据库 (使用 localStorage)
    let patents = JSON.parse(localStorage.getItem('patents')) || [];

    // DOM 元素
    const patentForm = document.getElementById('patentForm');
    const patentTableBody = document.querySelector('#patentTable tbody');
    const searchInput = document.getElementById('searchInput');
    const cancelEditBtn = document.getElementById('cancelEdit');
    const exportBtn = document.getElementById('exportBtn');
    const importBtn = document.getElementById('importBtn');
    const clearBtn = document.getElementById('clearBtn');

    // 统计元素
    const totalCountEl = document.getElementById('total-count');
    const soonCountEl = document.getElementById('soon-count');
    const overdueCountEl = document.getElementById('overdue-count');

    // 模态框元素
    const editModal = document.getElementById('editModal');
    const closeModal = document.querySelector('.close');
    const editForm = document.getElementById('editForm');

    // 编辑状态
    let isEditing = null;

JavaScript初始化,获取DOM元素,定义全局变量。

javascript

编辑

复制代码
    // ================= 工具函数 =================

    // 保存到 LocalStorage
    function saveToStorage() {
        localStorage.setItem('patents', JSON.stringify(patents));
    }

    // 格式化日期 (YYYY-MM-DD)
    function formatDate(dateStr) {
        if (!dateStr) return '';
        const date = new Date(dateStr);
        return date.toLocaleDateString('zh-CN');
    }

    // 计算日期差 (天数)
    function getDateDiffInDays(date1, date2) {
        const diffTime = Math.abs(date2 - date1);
        return Math.ceil(diffTime / (1000 * 60 * 60 * 24)); 
    }

    // 获取专利状态类名 (用于表格高亮)
    function getStatusClass(dueDateStr) {
        if (!dueDateStr) return '';
        
        const dueDate = new Date(dueDateStr);
        const today = new Date();
        today.setHours(0,0,0,0);
        dueDate.setHours(0,0,0,0);

        // 如果年费日小于今天,说明已过期
        if (dueDate < today) {
            return 'status-overdue';
        }
        
        // 如果在60天内
        const diffDays = getDateDiffInDays(today, dueDate);
        if (diffDays <= 60) {
            return 'status-soon';
        }

        return '';
    }

定义工具函数,包括数据存储、日期格式化、计算日期差和状态判断。

javascript

编辑

复制代码
    // 解析CSV数据
    function parseCSV(csvText) {
        const lines = csvText.split(/\r?\n/);
        const headers = lines[0].split(',').map(header => header.trim().replace(/^"|"$/g, ''));
        
        const result = [];
        for (let i = 1; i < lines.length; i++) {
            if (lines[i].trim() === '') continue;
            
            const values = [];
            let current = '';
            let inQuotes = false;
            
            for (let j = 0; j < lines[i].length; j++) {
                const char = lines[i][j];
                
                if (char === '"') {
                    inQuotes = !inQuotes;
                } else if (char === ',' && !inQuotes) {
                    values.push(current.trim());
                    current = '';
                } else {
                    current += char;
                }
            }
            values.push(current.trim());
            
            // 构建对象
            const obj = {};
            headers.forEach((header, index) => {
                if (values[index]) {
                    obj[header] = values[index].replace(/^"|"$/g, '');
                } else {
                    obj[header] = '';
                }
            });
            
            result.push(obj);
        }
        
        return result;
    }

CSV解析函数,处理带引号的数据。

javascript

编辑

复制代码
    // ================= 渲染与统计 =================

    // 渲染专利列表
    function renderPatents(filteredPatents = patents) {
        patentTableBody.innerHTML = '';
        
        let soonCount = 0;
        let overdueCount = 0;

        filteredPatents.forEach((patent, index) => {
            const rowClass = getStatusClass(patent.dueDate);
            
            // 统计逻辑
            if (rowClass === 'status-overdue') overdueCount++;
            if (rowClass === 'status-soon') soonCount++;

            const row = document.createElement('tr');
            row.className = rowClass;
            row.innerHTML = `
                <td>${patent.name}</td>
                <td><strong>"${patent.number}"</strong></td>
                <td>${patent.applicant || '-'}</td>
                <td>${patent.contact || '-'}</td>
                <td>${patent.feeAmount ? '¥' + parseFloat(patent.feeAmount).toFixed(2) : '-'}</td>
                <td>${patent.type}</td>
                <td>${formatDate(patent.dueDate)} 
                    <br><small>(${getRelativeDateText(patent.dueDate)})</small>
                </td>
                <td>${patent.status}</td>
                <td class="action-btns">
                    <button class="btn btn-warning btn-edit" data-index="${index}">编辑</button>
                    <button class="btn btn-danger btn-delete" data-index="${index}">删除</button>
                </td>
            `;
            patentTableBody.appendChild(row);
        });

        // 更新统计
        totalCountEl.textContent = filteredPatents.length;
        soonCountEl.textContent = soonCount;
        overdueCountEl.textContent = overdueCount;
    }

    // 获取相对日期文本
    function getRelativeDateText(dateStr) {
        if (!dateStr) return '';
        const due = new Date(dateStr);
        const today = new Date();
        const diffDays = getDateDiffInDays(today, due);

        if (due < today) {
            return `已逾期 ${diffDays} 天`;
        } else if (diffDays === 0) {
            return `今天到期`;
        } else {
            return `还剩 ${diffDays} 天`;
        }
    }

渲染专利列表函数,动态创建表格行并更新统计数据。

javascript

编辑

复制代码
    // ================= 事件处理 =================

    // 1. 添加/更新专利
    patentForm.addEventListener('submit', function(e) {
        e.preventDefault();
        
        const patentData = {
            name: document.getElementById('name').value,
            number: document.getElementById('number').value,
            inventor: document.getElementById('inventor').value,
            applicant: document.getElementById('applicant').value,
            contact: document.getElementById('contact').value,
            feeAmount: document.getElementById('feeAmount').value,
            filingDate: document.getElementById('filingDate').value,
            grantDate: document.getElementById('grantDate').value,
            dueDate: document.getElementById('dueDate').value,
            type: document.getElementById('type').value,
            status: document.getElementById('status').value,
            notes: document.getElementById('notes').value
        };

        // 检查专利号是否重复 (添加时)
        if (isEditing === null && patents.some(p => p.number === patentData.number)) {
            alert('错误:该专利号已存在!');
            return;
        }

        if (isEditing === null) {
            // 添加模式
            patents.push(patentData);
            alert('专利添加成功!');
        } else {
            // 编辑模式
            patents[isEditing] = patentData;
            alert('专利信息已更新!');
            // 重置表单状态
            isEditing = null;
            document.getElementById('cancelEdit').style.display = 'none';
            patentForm.querySelector('button[type="submit"]').textContent = '更新专利信息';
        }

        saveToStorage();
        renderPatents();
        patentForm.reset();
    });

处理表单提交事件,区分添加和编辑模式,检查重复专利号。

javascript

编辑

复制代码
    // 2. 搜索功能
    searchInput.addEventListener('input', function() {
        const query = this.value.toLowerCase();
        const filtered = patents.filter(patent => 
            patent.name.toLowerCase().includes(query) || 
            patent.number.toLowerCase().includes(query) ||
            (patent.applicant && patent.applicant.toLowerCase().includes(query)) ||
            (patent.contact && patent.contact.toLowerCase().includes(query))
        );
        renderPatents(filtered);
    });

实时搜索功能,根据关键词过滤专利数据。

javascript

编辑

复制代码
    // 3. 删除与编辑 (事件委托)
    patentTableBody.addEventListener('click', function(e) {
        const index = e.target.dataset.index;
        if (e.target.classList.contains('btn-delete')) {
            if (confirm('确定要删除这条专利记录吗?')) {
                patents.splice(index, 1);
                saveToStorage();
                renderPatents();
            }
        }

        if (e.target.classList.contains('btn-edit')) {
            // 填充表单
            const patent = patents[index];
            document.getElementById('name').value = patent.name;
            document.getElementById('number').value = patent.number;
            document.getElementById('inventor').value = patent.inventor;
            document.getElementById('applicant').value = patent.applicant;
            document.getElementById('contact').value = patent.contact;
            document.getElementById('feeAmount').value = patent.feeAmount || '';
            document.getElementById('filingDate').value = patent.filingDate;
            document.getElementById('grantDate').value = patent.grantDate;
            document.getElementById('dueDate').value = patent.dueDate;
            document.getElementById('type').value = patent.type;
            document.getElementById('status').value = patent.status;
            document.getElementById('notes').value = patent.notes;

            // 切换到编辑模式
            isEditing = index;
            document.getElementById('cancelEdit').style.display = 'inline-block';
            patentForm.querySelector('button[type="submit"]').textContent = '更新专利信息';
        }
    });

使用事件委托处理删除和编辑按钮点击事件。

javascript

编辑

复制代码
    // 4. 取消编辑
    cancelEditBtn.addEventListener('click', function() {
        patentForm.reset();
        isEditing = null;
        this.style.display = 'none';
        patentForm.querySelector('button[type="submit"]').textContent = '保存专利';
    });

    // 5. 导出 CSV
    exportBtn.addEventListener('click', function() {
        if (patents.length === 0) {
            alert('当前没有数据可导出!');
            return;
        }

        // 构建 CSV 内容
        let csvContent = "序号,专利名称,专利号,发明人,申请人,联系电话,年费金额,申请日期,授权日期,年费日,类型,状态,备注\n";
        patents.forEach((item, index) => {
            csvContent += `"${index+1}","${item.name}","${item.number}","${item.inventor}","${item.applicant}","${item.contact || ''}","${item.feeAmount || ''}","${item.filingDate}","${item.grantDate}","${item.dueDate}","${item.type}","${item.status}","${item.notes.replace(/"/g, '""')}"\n`;
        });

        // 创建下载链接
        const blob = new Blob(['\ufeff' + csvContent], { type: 'text/csv;charset=utf-8;' });
        const link = document.createElement('a');
        const url = URL.createObjectURL(blob);
        
        link.setAttribute('href', url);
        link.setAttribute('download', `专利管理系统_数据导出_${new Date().toISOString().slice(0, 10)}.csv`);
        link.style.visibility = 'hidden';
        
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    });

取消编辑功能和CSV导出功能,创建Blob对象并触发下载。

javascript

编辑

复制代码
    // 6. 导入 CSV
    importBtn.addEventListener('click', function() {
        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.accept = '.csv';
        
        fileInput.onchange = function(e) {
            const file = e.target.files[0];
            if (!file) return;
            
            const reader = new FileReader();
            reader.onload = function(e) {
                try {
                    const csvText = e.target.result;
                    const parsedData = parseCSV(csvText);
                    
                    if (parsedData.length > 0) {
                        if (confirm(`确定要导入 ${parsedData.length} 条专利数据吗?这将覆盖现有数据!`)) {
                            // 转换CSV数据为系统格式
                            const convertedData = parsedData.map(item => ({
                                name: item['专利名称'] || item['name'] || '',
                                number: item['专利号'] || item['number'] || '',
                                inventor: item['发明人'] || item['inventor'] || '',
                                applicant: item['申请人'] || item['applicant'] || '',
                                contact: item['联系电话'] || item['contact'] || '',
                                feeAmount: item['年费金额'] || item['feeAmount'] || '',
                                filingDate: item['申请日期'] || item['filingDate'] || '',
                                grantDate: item['授权日期'] || item['grantDate'] || '',
                                dueDate: item['年费日'] || item['dueDate'] || '',
                                type: item['类型'] || item['type'] || '发明专利',
                                status: item['状态'] || item['status'] || '申请中',
                                notes: item['备注'] || item['notes'] || ''
                            }));
                            
                            patents = convertedData;
                            saveToStorage();
                            renderPatents();
                            alert('数据导入成功!');
                        }
                    } else {
                        alert('导入失败:文件中没有找到有效的专利数据!');
                    }
                } catch (error) {
                    alert('导入失败:文件格式错误或损坏!\n错误详情:' + error.message);
                }
            };
            reader.readAsText(file);
        };
        
        fileInput.click();
    });

CSV导入功能,读取文件并解析数据。

javascript

编辑

复制代码
    // 7. 清空所有数据
    clearBtn.addEventListener('click', function() {
        if (patents.length === 0) {
            alert('当前没有数据可清空!');
            return;
        }
        
        if (confirm('确定要清空所有专利数据吗?此操作不可恢复!')) {
            patents = [];
            saveToStorage();
            renderPatents();
            alert('所有数据已清空!');
        }
    });

    // 8. 模态框关闭事件
    closeModal.onclick = function() {
        editModal.style.display = 'none';
    }

    window.onclick = function(event) {
        if (event.target === editModal) {
            editModal.style.display = 'none';
        }
    }

    // 初始化渲染
    renderPatents();

    // 鸿蒙系统兼容性检查
    if ('serviceWorker' in navigator) {
        // 注册Service Worker用于离线缓存
        window.addEventListener('load', () => {
            if ('serviceWorker' in navigator) {
                navigator.serviceWorker.register('/sw.js')
                    .then(registration => console.log('SW registered'))
                    .catch(error => console.log('SW registration failed'));
            }
        });
    }

    // 添加鸿蒙系统检测
    function detectHarmonyOS() {
        const userAgent = navigator.userAgent;
        if (userAgent.includes('HarmonyOS')) {
            document.title = '鸿蒙系统专利管理系统';
            console.log('检测到鸿蒙系统环境');
        }
    }

    // 页面加载完成后检测系统
    window.addEventListener('load', detectHarmonyOS);
</script>

</body>
</html>

清空数据功能、模态框关闭事件、初始化渲染以及鸿蒙系统检测代码。整个系统使用localStorage作为本地数据存储,实现了专利的增删改查功能。


完整代码

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>鸿蒙系统专利管理系统</title>
    <style>
        :root {
            --primary-color: #007BFF;
            --warning-color: #FFC107;
            --danger-color: #DC3545;
            --success-color: #28A745;
            --border-color: #dee2e6;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: 'HarmonyOS Sans', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            background-color: #f8f9fa;
            padding: 20px;
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
            background: #fff;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 0 15px rgba(0,0,0,0.1);
        }

        h1 {
            text-align: center;
            margin-bottom: 30px;
            color: #2c3e50;
            border-bottom: 3px solid var(--primary-color);
            padding-bottom: 10px;
        }

        /* 表单样式 */
        .form-container {
            background: #f1f3f5;
            padding: 20px;
            border-radius: 6px;
            margin-bottom: 30px;
            border: 1px solid var(--border-color);
        }

        .form-row {
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
            margin-bottom: 15px;
        }

        .form-group {
            flex: 1 1 300px;
        }

        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            color: #495057;
        }

        input[type="text"],
        input[type="number"],
        input[type="date"],
        input[type="tel"],
        select,
        textarea {
            width: 100%;
            padding: 10px;
            border: 1px solid var(--border-color);
            border-radius: 4px;
            font-size: 14px;
        }

        textarea {
            resize: vertical;
            height: 60px;
        }

        .btn {
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            font-weight: bold;
            transition: all 0.3s ease;
        }

        .btn-primary {
            background-color: var(--primary-color);
            color: white;
        }

        .btn-success {
            background-color: var(--success-color);
            color: white;
        }

        .btn-danger {
            background-color: var(--danger-color);
            color: white;
        }

        .btn-warning {
            background-color: var(--warning-color);
            color: #000;
        }

        .btn-secondary {
            background-color: #6c757d;
            color: white;
        }

        .btn:disabled {
            background-color: #6c757d;
            cursor: not-allowed;
        }

        /* 操作栏 */
        .actions-bar {
            display: flex;
            justify-content: space-between;
            margin-bottom: 20px;
            flex-wrap: wrap;
            gap: 10px;
        }

        .search-box {
            padding: 8px;
            border: 1px solid var(--border-color);
            border-radius: 4px;
            flex: 1;
            min-width: 200px;
        }

        .action-buttons {
            display: flex;
            gap: 10px;
        }

        /* 表格样式 */
        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
            box-shadow: 0 0 5px rgba(0,0,0,0.05);
        }

        th, td {
            padding: 12px 15px;
            text-align: left;
            border-bottom: 1px solid var(--border-color);
        }

        th {
            background-color: #f8f9fa;
            font-weight: bold;
            color: #495057;
            position: sticky;
            top: 0;
        }

        tr:hover {
            background-color: #f1f3f5;
            transition: background-color 0.2s;
        }

        .status-soon {
            background-color: #fff3cd !important; /* 黄色背景:即将到期 */
            font-weight: bold;
        }

        .status-overdue {
            background-color: #f8d7da !important; /* 红色背景:已逾期 */
            color: #721c24;
            font-weight: bold;
        }

        .action-btns .btn {
            padding: 5px 10px;
            margin-right: 5px;
            font-size: 12px;
        }

        /* 模态框样式 */
        .modal {
            display: none;
            position: fixed;
            z-index: 1000;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.5);
        }

        .modal-content {
            background-color: #fefefe;
            margin: 10% auto;
            padding: 20px;
            border-radius: 8px;
            width: 90%;
            max-width: 500px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.3);
        }

        .close {
            color: #aaa;
            float: right;
            font-size: 28px;
            font-weight: bold;
            cursor: pointer;
        }

        .close:hover {
            color: #000;
        }

        /* 统计信息 */
        .stats {
            display: flex;
            gap: 20px;
            margin-bottom: 20px;
            flex-wrap: wrap;
        }

        .stat-card {
            flex: 1;
            min-width: 200px;
            padding: 15px;
            background: #f8f9fa;
            border-left: 4px solid var(--primary-color);
            border-radius: 4px;
        }

        .stat-card.warning {
            border-left-color: var(--warning-color);
        }

        .stat-card.danger {
            border-left-color: var(--danger-color);
        }

        .stat-number {
            font-size: 24px;
            font-weight: bold;
            margin: 5px 0;
        }

        /* 鸿蒙系统特定样式 */
        .harmony-logo {
            display: flex;
            align-items: center;
            justify-content: center;
            margin-bottom: 20px;
        }

        .harmony-logo img {
            width: 60px;
            height: 60px;
            margin-right: 15px;
        }

        .harmony-badge {
            background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
            color: white;
            padding: 5px 10px;
            border-radius: 15px;
            font-size: 12px;
            margin-left: 10px;
        }

        /* 响应式设计优化 */
        @media (max-width: 768px) {
            .container {
                padding: 15px;
            }
            
            .form-row {
                flex-direction: column;
            }
            
            .form-group {
                flex: 1 1 100%;
            }
            
            .stats {
                flex-direction: column;
            }
            
            .actions-bar {
                flex-direction: column;
            }
            
            .action-buttons {
                flex-direction: column;
            }
            
            table {
                font-size: 14px;
            }
            
            th, td {
                padding: 8px 10px;
            }
        }
    </style>
</head>
<body>

<div class="container">
    <div class="harmony-logo">
        <h1>📝 专利管理系统</h1>
        <span class="harmony-badge">鸿蒙系统优化版</span>
    </div>

    <!-- 统计卡片 -->
    <div class="stats">
        <div class="stat-card">
            <div>总专利数</div>
            <div id="total-count" class="stat-number">0</div>
        </div>
        <div class="stat-card warning">
            <div>即将缴费 (60天内)</div>
            <div id="soon-count" class="stat-number">0</div>
        </div>
        <div class="stat-card danger">
            <div>已逾期</div>
            <div id="overdue-count" class="stat-number">0</div>
        </div>
    </div>

    <!-- 添加专利表单 -->
    <div class="form-container">
        <h2>添加新专利</h2>
        <form id="patentForm">
            <div class="form-row">
                <div class="form-group">
                    <label>专利名称 *</label>
                    <input type="text" id="name" required>
                </div>
                <div class="form-group">
                    <label>专利号 *</label>
                    <input type="text" id="number" required>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group">
                    <label>发明人</label>
                    <input type="text" id="inventor">
                </div>
                <div class="form-group">
                    <label>申请人</label>
                    <input type="text" id="applicant">
                </div>
            </div>
            <div class="form-row">
                <div class="form-group">
                    <label>联系电话</label>
                    <input type="tel" id="contact">
                </div>
                <div class="form-group">
                    <label>年费金额 (元)</label>
                    <input type="number" id="feeAmount" min="0" step="0.01">
                </div>
            </div>
            <div class="form-row">
                <div class="form-group">
                    <label>申请日期</label>
                    <input type="date" id="filingDate">
                </div>
                <div class="form-group">
                    <label>授权日期</label>
                    <input type="date" id="grantDate">
                </div>
                <div class="form-group">
                    <label>年费缴纳日 *</label>
                    <input type="date" id="dueDate" required>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group">
                    <label>专利类型</label>
                    <select id="type">
                        <option value="发明专利">发明专利</option>
                        <option value="实用新型">实用新型</option>
                        <option value="外观设计">外观设计</option>
                    </select>
                </div>
                <div class="form-group">
                    <label>状态</label>
                    <select id="status">
                        <option value="申请中">申请中</option>
                        <option value="已授权">已授权</option>
                        <option value="失效">失效</option>
                    </select>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group">
                    <label>备注</label>
                    <textarea id="notes"></textarea>
                </div>
            </div>
            <button type="submit" class="btn btn-primary">保存专利</button>
            <button type="button" id="cancelEdit" class="btn btn-danger" style="display:none;">取消编辑</button>
        </form>
    </div>

    <!-- 操作与表格 -->
    <div class="actions-bar">
        <input type="text" id="searchInput" class="search-box" placeholder="搜索专利名称或专利号...">
        <div class="action-buttons">
            <button id="exportBtn" class="btn btn-success">导出数据 (CSV)</button>
            <button id="importBtn" class="btn btn-secondary">导入数据 (CSV)</button>
            <button id="clearBtn" class="btn btn-danger">清空所有数据</button>
        </div>
    </div>

    <table id="patentTable">
        <thead>
            <tr>
                <th>专利名称</th>
                <th>专利号</th>
                <th>申请人</th>
                <th>联系方式</th>
                <th>年费金额</th>
                <th>类型</th>
                <th>年费缴纳日</th>
                <th>状态</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            <!-- 数据将通过 JS 动态插入 -->
        </tbody>
    </table>
</div>

<!-- 编辑模态框 -->
<div id="editModal" class="modal">
    <div class="modal-content">
        <span class="close">&times;</span>
        <h2>编辑专利信息</h2>
        <form id="editForm">
            <input type="hidden" id="editIndex">
            <div class="form-row">
                <div class="form-group"><label>专利名称</label><input type="text" id="editName" required></div>
                <div class="form-group"><label>专利号</label><input type="text" id="editNumber" required></div>
            </div>
            <div class="form-row">
                <div class="form-group"><label>发明人</label><input type="text" id="editInventor"></div>
                <div class="form-group"><label>申请人</label><input type="text" id="editApplicant"></div>
            </div>
            <div class="form-row">
                <div class="form-group"><label>联系电话</label><input type="tel" id="editContact"></div>
                <div class="form-group"><label>年费金额 (元)</label><input type="number" id="editFeeAmount" min="0" step="0.01"></div>
            </div>
            <div class="form-row">
                <div class="form-group"><label>申请日期</label><input type="date" id="editFilingDate"></div>
                <div class="form-group"><label>授权日期</label><input type="date" id="editGrantDate"></div>
                <div class="form-group"><label>年费缴纳日</label><input type="date" id="editDueDate" required></div>
            </div>
            <div class="form-row">
                <div class="form-group">
                    <label>类型</label>
                    <select id="editType">
                        <option value="发明专利">发明专利</option>
                        <option value="实用新型">实用新型</option>
                        <option value="外观设计">外观设计</option>
                    </select>
                </div>
                <div class="form-group">
                    <label>状态</label>
                    <select id="editStatus">
                        <option value="申请中">申请中</option>
                        <option value="已授权">已授权</option>
                        <option value="失效">失效</option>
                    </select>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group"><label>备注</label><textarea id="editNotes"></textarea></div>
            </div>
            <button type="submit" class="btn btn-primary">更新专利</button>
        </form>
    </div>
</div>

<script>
    // 模拟数据库 (使用 localStorage)
    let patents = JSON.parse(localStorage.getItem('patents')) || [];

    // DOM 元素
    const patentForm = document.getElementById('patentForm');
    const patentTableBody = document.querySelector('#patentTable tbody');
    const searchInput = document.getElementById('searchInput');
    const cancelEditBtn = document.getElementById('cancelEdit');
    const exportBtn = document.getElementById('exportBtn');
    const importBtn = document.getElementById('importBtn');
    const clearBtn = document.getElementById('clearBtn');

    // 统计元素
    const totalCountEl = document.getElementById('total-count');
    const soonCountEl = document.getElementById('soon-count');
    const overdueCountEl = document.getElementById('overdue-count');

    // 模态框元素
    const editModal = document.getElementById('editModal');
    const closeModal = document.querySelector('.close');
    const editForm = document.getElementById('editForm');

    // 编辑状态
    let isEditing = null;

    // ================= 工具函数 =================

    // 保存到 LocalStorage
    function saveToStorage() {
        localStorage.setItem('patents', JSON.stringify(patents));
    }

    // 格式化日期 (YYYY-MM-DD)
    function formatDate(dateStr) {
        if (!dateStr) return '';
        const date = new Date(dateStr);
        return date.toLocaleDateString('zh-CN');
    }

    // 计算日期差 (天数)
    function getDateDiffInDays(date1, date2) {
        const diffTime = Math.abs(date2 - date1);
        return Math.ceil(diffTime / (1000 * 60 * 60 * 24)); 
    }

    // 获取专利状态类名 (用于表格高亮)
    function getStatusClass(dueDateStr) {
        if (!dueDateStr) return '';
        
        const dueDate = new Date(dueDateStr);
        const today = new Date();
        today.setHours(0,0,0,0);
        dueDate.setHours(0,0,0,0);

        // 如果年费日小于今天,说明已过期
        if (dueDate < today) {
            return 'status-overdue';
        }
        
        // 如果在60天内
        const diffDays = getDateDiffInDays(today, dueDate);
        if (diffDays <= 60) {
            return 'status-soon';
        }

        return '';
    }

    // 解析CSV数据
    function parseCSV(csvText) {
        const lines = csvText.split(/\r?\n/);
        const headers = lines[0].split(',').map(header => header.trim().replace(/^"|"$/g, ''));
        
        const result = [];
        for (let i = 1; i < lines.length; i++) {
            if (lines[i].trim() === '') continue;
            
            const values = [];
            let current = '';
            let inQuotes = false;
            
            for (let j = 0; j < lines[i].length; j++) {
                const char = lines[i][j];
                
                if (char === '"') {
                    inQuotes = !inQuotes;
                } else if (char === ',' && !inQuotes) {
                    values.push(current.trim());
                    current = '';
                } else {
                    current += char;
                }
            }
            values.push(current.trim());
            
            // 构建对象
            const obj = {};
            headers.forEach((header, index) => {
                if (values[index]) {
                    obj[header] = values[index].replace(/^"|"$/g, '');
                } else {
                    obj[header] = '';
                }
            });
            
            result.push(obj);
        }
        
        return result;
    }

    // ================= 渲染与统计 =================

    // 渲染专利列表
    function renderPatents(filteredPatents = patents) {
        patentTableBody.innerHTML = '';
        
        let soonCount = 0;
        let overdueCount = 0;

        filteredPatents.forEach((patent, index) => {
            const rowClass = getStatusClass(patent.dueDate);
            
            // 统计逻辑
            if (rowClass === 'status-overdue') overdueCount++;
            if (rowClass === 'status-soon') soonCount++;

            const row = document.createElement('tr');
            row.className = rowClass;
            row.innerHTML = `
                <td>${patent.name}</td>
                <td><strong>"${patent.number}"</strong></td>
                <td>${patent.applicant || '-'}</td>
                <td>${patent.contact || '-'}</td>
                <td>${patent.feeAmount ? '¥' + parseFloat(patent.feeAmount).toFixed(2) : '-'}</td>
                <td>${patent.type}</td>
                <td>${formatDate(patent.dueDate)} 
                    <br><small>(${getRelativeDateText(patent.dueDate)})</small>
                </td>
                <td>${patent.status}</td>
                <td class="action-btns">
                    <button class="btn btn-warning btn-edit" data-index="${index}">编辑</button>
                    <button class="btn btn-danger btn-delete" data-index="${index}">删除</button>
                </td>
            `;
            patentTableBody.appendChild(row);
        });

        // 更新统计
        totalCountEl.textContent = filteredPatents.length;
        soonCountEl.textContent = soonCount;
        overdueCountEl.textContent = overdueCount;
    }

    // 获取相对日期文本
    function getRelativeDateText(dateStr) {
        if (!dateStr) return '';
        const due = new Date(dateStr);
        const today = new Date();
        const diffDays = getDateDiffInDays(today, due);

        if (due < today) {
            return `已逾期 ${diffDays} 天`;
        } else if (diffDays === 0) {
            return `今天到期`;
        } else {
            return `还剩 ${diffDays} 天`;
        }
    }

    // ================= 事件处理 =================

    // 1. 添加/更新专利
    patentForm.addEventListener('submit', function(e) {
        e.preventDefault();
        
        const patentData = {
            name: document.getElementById('name').value,
            number: document.getElementById('number').value,
            inventor: document.getElementById('inventor').value,
            applicant: document.getElementById('applicant').value,
            contact: document.getElementById('contact').value,
            feeAmount: document.getElementById('feeAmount').value,
            filingDate: document.getElementById('filingDate').value,
            grantDate: document.getElementById('grantDate').value,
            dueDate: document.getElementById('dueDate').value,
            type: document.getElementById('type').value,
            status: document.getElementById('status').value,
            notes: document.getElementById('notes').value
        };

        // 检查专利号是否重复 (添加时)
        if (isEditing === null && patents.some(p => p.number === patentData.number)) {
            alert('错误:该专利号已存在!');
            return;
        }

        if (isEditing === null) {
            // 添加模式
            patents.push(patentData);
            alert('专利添加成功!');
        } else {
            // 编辑模式
            patents[isEditing] = patentData;
            alert('专利信息已更新!');
            // 重置表单状态
            isEditing = null;
            document.getElementById('cancelEdit').style.display = 'none';
            patentForm.querySelector('button[type="submit"]').textContent = '更新专利信息';
        }

        saveToStorage();
        renderPatents();
        patentForm.reset();
    });

    // 2. 搜索功能
    searchInput.addEventListener('input', function() {
        const query = this.value.toLowerCase();
        const filtered = patents.filter(patent => 
            patent.name.toLowerCase().includes(query) || 
            patent.number.toLowerCase().includes(query) ||
            (patent.applicant && patent.applicant.toLowerCase().includes(query)) ||
            (patent.contact && patent.contact.toLowerCase().includes(query))
        );
        renderPatents(filtered);
    });

    // 3. 删除与编辑 (事件委托)
    patentTableBody.addEventListener('click', function(e) {
        const index = e.target.dataset.index;
        if (e.target.classList.contains('btn-delete')) {
            if (confirm('确定要删除这条专利记录吗?')) {
                patents.splice(index, 1);
                saveToStorage();
                renderPatents();
            }
        }

        if (e.target.classList.contains('btn-edit')) {
            // 填充表单
            const patent = patents[index];
            document.getElementById('name').value = patent.name;
            document.getElementById('number').value = patent.number;
            document.getElementById('inventor').value = patent.inventor;
            document.getElementById('applicant').value = patent.applicant;
            document.getElementById('contact').value = patent.contact;
            document.getElementById('feeAmount').value = patent.feeAmount || '';
            document.getElementById('filingDate').value = patent.filingDate;
            document.getElementById('grantDate').value = patent.grantDate;
            document.getElementById('dueDate').value = patent.dueDate;
            document.getElementById('type').value = patent.type;
            document.getElementById('status').value = patent.status;
            document.getElementById('notes').value = patent.notes;

            // 切换到编辑模式
            isEditing = index;
            document.getElementById('cancelEdit').style.display = 'inline-block';
            patentForm.querySelector('button[type="submit"]').textContent = '更新专利信息';
        }
    });

    // 4. 取消编辑
    cancelEditBtn.addEventListener('click', function() {
        patentForm.reset();
        isEditing = null;
        this.style.display = 'none';
        patentForm.querySelector('button[type="submit"]').textContent = '保存专利';
    });

    // 5. 导出 CSV
    exportBtn.addEventListener('click', function() {
        if (patents.length === 0) {
            alert('当前没有数据可导出!');
            return;
        }

        // 构建 CSV 内容
        let csvContent = "序号,专利名称,专利号,发明人,申请人,联系电话,年费金额,申请日期,授权日期,年费日,类型,状态,备注\n";
        patents.forEach((item, index) => {
            csvContent += `"${index+1}","${item.name}","${item.number}","${item.inventor}","${item.applicant}","${item.contact || ''}","${item.feeAmount || ''}","${item.filingDate}","${item.grantDate}","${item.dueDate}","${item.type}","${item.status}","${item.notes.replace(/"/g, '""')}"\n`;
        });

        // 创建下载链接
        const blob = new Blob(['\ufeff' + csvContent], { type: 'text/csv;charset=utf-8;' });
        const link = document.createElement('a');
        const url = URL.createObjectURL(blob);
        
        link.setAttribute('href', url);
        link.setAttribute('download', `专利管理系统_数据导出_${new Date().toISOString().slice(0, 10)}.csv`);
        link.style.visibility = 'hidden';
        
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    });

    // 6. 导入 CSV
    importBtn.addEventListener('click', function() {
        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.accept = '.csv';
        
        fileInput.onchange = function(e) {
            const file = e.target.files[0];
            if (!file) return;
            
            const reader = new FileReader();
            reader.onload = function(e) {
                try {
                    const csvText = e.target.result;
                    const parsedData = parseCSV(csvText);
                    
                    if (parsedData.length > 0) {
                        if (confirm(`确定要导入 ${parsedData.length} 条专利数据吗?这将覆盖现有数据!`)) {
                            // 转换CSV数据为系统格式
                            const convertedData = parsedData.map(item => ({
                                name: item['专利名称'] || item['name'] || '',
                                number: item['专利号'] || item['number'] || '',
                                inventor: item['发明人'] || item['inventor'] || '',
                                applicant: item['申请人'] || item['applicant'] || '',
                                contact: item['联系电话'] || item['contact'] || '',
                                feeAmount: item['年费金额'] || item['feeAmount'] || '',
                                filingDate: item['申请日期'] || item['filingDate'] || '',
                                grantDate: item['授权日期'] || item['grantDate'] || '',
                                dueDate: item['年费日'] || item['dueDate'] || '',
                                type: item['类型'] || item['type'] || '发明专利',
                                status: item['状态'] || item['status'] || '申请中',
                                notes: item['备注'] || item['notes'] || ''
                            }));
                            
                            patents = convertedData;
                            saveToStorage();
                            renderPatents();
                            alert('数据导入成功!');
                        }
                    } else {
                        alert('导入失败:文件中没有找到有效的专利数据!');
                    }
                } catch (error) {
                    alert('导入失败:文件格式错误或损坏!\n错误详情:' + error.message);
                }
            };
            reader.readAsText(file);
        };
        
        fileInput.click();
    });

    // 7. 清空所有数据
    clearBtn.addEventListener('click', function() {
        if (patents.length === 0) {
            alert('当前没有数据可清空!');
            return;
        }
        
        if (confirm('确定要清空所有专利数据吗?此操作不可恢复!')) {
            patents = [];
            saveToStorage();
            renderPatents();
            alert('所有数据已清空!');
        }
    });

    // 8. 模态框关闭事件
    closeModal.onclick = function() {
        editModal.style.display = 'none';
    }

    window.onclick = function(event) {
        if (event.target === editModal) {
            editModal.style.display = 'none';
        }
    }

    // 初始化渲染
    renderPatents();

    // 鸿蒙系统兼容性检查
    if ('serviceWorker' in navigator) {
        // 注册Service Worker用于离线缓存
        window.addEventListener('load', () => {
            if ('serviceWorker' in navigator) {
                navigator.serviceWorker.register('/sw.js')
                    .then(registration => console.log('SW registered'))
                    .catch(error => console.log('SW registration failed'));
            }
        });
    }

    // 添加鸿蒙系统检测
    function detectHarmonyOS() {
        const userAgent = navigator.userAgent;
        if (userAgent.includes('HarmonyOS')) {
            document.title = '鸿蒙系统专利管理系统';
            console.log('检测到鸿蒙系统环境');
        }
    }

    // 页面加载完成后检测系统
    window.addEventListener('load', detectHarmonyOS);
</script>

</body>
</html>
相关推荐
南村群童欺我老无力.2 小时前
Flutter 框架跨平台鸿蒙开发 - 打造经典连连看游戏
flutter·游戏·华为·harmonyos
弓.长.2 小时前
React Native 鸿蒙跨平台开发:实现一个模拟计算器
react native·react.js·harmonyos
南村群童欺我老无力.2 小时前
Flutter 框架跨平台鸿蒙开发 - 打造表情包制作器应用
开发语言·javascript·flutter·华为·harmonyos
IT陈图图2 小时前
漫游记:基于 Flutter × OpenHarmony 的旅行记录应用首页实现
flutter·华为·鸿蒙·openharmony
小白阿龙3 小时前
鸿蒙+flutter 跨平台开发——合成大西瓜游戏的实现
flutter·游戏·harmonyos·鸿蒙
小白阿龙4 小时前
鸿蒙+flutter 跨平台开发——快捷记账应用的开发
flutter·华为·harmonyos·鸿蒙
Felven4 小时前
华为鲲鹏920s处理器在统信系统下接收外部GPIO中断问题
华为·统信·鲲鹏920s·gpio中断
菜鸟小芯4 小时前
【开源鸿蒙跨平台开发先锋训练营】DAY4~DAY6 OpenHarmony版Flutter本地美食清单上拉加载 + 下拉刷新 + 数据加载提示实现
flutter·harmonyos
funnycoffee1234 小时前
思科,华为,华三交换机清空端口配置命令
华为·清空接口配置