前言

1.使用MAC 开发Electron鸿蒙跨平台应用-环境搭建及常见问题

2.Electron-Vue-ElementPlus开发鸿蒙跨平台应用-AtomGit 口袋工具

3.Electron开发鸿蒙跨平台应用-组件方法封装

4.Electron跨平台开发鸿蒙应用-持续迭代应用1.0

5.Electron跨平台开发鸿蒙应用-持续迭代应用2.0

6.【实战复盘】基于 Electron + Vue3 开发 OpenHarmony 应用的阶段性总结

项目起源

不知不觉已经12月了,又是一年年末。年味越来越重了,各大直播间早已带起了年货,今天突然想做一个节日气息浓厚的小玩具,顺便跟大家分享一下,代码简单,情绪价值较高😄。

快速开始

项目的跨平台基础框架,可参照前言的文章使用MAC 开发Electron鸿蒙跨平台应用-环境搭建及常见问题

项目简介

采用vue axios快速构建

html代码如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI 智能对联生成器</title>
    <!-- 引入 Vue 3 CDN -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <!-- 引入 Axios CDN -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    
    <style>
        :root {
            --primary-red: #c91919;
            --paper-red: #e60000;
            --gold: #fcc200;
            --bg-color: #fdf6ec;
        }

        body {
            margin: 0;
            padding: 0;
            font-family: "Kaiti", "STKaiti", "KaiTi_GB2312", serif; /* 使用楷体更有韵味 */
            background-color: var(--bg-color);
            color: #333;
            display: flex;
            justify-content: center;
            min-height: 100vh;
        }

        #app {
            width: 100%;
            max-width: 600px;
            padding: 20px;
            text-align: center;
        }

        h1 {
            color: var(--primary-red);
            font-size: 2.5rem;
            margin-bottom: 30px;
            text-shadow: 2px 2px 0px rgba(0,0,0,0.1);
        }

        .input-group {
            display: flex;
            gap: 10px;
            margin-bottom: 30px;
            justify-content: center;
        }

        input {
            padding: 12px 20px;
            border: 2px solid var(--primary-red);
            border-radius: 25px;
            font-size: 1.1rem;
            width: 60%;
            outline: none;
        }

        button {
            padding: 12px 25px;
            background-color: var(--primary-red);
            color: var(--gold);
            border: none;
            border-radius: 25px;
            font-size: 1.1rem;
            cursor: pointer;
            font-weight: bold;
            transition: transform 0.1s;
        }

        button:active {
            transform: scale(0.95);
        }

        button:disabled {
            background-color: #ccc;
            cursor: not-allowed;
        }

        /* 对联展示区域 */
        .couplet-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 20px;
            margin-top: 20px;
            opacity: 0;
            animation: fadeIn 0.8s forwards;
        }

        .horizontal-scroll {
            background-color: var(--paper-red);
            color: black;
            padding: 10px 30px;
            font-size: 1.5rem;
            font-weight: bold;
            letter-spacing: 5px;
            border-radius: 4px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.2);
        }

        .vertical-scrolls {
            display: flex;
            justify-content: space-between;
            width: 80%;
            max-width: 400px;
        }

        .scroll {
            background-color: var(--paper-red);
            color: black;
            padding: 20px 15px;
            font-size: 1.8rem;
            font-weight: bold;
            writing-mode: vertical-rl; /* 文字竖排 */
            text-orientation: upright;
            border-radius: 4px;
            min-height: 200px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.2);
            letter-spacing: 5px;
        }

        .loading-text {
            color: var(--primary-red);
            font-size: 1.2rem;
            margin-top: 20px;
        }

        @keyframes fadeIn {
            to { opacity: 1; }
        }

        .tip {
            margin-top: 50px;
            font-size: 0.8rem;
            color: #999;
            font-family: sans-serif;
        }
    </style>
</head>
<body>

<div id="app">
    <h1>🧧 AI 智能对联</h1>
    
    <div class="input-group">
        <input 
            type="text" 
            v-model="keyword" 
            @keyup.enter="generateCouplet"
            placeholder="输入关键词(如:新婚、春节)..."
        >
        <button @click="generateCouplet" :disabled="loading">
            {{ loading ? '生成中...' : '求对联' }}
        </button>
    </div>

    <!-- 加载状态 -->
    <div v-if="loading" class="loading-text">
        正在挥毫泼墨,请稍候...
    </div>

    <!-- 结果展示 -->
    <div v-if="result && !loading" class="couplet-container">
        <!-- 横批 -->
        <div class="horizontal-scroll">
            {{ result.horizontal }}
        </div>
        
        <!-- 上下联 -->
        <div class="vertical-scrolls">
            <!-- 右边是上联 (传统习惯) -->
            <div class="scroll">
                {{ result.upper }}
            </div>
            <!-- 左边是下联 -->
            <div class="scroll">
                {{ result.lower }}
            </div>
        </div>
    </div>

    <div class="tip">
        当前模式: {{ useMock ? 'Mock 模拟数据' : '真实 API 请求' }}
    </div>
</div>

<script>
    const { createApp, ref, reactive } = Vue;

    createApp({
        setup() {
            // --- 配置区域 ---
            const useMock = ref(true); // 【开关】设置为 false 即可发送真实请求
            const apiUrl = 'https://api.example.com/v1/couplet'; // 替换为你的后端地址
            // ----------------
            
            const keyword = ref('');
            const loading = ref(false);
            const result = ref(null);

            // 1. Mock 模拟函数
            const fetchMockData = (kw) => {
                return new Promise((resolve) => {
                    setTimeout(() => {
                        // 根据简单的关键词返回不同的假数据,增加演示效果
                        let mockRes = {};
                        if (kw.includes('婚')) {
                            mockRes = { upper: '百年恩爱双心结', lower: '千里姻缘一线牵', horizontal: '喜结良缘' };
                        } else if (kw.includes('春') || kw.includes('年')) {
                            mockRes = { upper: '春风送暖入屠苏', lower: '瑞气盈门万象新', horizontal: '辞旧迎新' };
                        } else if (kw.includes('财')) {
                            mockRes = { upper: '生意兴隆通四海', lower: '财源茂盛达三江', horizontal: '财源广进' };
                        } else {
                            mockRes = { upper: '云间瑞气三千丈', lower: '花里春风十二楼', horizontal: '万事如意' };
                        }
                        resolve({ data: { code: 200, data: mockRes } });
                    }, 1000); // 模拟网络延迟 1秒
                });
            };

            // 2. 真实 API 请求函数
            const fetchRealApi = (kw) => {
                // 这里定义你的真实后端接口格式
                return axios.post(apiUrl, {
                    prompt: kw, // 传递给后端的参数
                    format: 'json'
                }, {
                    headers: {
                        'Content-Type': 'application/json',
                        // 'Authorization': 'Bearer YOUR_TOKEN' // 如果需要 Token
                    }
                });
            };

            // 3. 核心业务逻辑
            const generateCouplet = async () => {
                if (!keyword.value.trim()) {
                    alert('请先输入关键词~');
                    return;
                }

                loading.value = true;
                result.value = null; // 清空旧结果

                try {
                    let response;
                    
                    if (useMock.value) {
                        console.log('正在使用 Mock 数据...');
                        response = await fetchMockData(keyword.value);
                    } else {
                        console.log('正在请求真实 API...');
                        response = await fetchRealApi(keyword.value);
                    }

                    // 假设后端返回结构为: { code: 200, data: { upper: '...', lower: '...', horizontal: '...' } }
                    const { data } = response.data;
                    
                    if (data) {
                        result.value = {
                            upper: data.upper,      // 上联
                            lower: data.lower,      // 下联
                            horizontal: data.horizontal // 横批
                        };
                    } else {
                        alert('生成失败,后端返回数据格式有误');
                    }

                } catch (error) {
                    console.error('请求出错:', error);
                    alert('生成出错了,请检查网络或控制台日志');
                } finally {
                    loading.value = false;
                }
            };

            return {
                keyword,
                loading,
                result,
                generateCouplet,
                useMock
            };
        }
    }).mount('#app');
</script>

</body>
</html>

对联生成:可对接AI大模型,这边采用servless快速构建,方便好玩的laf云函数快速部署,生成对联的AI接口,laf云函数代码如下:

laf 是开源的云开发平台,提供云函数、云数据库、云存储等开箱即用的应用资源。

让开发者专注于业务开发,无需折腾服务器,快速释放创意。

import cloud from '@lafjs/cloud'; // 引入 @lafjs/cloud,用于云函数开发
const db = cloud.database(); // 获取数据库实例
import axios from 'axios'; // 引入 axios,用于 HTTP 请求
const dayjs = require('dayjs'); // 引入 dayjs,用于时间格式化
const model = "deepseek-chat"
// const modelkey = process.env.DMXAPI_KEY_FAST;
// const modelkey = "sk-3e0900bf1fe14e0ff58ef"
const modelkey = "sk-dpNPBnL0JP3"
export default async function (ctx: FunctionContext) {
  // 设置 axios 默认配置
  axios.defaults.timeout = 20000; // 设置超时时间为 5000 毫秒 (5 秒)
  const { type, prompt } = ctx.request.body || {}; // 从请求体中获取类型 (type) 和提示词 (prompt)
  const ip = ctx.headers['x-forwarded-for'] || ctx.headers['x-real-ip']; // 获取客户端 IP 地址

  // 检查必要参数是否存在
  if (!type || !prompt) {
    return {
      code: 0,
      msg: '缺少必要参数:type 或 prompt',
    };
  }


  console.log(`用户:${ip}, 类型:${type}, 时间:${dayjs().format('YYYY-MM-DD HH:mm:ss')}, 提示词:${prompt}`); // 日志记录

  let result;

  try {
    // 根据类型调用不同的生成功能
    switch (type) {
      case 'couplets': // 生成春联
        result = await handleCoupletsGeneration(ctx, type, prompt, generateCoupletsPrompt());
        break;
      case 'wish': // 生成新年祝福语
        result = await handleDirectGeneration(ctx, type, prompt, generateNewYearBlessingsPrompt());
        break;
      default:
        // 未知类型返回错误
        return {
          code: 0,
          msg: `未知的生成类型: ${type}`,
        };
    }
    return result; // 返回结果
  } catch (error) {
    console.error('生成失败:', error); // 错误日志记录
    return {
      code: 0,
      msg: '生成失败,请稍后重试',
    };
  }
}

// 处理对联生成逻辑,解析为 JSON 对象
async function handleCoupletsGeneration(ctx: FunctionContext, type: string, prompt: string, systemPrompt: string) {
  const url = "https://www.dmxapi.com/v1/chat/completions";
  const payload = JSON.stringify({
    model: model,
    messages: [
      { role: "system", content: systemPrompt }, // 系统提示,用于指定生成规则
      { role: "user", content: `我的提示是:${prompt}` }, // 用户输入内容
    ],
  });

  const headers = {
    'Accept': 'application/json',
    'Authorization': modelkey, // 从环境变量中读取 API 密钥
    'User-Agent': 'mtuopenai/1.0.0 (https://www.dmxapi.com)',
    'Content-Type': 'application/json',
  };

  const res = await fetchData(url, payload, headers, true); // 调用 API 获取结果并解析 JSON
  await saveData(ctx, type, prompt, res.data); // 保存生成结果到数据库
  return res;
}

// 处理直接生成逻辑,返回字符串内容
async function handleDirectGeneration(ctx: FunctionContext, type: string, prompt: string, systemPrompt: string) {
  const url = "https://www.dmxapi.com/v1/chat/completions";
  const payload = JSON.stringify({
    model: model,
    messages: [
      { role: "system", content: systemPrompt },
      { role: "user", content: `我的提示是:${prompt}` },
    ],
  });

  const headers = {
    'Accept': 'application/json',
    'Authorization': modelkey,
    'User-Agent': 'mtuopenai/1.0.0 (https://www.dmxapi.com)',
    'Content-Type': 'application/json',
  };

  const res = await fetchData(url, payload, headers, false); // 调用 API 获取原始字符串
  await saveData(ctx, type, prompt, res.data); // 保存结果到数据库
  return res;
}

// 生成春联的系统提示
function generateCoupletsPrompt(): string {
  return `
    请根据我的提示生成一组春联,包含上联、下联各一句,每句字数在五到十三字之间,上下联字数必须相同,并附上一个恰当的不超过五个字的横批。
    并给出一个字总结。
    不要使用生僻字和标点符号。
    格式类型:{
      "上联": "",
      "下联": "",
      "横批": "",
      "总结": ""
    }
    直接给出可以被 JSON.parse 解析的字符串,不需要解释内容。
  `;
}

// 生成新年祝福语的系统提示
function generateNewYearBlessingsPrompt(): string {
  return `
    根据我的提示生成一条新年祝福语,语言简洁、温馨、适合家庭、朋友或同事之间的交流。
    直接返回字符串内容,无需 JSON 格式。
  `;
}

// 生成朋友圈新年文案的系统提示
function generateNewYearPostsPrompt(): string {
  return `
    根据我的提示生成一条朋友圈新年文案,内容积极向上、幽默或温暖。
    直接返回字符串内容,无需 JSON 格式。
  `;
}

// 通用的 API 调用函数
async function fetchData(url: string, payload: string, headers: object, parseJson: boolean) {
  try {
    const response = await axios.post(url, payload, { headers }); // 发起 POST 请求
    const msg = response.data.choices[0].message.content; // 获取返回的内容
    if (parseJson) {
      // 需要解析为 JSON 的情况
      const parsedData = JSON.parse(msg.replace(/```json|```/g, "").trim());
      return {
        code: 1,
        data: parsedData, // 返回解析后的数据
        msg: '调用成功',
      };
    } else {
      // 不需要解析 JSON,直接返回字符串
      return {
        code: 1,
        data: msg.trim(),
        msg: '调用成功',
      };
    }
  } catch (error) {
    console.error('API 调用失败:', error); // 错误日志记录
    return {
      code: 0,
      data: null,
      msg: '小助手走神了,再来一次吧~',
    };
  }
}

// 保存生成结果到数据库
async function saveData(ctx: FunctionContext, type: string, prompt: string, data: any) {
  const ip = ctx.headers['x-forwarded-for'] || ctx.headers['x-real-ip']; // 获取 IP 地址
  await db.collection("ai_spring").add({
    type, // 生成类型
    prompt, // 用户输入提示词
    result: data, // 生成结果
    createIp: ip, // 记录请求的 IP 地址
    createTime: dayjs().format('YYYY-MM-DD HH:mm:ss'), // 记录生成时间
  });
}

Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js。凭借其优雅的 API 设计、强大的拦截器机制以及自动转换 JSON 数据等特性,它已成为前端领域的“标准配置”。

本文将带你从零开始,掌握 Axios 的核心用法,并学会如何在实际项目中进行二次封装。

为什么选择 Axios?

虽然浏览器原生提供了 fetch API,但 Axios 依然拥有显著优势:

  • 支持请求/响应拦截器(核心功能)。

  • 自动转换 JSON 数据(省去了 response.json() 的步骤)。

  • 客户端支持防御 XSRF

  • 优雅的错误处理

  • 取消请求机制。

axios基础示例:

import axios from 'axios';

// 1. 发送 GET 请求
async function getUser() {
  try {
    const response = await axios.get('/user', {
      params: { id: 123 }
    });
    console.log(response.data);
  } catch (error) {
    console.error(error);
  }
}

// 2. 发送 POST 请求
async function createUser() {
  try {
    const response = await axios.post('/user', {
      firstName: 'Fred',
      lastName: 'Flintstone'
    });
    console.log(response.data);
  } catch (error) {
    console.error(error);
  }
}
项目预览

输入提示词,点求对联按钮即可生成自己的专属对联

最后

祝大年新年快乐!愿技术突飞猛进,再攀高峰;祝鸿蒙生态繁荣昌盛,越来越好!

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

赋能鸿蒙PC开发者,共建全场景原生生态,共享一次开发多端部署创新价值。

更多推荐