Cordova 开发鸿蒙PC应用藏头诗应用实现技术博客

Cordova 开发鸿蒙PC应用藏头诗应用实现技术博客

目录

  1. 项目概述
  2. 技术选型
  3. 功能需求分析
  4. 实现步骤
  5. 核心代码解析
  6. API 集成详解
  7. 用户体验优化
  8. 常见问题与解决方案
  9. 最佳实践
  10. 总结

项目概述

藏头诗应用是一个基于 Cordova 框架开发的移动应用,通过调用第三方 API 实现智能诗词生成功能。用户只需输入关键字,即可生成包含藏头、藏尾、藏中等不同形式的优美诗词。

核心功能

  • 多种藏字模式:支持藏头、藏尾、藏中、递增、递减五种模式
  • 诗句格式选择:支持五言诗和七言诗
  • 押韵类型:支持双句一压、双句押韵、一三四押三种押韵方式
  • Token 管理:自动保存和加载 API Token
  • 响应式设计:完美适配移动设备和桌面浏览器
  • 用户友好界面:美观的 UI 设计和流畅的交互体验

技术选型

前端技术栈

  • HTML5:页面结构
  • CSS3:样式设计和响应式布局
  • JavaScript (ES6+):业务逻辑和 API 调用
  • Cordova:跨平台移动应用框架

API 服务

  • API 提供商:AlAPI(alapi.cn
  • API 端点https://v3.alapi.cn/api/ai/poem
  • 请求方式:POST
  • 数据格式:URL-encoded form data

image-20251121150718671


功能需求分析

1. 用户输入需求

  • API Token(必填)
  • 关键字(2-8个字,必填)
  • 诗句格式(五言/七言,可选)
  • 藏头位置(藏头/藏尾/藏中/递增/递减,可选)
  • 押韵类型(双句一压/双句押韵/一三四押,可选)

2. 功能需求

  • 表单验证
  • API 调用和错误处理
  • 结果展示(完整诗词和分行列表)
  • Token 本地存储
  • 加载状态提示
  • 错误信息提示

3. 用户体验需求

  • 响应式设计
  • 流畅的交互动画
  • 清晰的错误提示
  • Token 获取引导

实现步骤

步骤 1: 创建页面结构

创建 poem.html 文件,包含:

  • 导航栏
  • 页面标题和说明
  • 表单区域
  • 结果展示区域
  • Token 获取弹窗

步骤 2: 设计 UI 样式

使用 CSS3 实现:

  • 响应式布局
  • 渐变背景和阴影效果
  • 表单样式和焦点效果
  • 弹窗样式
  • 移动端适配

步骤 3: 实现 JavaScript 逻辑

创建 poem.js 文件,实现:

  • 表单提交处理
  • API 调用
  • 数据验证
  • 结果展示
  • Token 存储

步骤 4: 配置安全策略

更新 Content Security Policy (CSP),允许:

  • 访问 API 域名
  • 加载 iframe 内容
  • 执行必要的脚本

核心代码解析

1. HTML 结构

表单部分
<form id="poem-form">
    <div class="form-group">
        <label for="token" class="label-with-help">
            <span>API Token *</span>
            <button type="button" class="help-btn" onclick="openTokenModal()">获取 Token</button>
        </label>
        <div class="token-input-wrapper">
            <input type="text" id="token" name="token" placeholder="请输入API Token" required>
        </div>
    </div>

    <div class="form-group">
        <label for="keyword">关键字 *</label>
        <input type="text" id="keyword" name="keyword" 
               placeholder="请输入2-8个字的藏字内容,如:我喜欢你" 
               required maxlength="8" minlength="2">
    </div>

    <div class="form-row">
        <div class="form-group">
            <label for="num">诗句格式</label>
            <select id="num" name="num">
                <option value="5">五言诗</option>
                <option value="7">七言诗</option>
            </select>
        </div>

        <div class="form-group">
            <label for="type">藏头位置</label>
            <select id="type" name="type">
                <option value="1">藏头</option>
                <option value="2">藏尾</option>
                <option value="3">藏中</option>
                <option value="4">递增</option>
                <option value="5">递减</option>
            </select>
        </div>

        <div class="form-group">
            <label for="rhyme">押韵类型</label>
            <select id="rhyme" name="rhyme">
                <option value="1">双句一压</option>
                <option value="2">双句押韵</option>
                <option value="3">一三四押</option>
            </select>
        </div>
    </div>

    <button type="submit" class="submit-btn" id="submit-btn">生成藏头诗</button>
</form>
Token 获取弹窗
<div class="modal-overlay" id="token-modal" onclick="closeTokenModal(event)">
    <div class="modal-content" onclick="event.stopPropagation()">
        <div class="modal-header">
            <h2>获取 API Token</h2>
            <button class="modal-close" onclick="closeTokenModal()">&times;</button>
        </div>
        <div class="modal-body">
            <p style="margin-bottom: 15px; color: #666;">
                请在下方页面注册并获取您的 API Token,然后将 Token 复制到输入框中。
            </p>
            <iframe src="https://www.alapi.cn/aff/nutpi" id="token-iframe"></iframe>
        </div>
        <div class="modal-footer">
            <p style="margin: 0; color: #666;">
                如果页面无法加载,请
                <a href="https://www.alapi.cn/aff/nutpi" target="_blank">点击这里在新窗口打开</a>
            </p>
        </div>
    </div>
</div>

2. CSS 样式设计

响应式表单布局
.form-container {
    background-color: #f8f9fa;
    padding: 25px;
    border-radius: 8px;
    margin-bottom: 30px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.form-group {
    margin-bottom: 20px;
}

.form-group input,
.form-group select {
    width: 100%;
    padding: 12px;
    border: 1px solid #ddd;
    border-radius: 6px;
    font-size: 16px;
    box-sizing: border-box;
    transition: border-color 0.3s;
}

.form-group input:focus,
.form-group select:focus {
    outline: none;
    border-color: #3498db;
    box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}

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

@media (max-width: 768px) {
    .form-row {
        flex-direction: column;
    }
}
弹窗样式
.modal-overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 1000;
    justify-content: center;
    align-items: center;
}

.modal-overlay.show {
    display: flex;
}

.modal-content {
    background-color: white;
    border-radius: 8px;
    width: 90%;
    max-width: 600px;
    max-height: 80vh;
    overflow: hidden;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
    display: flex;
    flex-direction: column;
}

3. JavaScript 核心逻辑

表单提交处理
// 表单提交处理
document.getElementById('poem-form').addEventListener('submit', function(e) {
    e.preventDefault();
    generatePoem();
});
API 调用函数
function generatePoem() {
    const form = document.getElementById('poem-form');
    const submitBtn = document.getElementById('submit-btn');
    const loading = document.getElementById('loading');
    const errorMessage = document.getElementById('error-message');
    const resultContainer = document.getElementById('result-container');
    
    // 获取表单数据
    const formData = {
        token: document.getElementById('token').value.trim(),
        keyword: document.getElementById('keyword').value.trim(),
        num: document.getElementById('num').value,
        type: document.getElementById('type').value,
        rhyme: document.getElementById('rhyme').value
    };
    
    // 验证关键字长度
    if (formData.keyword.length < 2 || formData.keyword.length > 8) {
        showError('关键字长度必须在2-8个字之间');
        return;
    }
    
    // 验证token
    if (!formData.token) {
        showError('请输入API Token');
        return;
    }
    
    // 显示加载状态
    submitBtn.disabled = true;
    submitBtn.textContent = '生成中...';
    loading.classList.add('show');
    errorMessage.classList.remove('show');
    resultContainer.classList.remove('show');
    
    // 构建请求参数
    const params = new URLSearchParams();
    params.append('token', formData.token);
    params.append('keyword', formData.keyword);
    params.append('num', formData.num);
    params.append('type', formData.type);
    params.append('rhyme', formData.rhyme);
    
    // 发送POST请求
    fetch('https://v3.alapi.cn/api/ai/poem', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: params.toString()
    })
    .then(response => {
        if (!response.ok) {
            throw new Error('网络请求失败: ' + response.status);
        }
        return response.json();
    })
    .then(data => {
        console.log('API响应:', data);
        
        if (data.success && data.code === 200) {
            displayPoem(data.data);
        } else {
            showError(data.message || '生成失败,请检查参数');
        }
    })
    .catch(error => {
        console.error('请求错误:', error);
        showError('请求失败: ' + error.message + '。请检查网络连接或API Token是否正确');
    })
    .finally(() => {
        // 恢复按钮状态
        submitBtn.disabled = false;
        submitBtn.textContent = '生成藏头诗';
        loading.classList.remove('show');
    });
}
结果展示函数
function displayPoem(data) {
    const resultContainer = document.getElementById('result-container');
    const poemText = document.getElementById('poem-text');
    const poemList = document.getElementById('poem-list');
    
    // 显示诗词文本
    if (data.poem) {
        poemText.textContent = data.poem;
    } else {
        poemText.textContent = '未生成诗词内容';
    }
    
    // 显示诗词列表
    poemList.innerHTML = '';
    if (data.list && Array.isArray(data.list)) {
        data.list.forEach((line, index) => {
            const li = document.createElement('li');
            li.textContent = `${index + 1}. ${line}`;
            poemList.appendChild(li);
        });
    }
    
    // 显示结果容器
    resultContainer.classList.add('show');
    
    // 滚动到结果区域
    resultContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
Token 本地存储
// 页面加载完成后的初始化
document.addEventListener('DOMContentLoaded', function() {
    // 尝试从本地存储加载token
    const savedToken = localStorage.getItem('poem_api_token');
    if (savedToken) {
        document.getElementById('token').value = savedToken;
    }
    
    // 保存token到本地存储
    document.getElementById('token').addEventListener('blur', function() {
        const token = this.value.trim();
        if (token) {
            localStorage.setItem('poem_api_token', token);
        }
    });
    
    console.log('藏头诗页面加载完成');
});
弹窗控制函数
// 打开 Token 获取弹窗
function openTokenModal() {
    document.getElementById('token-modal').classList.add('show');
    document.body.style.overflow = 'hidden'; // 禁止背景滚动
}

// 关闭 Token 获取弹窗
function closeTokenModal(event) {
    if (event && event.target !== event.currentTarget) {
        return; // 如果点击的是弹窗内容,不关闭
    }
    document.getElementById('token-modal').classList.remove('show');
    document.body.style.overflow = ''; // 恢复背景滚动
}

// ESC 键关闭弹窗
document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape') {
        const modal = document.getElementById('token-modal');
        if (modal.classList.contains('show')) {
            closeTokenModal();
        }
    }
});

API 集成详解

API 请求参数

参数名类型必填说明示例
tokenstring接口调用token,需要在token管理中创建qlVquQZPYSeaCi6u
keywordstring藏字内容,2-8个字我喜欢你
numstring诗句格式,5五言诗[默认]、7七言诗5
typestring藏头位置,1藏头[默认]、2藏尾、3藏中、4递增、5递减1
rhymestring押韵类型,1双句一压[默认]、2双句押韵、3一三四押1

API 响应格式

{
    "request_id": "726522748246958080",
    "success": true,
    "message": "success",
    "code": 200,
    "data": {
        "keyword": "天下第一",
        "poem": "天香飘户月枝春,下感知己时横流。第一莫教渔父见,一归华表好增伤。",
        "list": [
            "天香飘户月枝春",
            "下感知己时横流",
            "第一莫教渔父见",
            "一归华表好增伤"
        ]
    },
    "time": 1734176521,
    "usage": 0
}

请求实现

// 构建请求参数
const params = new URLSearchParams();
params.append('token', formData.token);
params.append('keyword', formData.keyword);
params.append('num', formData.num);
params.append('type', formData.type);
params.append('rhyme', formData.rhyme);

// 发送POST请求
fetch('https://v3.alapi.cn/api/ai/poem', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: params.toString()
})

错误处理

.then(response => {
    if (!response.ok) {
        throw new Error('网络请求失败: ' + response.status);
    }
    return response.json();
})
.then(data => {
    if (data.success && data.code === 200) {
        displayPoem(data.data);
    } else {
        showError(data.message || '生成失败,请检查参数');
    }
})
.catch(error => {
    console.error('请求错误:', error);
    showError('请求失败: ' + error.message + '。请检查网络连接或API Token是否正确');
});

用户体验优化

1. 表单验证

前端验证
// 验证关键字长度
if (formData.keyword.length < 2 || formData.keyword.length > 8) {
    showError('关键字长度必须在2-8个字之间');
    return;
}

// 验证token
if (!formData.token) {
    showError('请输入API Token');
    return;
}
HTML5 验证
<input type="text" id="keyword" name="keyword" 
       placeholder="请输入2-8个字的藏字内容,如:我喜欢你" 
       required maxlength="8" minlength="2">

2. 加载状态提示

// 显示加载状态
submitBtn.disabled = true;
submitBtn.textContent = '生成中...';
loading.classList.add('show');

// 恢复按钮状态
submitBtn.disabled = false;
submitBtn.textContent = '生成藏头诗';
loading.classList.remove('show');
.loading {
    text-align: center;
    padding: 20px;
    color: #666;
    display: none;
}

.loading.show {
    display: block;
}

.loading::after {
    content: '...';
    animation: dots 1.5s steps(4, end) infinite;
}

@keyframes dots {
    0%, 20% { content: '.'; }
    40% { content: '..'; }
    60%, 100% { content: '...'; }
}

3. 错误提示

function showError(message) {
    const errorMessage = document.getElementById('error-message');
    errorMessage.textContent = message;
    errorMessage.classList.add('show');
}
.error-message {
    background-color: #fee;
    color: #c00;
    padding: 15px;
    border-radius: 6px;
    border-left: 4px solid #f00;
    margin-top: 20px;
    display: none;
}

.error-message.show {
    display: block;
}

4. Token 自动保存

// 页面加载时恢复 Token
const savedToken = localStorage.getItem('poem_api_token');
if (savedToken) {
    document.getElementById('token').value = savedToken;
}

// 输入框失去焦点时保存 Token
document.getElementById('token').addEventListener('blur', function() {
    const token = this.value.trim();
    if (token) {
        localStorage.setItem('poem_api_token', token);
    }
});

5. 结果展示优化

// 滚动到结果区域
resultContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
.poem-text {
    font-size: 20px;
    line-height: 2;
    color: #2c3e50;
    text-align: center;
    font-family: 'KaiTi', '楷体', serif;
    white-space: pre-line;
}

常见问题与解决方案

问题 1: 跨域请求失败

原因:浏览器的同源策略限制

解决方案

  1. 配置正确的 CSP 策略
  2. 使用 fetch API(Cordova 环境支持跨域)
  3. 确保 API 服务器支持 CORS
<meta http-equiv="Content-Security-Policy" 
      content="default-src 'self' data: https://ssl.gstatic.com https://v3.alapi.cn https://www.alapi.cn 'unsafe-eval'; 
               style-src 'self' 'unsafe-inline'; 
               media-src *; 
               img-src 'self' data: content:; 
               script-src 'self' 'unsafe-inline' 'unsafe-eval'; 
               connect-src 'self' https://v3.alapi.cn https://www.alapi.cn; 
               frame-src https://www.alapi.cn;">

问题 2: iframe 无法加载

原因:CSP 策略限制或网络问题

解决方案

  1. 添加 frame-src 指令到 CSP
  2. 提供备用链接在新窗口打开
<div class="modal-footer">
    <p style="margin: 0; color: #666;">
        如果页面无法加载,请
        <a href="https://www.alapi.cn/aff/nutpi" target="_blank">点击这里在新窗口打开</a>
    </p>
</div>

问题 3: Token 丢失

原因:浏览器清除缓存或 localStorage 被清空

解决方案

  1. 使用 localStorage 持久化存储
  2. 添加 Token 输入提示
  3. 提供快速获取 Token 的入口

问题 4: API 请求超时

原因:网络延迟或 API 服务器响应慢

解决方案

// 添加超时处理
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时

fetch('https://v3.alapi.cn/api/ai/poem', {
    method: 'POST',
    signal: controller.signal,
    // ... 其他配置
})
.then(response => {
    clearTimeout(timeoutId);
    // ... 处理响应
})
.catch(error => {
    if (error.name === 'AbortError') {
        showError('请求超时,请检查网络连接后重试');
    }
});

问题 5: 移动端显示问题

原因:响应式设计不完善

解决方案

@media (max-width: 768px) {
    .form-row {
        flex-direction: column;
    }
    .modal-content {
        width: 95%;
        max-height: 90vh;
    }
    .modal-body iframe {
        height: 400px;
    }
    .token-input-wrapper {
        flex-direction: column;
    }
    .help-btn {
        margin-top: 8px;
        width: 100%;
    }
}

最佳实践

1. 代码组织

  • 分离关注点:HTML 负责结构,CSS 负责样式,JavaScript 负责逻辑
  • 模块化设计:将功能拆分为独立的函数
  • 命名规范:使用有意义的变量和函数名

2. 错误处理

// 完善的错误处理链
fetch(url, options)
    .then(response => {
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        return response.json();
    })
    .then(data => {
        if (data.success) {
            // 处理成功情况
        } else {
            throw new Error(data.message || '未知错误');
        }
    })
    .catch(error => {
        // 统一错误处理
        console.error('Error:', error);
        showError(error.message);
    });

3. 用户体验

  • 即时反馈:按钮状态、加载提示、错误信息
  • 数据持久化:Token 自动保存
  • 无障碍访问:合理的标签和提示文字
  • 响应式设计:适配各种屏幕尺寸

4. 性能优化

// 防抖处理(避免频繁请求)
let debounceTimer;
function generatePoemDebounced() {
    clearTimeout(debounceTimer);
    debounceTimer = setTimeout(() => {
        generatePoem();
    }, 300);
}

// 请求取消(避免重复请求)
let currentController = null;
function generatePoem() {
    // 取消之前的请求
    if (currentController) {
        currentController.abort();
    }
    
    currentController = new AbortController();
    fetch(url, {
        signal: currentController.signal,
        // ...
    });
}

5. 安全性

  • 输入验证:前端和后端双重验证
  • CSP 配置:限制资源加载来源
  • Token 安全:不在代码中硬编码 Token
  • HTTPS:生产环境使用 HTTPS

项目结构

www/
├── poem.html          # 藏头诗页面
├── js/
│   └── poem.js        # 藏头诗功能脚本
├── index.html         # 首页
├── about.html         # 关于我们
└── css/
    └── index.css      # 样式文件

harmonyos/entry/src/main/resources/rawfile/www/
├── poem.html          # 藏头诗页面(同步)
├── js/
│   └── poem.js        # 藏头诗功能脚本(同步)
└── ...

部署说明

1. 开发环境

# 添加浏览器平台(用于测试)
hcordova platform add browser

# 运行浏览器预览
hcordova run browser

2. 生产环境

# 构建 HarmonyOS 应用
hcordova build harmonyos

# 构建 Android 应用
hcordova build android

# 构建 iOS 应用(仅 macOS)
hcordova build ios

3. 配置检查

  • ✅ 检查 config.xml 中的 CSP 配置
  • ✅ 确认 API 域名在白名单中
  • ✅ 测试 Token 获取功能
  • ✅ 验证表单验证逻辑

测试建议

1. 功能测试

  • 表单验证测试(关键字长度、必填项)
  • API 调用测试(正常情况、错误情况)
  • Token 存储测试(保存、加载)
  • 弹窗功能测试(打开、关闭、ESC 键)

2. 兼容性测试

  • 不同浏览器测试(Chrome、Safari、Firefox)
  • 移动设备测试(iOS、Android、HarmonyOS)
  • 不同屏幕尺寸测试

3. 性能测试

  • API 响应时间
  • 页面加载速度
  • 内存使用情况

扩展功能建议

1. 历史记录

// 保存生成历史
function saveHistory(poemData) {
    const history = JSON.parse(localStorage.getItem('poem_history') || '[]');
    history.unshift({
        keyword: poemData.keyword,
        poem: poemData.poem,
        timestamp: Date.now()
    });
    // 只保留最近 50 条
    if (history.length > 50) {
        history.pop();
    }
    localStorage.setItem('poem_history', JSON.stringify(history));
}

2. 分享功能

// 分享诗词
function sharePoem(poem) {
    if (navigator.share) {
        navigator.share({
            title: '我生成的藏头诗',
            text: poem
        });
    } else {
        // 复制到剪贴板
        navigator.clipboard.writeText(poem);
        alert('诗词已复制到剪贴板');
    }
}

3. 收藏功能

// 收藏诗词
function favoritePoem(poemData) {
    const favorites = JSON.parse(localStorage.getItem('poem_favorites') || '[]');
    favorites.push(poemData);
    localStorage.setItem('poem_favorites', JSON.stringify(favorites));
}

4. 导出功能

// 导出为图片
function exportAsImage(poem) {
    // 使用 canvas 或第三方库生成图片
    // 例如使用 html2canvas
}

总结

本文详细介绍了如何在 Cordova 应用中实现藏头诗生成功能,包括:

  1. 完整的实现方案:从页面设计到 API 集成
  2. 用户体验优化:表单验证、加载提示、错误处理
  3. Token 管理:自动保存和获取引导
  4. 响应式设计:完美适配各种设备
  5. 最佳实践:代码组织、错误处理、性能优化

关键技术点

  • Fetch API:现代化的网络请求方式
  • LocalStorage:客户端数据持久化
  • Modal 弹窗:良好的用户交互体验
  • CSP 配置:安全策略配置
  • 响应式 CSS:移动端适配

项目亮点

  • 🎨 美观的 UI 设计:渐变背景、阴影效果、流畅动画
  • 🔒 完善的错误处理:网络错误、API 错误、用户输入错误
  • 💾 智能的 Token 管理:自动保存、一键获取
  • 📱 完美的移动端适配:响应式布局、触摸优化
  • 良好的性能:快速响应、流畅交互

通过本文的指导,您可以快速实现一个功能完整、用户体验良好的藏头诗应用。希望本文对您的开发工作有所帮助!


作者: 坚果派开发团队
最后更新: 2025年
版本: 1.0

参考资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序媛夏天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值