【纯前端】使用AI工具cursor实现一个类似于聊天界面的备忘录APP(一)实现消息的发送、SQLite本地存储

需求背景:

很多时候,我们会拿微信或者QQ作为自己的备忘录,一是方便记录,二是可以借助聊天工具自带的功能对消息进行管理,从而实现备忘录的同等功效,但是聊天工具在备忘录管理的功能上还是缺少,例如分类管理、内容碎片化管理、归档等功能。基于此,在常见的聊天工具背景下,也实现一个既类似于聊天的一个界面(单向聊天)、也可以实现消息的管理。


需求描述:

  1. 实现聊天的功能:包括消息的发送、引用、撤回、置顶、提醒、多选、合并、删除等功能;

  2. 实现消息管理的功能:消息分类、消息的再编辑、消息的合并、删除、归档、数据迁移等功能;

  3. 实现消息的离线本地持久化存储,采用SQLite,实现本地存储。

  4. 所有功能的实现采用AI工具cursor实现。


预览效果:


1.消息的构造、发送

在主页面中,参照主流聊天工具设计自己的功能,不同的是我们不需要对方回复,也就不需要向后台发送数据。这里设计获取输入框的文本构造自己的消息newMessage,将列表数据发送给插入到SQLite。

//发送新消息
sendMessage() {
          if (this.inputMessage.trim()) {
            const now = new Date()
            const timeString = now.toISOString()
            const newMessage = {
			  uuid: this.generateUUID(),
              text: this.inputMessage.trim(),
              time: timeString,
			  category:'',
              isPinned: false,
              isRecalled: false,
              isQuoted: this.quotingMessage !== null,
              quotedText: this.quotingMessage ? this.quotingMessage.text : '',
              quotedIndex: this.quotingMessage ?  this.quotingMessage.uuid : null,
			  isMerged: false
            }
            this.messages.unshift(newMessage)
            this.inputMessage = ''
			this.saveMessagedb(newMessage)
			  this.$refs.messageInput.focus()
            })
			
          }
        },

newMessage装到现在的messages里,并把this.saveMessagedb(newMessage)更新到数据库里。

为保证数据的唯一性,添加了一个generateUUID(),作为uuid

//uuid生成
generateUUID() {
	  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
	    var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
	    return v.toString(16);
	  });
	}
2.消息的存储

数据的本地存储,考虑过本地缓存,但因为本地缓存的大小限制,以及可能影响性能,采用SQLite的方式将聊天/备忘录的数据存到本地数据库。在uniapp中,可以直接启用SQLite的插件,不需要额外的插件。

消息的存储方法放到dbHelper.js中,实现数据新增。

//数据新增
export async function saveMessageToDb(message) {
  try {
      
    const db = await getDatabase();
    
    const { uuid, text, time, category, isPinned, isRecalled, isQuoted, quotedText, quotedIndex, isMerged } = message;
    const sql = `
      INSERT INTO messages 
      (uuid, text, time, category, isPinned, isRecalled, isQuoted, quotedText, quotedIndex, isMerged) 
      VALUES 
      ('${uuid}', '${escapeSqlString(text)}', '${time}', '${category || ''}', 
       ${isPinned ? 1 : 0}, ${isRecalled ? 1 : 0}, ${isQuoted ? 1 : 0}, 
       '${quotedText ? escapeSqlString(quotedText): ''}', ${quotedIndex ? `'${quotedIndex}'` : 'NULL'}, ${isMerged ? 1 : 0})
    `;
    
    return new Promise((resolve, reject) => {
      db.executeSql({
        name: 'chat_database',
        sql: sql,
        success: function(res) {
          console.log('消息保存成功:', res);
          resolve(res);
        },
        fail: function(e) {
          console.error('保存消息失败:', JSON.stringify(e));
          reject(e);
        }
      });
    });
  } catch (error) {
    console.error('保存消息到数据库失败:', error);
    throw error;
  }
}

这里的escapeSqlString方法,是防止文本输入时,存在sql注入,因此增加一个特殊符号的替换方法,查询的时候会反替换回去。

其中,getDatabase()会去打开数据库,增加一个判断,如果数据库连接存在,则使用已存在的数据库连接。

export function getDatabase() {
  
    return new Promise((resolve, reject) => {
    if (db) {
      console.log('使用已存在的数据库连接');
      resolve(db);
      return;
    }
    plus.sqlite.openDatabase({
      name: 'chat_database',
      path: '_doc/chat_database.db',
      success: function(e) {
        console.log('数据库打开成功');
        db = plus.sqlite; 
        resolve(db);
      },
      fail: function(e) {
        console.error('数据库打开失败:', JSON.stringify(e));
        if (e.code === -1402) {
          console.log('数据库已经打开,尝试使用已打开的数据库');
          db = plus.sqlite;
          resolve(db);
        } else {
          reject(e);
        }
      }
    });

    setTimeout(() => {
      if (!db) {
        console.error('获取数据库连接超时');
        reject(new Error('获取数据库连接超时'));
      }
    }, 10000);
  });
}

到这里,我们基本实现了消息的构造、发送、存储。

3.数据加载

我们现在的数据是基于messages里进行v-for,因此,我们可以将应用退出,this.messages会清空。设置一个初始化加载方法,从数据库加载。

//数据初始化加载
async loadInitialMessages() {

	  try {
	    const messages = await loadMessagesFromDb(0, this.pageSize,true);
		this.messages = messages
	    this.loadedMessages = [...this.messages];
	  } catch (error) {
	    console.error('加载初始消息失败:', error);
	  }
	}
 mounted() {
        this.$nextTick(() => {
          this.loadInitialMessages();
        });
  }

这里,使用unescapeSqlString对防注入替换的内容反替换回去,保留原本消息。

//数据加载,添加分页查询的参数
export async function loadMessagesFromDb(offset, limit, sortDesc = false) {
  try {
    const db = await getDatabase();
    return new Promise((resolve, reject) => {
      const sql = `
        SELECT * FROM messages 
        WHERE isRecalled = 0 
        ORDER BY time ${sortDesc ? 'DESC' : 'ASC'} 
        LIMIT ${limit} OFFSET ${offset}
      `;
      db.selectSql({
        name: 'chat_database',
        sql: sql,
		success: function(res) {
          // 处理查询结果,还原特殊字符
          const processedRes = res.map(item => ({
            ...item,
            text: unescapeSqlString(item.text),
            quotedText: item.quotedText ? unescapeSqlString(item.quotedText) : ''
          }));
		  console.log('成功从数据库加载消息:', processedRes);
          resolve(processedRes);
        },
        fail: function(e) {
          console.error('加载消息失败:', JSON.stringify(e));
          reject(e);
        }
      });
    });
  } catch (error) {
    console.error('加载消息时发生错误:', error);
    throw error;
  }
}

这会便可以验证我们的数据存储是否生效,当然,我们在开发的时候,可以打印输出日志,查看我们的数据是否能够从数据库获取。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值