Cordova 开发鸿蒙PC应用藏头诗应用实现技术博客
目录
项目概述
藏头诗应用是一个基于 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

功能需求分析
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()">×</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 请求参数
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| token | string | 是 | 接口调用token,需要在token管理中创建 | qlVquQZPYSeaCi6u |
| keyword | string | 是 | 藏字内容,2-8个字 | 我喜欢你 |
| num | string | 否 | 诗句格式,5五言诗[默认]、7七言诗 | 5 |
| type | string | 否 | 藏头位置,1藏头[默认]、2藏尾、3藏中、4递增、5递减 | 1 |
| rhyme | string | 否 | 押韵类型,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: 跨域请求失败
原因:浏览器的同源策略限制
解决方案:
- 配置正确的 CSP 策略
- 使用
fetchAPI(Cordova 环境支持跨域) - 确保 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 策略限制或网络问题
解决方案:
- 添加
frame-src指令到 CSP - 提供备用链接在新窗口打开
<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 被清空
解决方案:
- 使用
localStorage持久化存储 - 添加 Token 输入提示
- 提供快速获取 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 应用中实现藏头诗生成功能,包括:
- ✅ 完整的实现方案:从页面设计到 API 集成
- ✅ 用户体验优化:表单验证、加载提示、错误处理
- ✅ Token 管理:自动保存和获取引导
- ✅ 响应式设计:完美适配各种设备
- ✅ 最佳实践:代码组织、错误处理、性能优化
关键技术点
- Fetch API:现代化的网络请求方式
- LocalStorage:客户端数据持久化
- Modal 弹窗:良好的用户交互体验
- CSP 配置:安全策略配置
- 响应式 CSS:移动端适配
项目亮点
- 🎨 美观的 UI 设计:渐变背景、阴影效果、流畅动画
- 🔒 完善的错误处理:网络错误、API 错误、用户输入错误
- 💾 智能的 Token 管理:自动保存、一键获取
- 📱 完美的移动端适配:响应式布局、触摸优化
- ⚡ 良好的性能:快速响应、流畅交互
通过本文的指导,您可以快速实现一个功能完整、用户体验良好的藏头诗应用。希望本文对您的开发工作有所帮助!
作者: 坚果派开发团队
最后更新: 2025年
版本: 1.0
参考资源:

被折叠的 条评论
为什么被折叠?



