实战项目介绍
千呼万唤始出来~,经过长达10天的构思,实战项目最终还是产生了一些自认为有效想法,一起期待后续的落地笔记吧。
注意:实战项目的核心目的是巩固加深对
Vue3
的应用,因此其他辅助工程的搭建上会尽量简化,起到辅助作用即可,如有问题欢迎留言探讨~
项目目标
开发一款基于 Vue 3 的 AI 智能知识库与对话系统,具备以下功能:
- 智能问答:基于上传的文档(如 PDF、TXT、DOCX 等)提供精准的问答服务。
- 多轮对话:支持上下文理解,实现多轮对话。
- 文档上传与管理:支持多种文档格式的上传、嵌入和管理。
- 联网搜索:结合联网搜索能力,提供更全面的答案。
- 用户注册与登录:支持用户账户管理。
- 对话历史记录:保存用户的对话历史,支持查看和导出。
- 友好的用户界面:提供简洁、直观的前端交互体验。
技术选型与架构设计
前端
Vue 3
:现代前端框架,提供响应式数据绑定和组件化开发。Pinia
:状态管理库,替代 Vuex,提供更简洁的 API。Element Plus
:UI 组件库,提供丰富的组件和样式。Axios
:用于与后端 API 进行数据交互。
后端
Node.js+Express
:轻量级后端框架,用于处理 API 请求。DeepSeek API
:提供 AI 对话和知识库问答能力。Ollama
:本地部署的语言模型,支持离线使用。
数据库
MongoDB
:用于存储用户信息、文档和对话记录。LanceDB或Pinecone
:向量数据库,用于存储文档嵌入。
环境准备工作
Vue3项目搭建
方法一:使用 Vue CLI 搭建
安装 Vue CLI 如果尚未安装 Vue CLI,可以通过以下命令全局安装:
bash
npm install -g @vue/cli
或者使用国内镜像源加速安装:
bash
npm install -g @vue/cli --registry=https://registry.npmmirror.com
创建项目 在终端中运行以下命令创建 Vue 3 项目
bash
vue create ai-vue-project
按提示选择默认的 Vue 3 模板或手动选择特性(如 Vue Router、Vuex 等)。
进入项目目录并安装依赖
bash
cd ai-vue-project
npm install
启动开发服务器
bash
npm run serve
方法二:使用 Vite 搭建
安装 Node.js 确保已安装 Node.js,版本需在 12.0.0 以上。安装完成后,通过以下命令检查版本:
bash
node -v
创建项目 使用 Vite 创建 Vue 3 项目:
bash
npm create vite@latest ai-vue-project -- --template vue
或者使用 TypeScript 模板:
bash
npm create vite@latest ai-vue-project -- --template vue-ts
进入项目目录并安装依赖
bash
cd ai-vue-project
npm install
启动开发服务器
bash
npm run dev
其他说明 推荐使用Vite
Vite
是Vue 3
的官方推荐构建工具,具有以下优势:
- 启动速度快:冷启动速度极快,无需等待。
- 热重载高效:代码修改后可以即时更新,无需手动刷新。
- 支持
TypeScript
:与TypeScript
配合良好,提供更好的开发体验。
后续学习
- Vue 3 官方文档:Vue 3 Guide
- Vite 官方文档:Vite Documentation
- Vue 3 新特性:如 Composition API、Teleport、Fragment 等。
Vue3 Pania安装
具体安装内容可以直接传送到文章《【涅槃】Vue3学习笔记(五)》按步骤进行安装使用,这里就不在重复赘述了。
官方文档和更多资源
- Pinia 官方文档:pinia.vuejs.org/
- Pinia 中文文档:pinia.vuejs.org/zh/
Element Plus安装
Element Plus 是 Vue 3 的官方 UI 组件库,基于 Element UI 重构,提供了丰富的组件和主题配置,适用于构建现代化的 Vue 3 应用。
安装 Element Plus
在 Vue 3 项目中安装 Element Plus,可以使用以下命令:
bash
npm install element-plus --save
或者使用yarn
:
bash
yarn add element-plus
全局引入 Element Plus
在项目的入口文件(如main.js
或main.ts
)中,全局引入 Element Plus 和其样式文件:
javascript
import { createApp } from 'vue';
import App from './App.vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
const app = createApp(App);
app.use(ElementPlus);
app.mount('#app');
按需引入组件 如果你只想引入部分组件,可以通过以下方式按需引入。例如,引入ElButton
和ElMessage
:
javascript
import { createApp } from 'vue';
import App from './App.vue';
import { ElButton, ElMessage } from 'element-plus';
import 'element-plus/dist/index.css';
const app = createApp(App);
app.component('ElButton', ElButton);
app.config.globalProperties.$message = ElMessage;
app.mount('#app');
在组件中使用ElButton
:
vue
<template>
<div>
<el-button type="primary" @click="showMessage">点击我</el-button>
</div>
</template>
<script setup>
import { ElMessage } from 'element-plus';
function showMessage() {
ElMessage('这是一个消息提示');
}
</script>
使用 Element Plus 的主题配置 Element Plus 支持主题配置,可以通过修改变量来自定义样式。在项目中创建一个样式文件(如src/styles/element-custom.scss
),并覆盖默认变量:
scss
// src/styles/element-custom.scss
@use 'element-plus/theme-chalk/src/index' as *;
$--color-primary: teal; // 修改主题颜色
然后在main.js
或vite.config.js
中引入自定义样式文件:
javascript
import 'element-plus/dist/index.css';
import './styles/element-custom.scss';
使用 TypeScript 支持
Element Plus 提供了完整的 TypeScript 类型定义。如果你的项目使用了 TypeScript,可以直接享受类型提示和自动补全功能。
例如,使用ElMessage
的 TypeScript 类型:
typescript
import { ElMessage } from 'element-plus';
const showMessage = (message: string) => {
ElMessage({
message,
type: 'success',
});
};
使用 Element Plus 的国际化支持 Element Plus 支持国际化,可以通过安装element-plus/es/locale
来使用其他语言。例如,使用中文:
javascript
import { createApp } from 'vue';
import App from './App.vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import zhCn from 'element-plus/es/locale/lang/zh-cn';
const app = createApp(App);
app.use(ElementPlus, {
locale: zhCn,
});
app.mount('#app');
示例:创建一个简单的表单
以下是一个使用 Element Plus 创建的简单表单示例:
vue
<template>
<div>
<el-form label-width="100px">
<el-form-item label="用户名">
<el-input v-model="form.username"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input type="password" v-model="form.password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import { ElMessage } from 'element-plus';
const form = reactive({
username: '',
password: '',
});
const submitForm = () => {
if (form.username && form.password) {
ElMessage('表单提交成功');
} else {
ElMessage.error('用户名和密码不能为空');
}
};
</script>
官方文档和资源
- Element Plus 官方文档:element-plus.org/
- Element Plus 组件列表:element-plus.org/#/zh-CN/com...
- Element Plus GitHub 仓库:github.com/element-plu...
Axios使用
在 Vue 3 项目中使用 Axios 进行 HTTP 请求是一种常见的选择。Axios 是一个基于 Promise 的 HTTP 客户端,支持浏览器和 Node.js 环境,非常适合用于与后端 API 交互。
安装 Axios
在项目中安装 Axios,运行以下命令
bash
npm install axios
或者使用yarn
:
bash
yarn add axios
创建 Axios 实例
为了更好地管理请求和响应,建议创建一个 Axios 实例,并在其中配置默认参数(如基础 URL、超时时间等)。在项目中创建一个src/axios.js
文件:
javascript
// src/axios.js
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://api.example.com', // 设置基础 URL
timeout: 5000, // 设置超时时间
});
// 请求拦截器
instance.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么,例如添加 Token
config.headers['Authorization'] = `Bearer ${localStorage.getItem('token')}`;
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
// 响应拦截器
instance.interceptors.response.use(
(response) => {
// 对响应数据做点什么
return response;
},
(error) => {
// 对响应错误做点什么
if (error.response.status === 401) {
// 处理 401 未授权错误
console.error('未授权,请重新登录');
}
return Promise.reject(error);
}
);
export default instance;
在组件中使用 Axios
在 Vue 3 组件中,可以通过导入 Axios 实例来发送请求。以下是一个示例:
示例:获取用户数据
vue
<template>
<div>
<h1>用户信息</h1>
<p v-if="loading">加载中...</p>
<p v-if="error" style="color: red">{{ error }}</p>
<div v-if="user">
<p>用户名:{{ user.name }}</p>
<p>邮箱:{{ user.email }}</p>
</div>
<button @click="fetchUser">获取用户信息</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import axios from '@/axios'; // 导入自定义的 Axios 实例
const user = ref(null);
const loading = ref(false);
const error = ref(null);
const fetchUser = async () => {
loading.value = true;
error.value = null;
try {
const response = await axios.get('/user'); // 使用 Axios 实例发送请求
user.value = response.data;
} catch (err) {
error.value = err.message || '请求失败';
} finally {
loading.value = false;
}
};
</script>
使用 Axios 进行表单提交
Axios 也支持发送 POST 请求,例如提交表单数据:
示例:提交登录表单
vue
<template>
<div>
<h1>登录</h1>
<p v-if="error" style="color: red">{{ error }}</p>
<form @submit.prevent="login">
<div>
<label for="username">用户名:</label>
<input type="text" id="username" v-model="formData.username" />
</div>
<div>
<label for="password">密码:</label>
<input type="password" id="password" v-model="formData.password" />
</div>
<button type="submit">登录</button>
</form>
</div>
</template>
<script setup>
import { ref } from 'vue';
import axios from '@/axios';
const formData = ref({
username: '',
password: '',
});
const error = ref(null);
const login = async () => {
error.value = null;
try {
const response = await axios.post('/login', formData.value);
console.log('登录成功', response.data);
alert('登录成功');
} catch (err) {
error.value = err.response?.data?.message || '登录失败';
}
};
</script>
全局配置 Axios
如果你希望在项目中全局使用 Axios,可以在main.js
中进行配置:
javascript
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import axios from 'axios';
const app = createApp(App);
// 将 Axios 挂载到 Vue 的全局属性上
app.config.globalProperties.$axios = axios;
app.mount('#app');
然后在组件中通过this.$axios
使用 Axios:
javascript
this.$axios.get('/user').then((response) => {
console.log(response.data);
});
注意事项
- 拦截器
- 请求拦截器可以用于统一设置请求头(如 Token)。
- 响应拦截器可以用于统一处理错误(如 401 重定向到登录页)。
- 环境变量
- 可以使用环境变量来区分开发环境和生产环境的 API 地址。例如:
javascript
const instance = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'https://api.example.com',
});
- 错误处理
- 在实际项目中,建议对错误进行更详细的处理,例如显示错误提示或记录日志。
VueRouter了解与使用
参考笔记《【涅槃】Vue3学习笔记(三)》VueRouter的使用。
Node.js+Express搭建用户认证
用户认证方式的实现:Session 认证和JWT 认证。这两种方式都适用于 Node.js+Express,可以根据你的应用场景选择合适的方案。
方案 1:使用 Session 认证
1.安装依赖
bash
npm install express express-session bcryptjs body-parser
2.配置 Express 和 Session
javascript
const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');
const bcrypt = require('bcryptjs');
const app = express();
const port = 3000;
// 中间件
app.use(bodyParser.json());
app.use(session({
secret: 'your_secret_key', // 替换为你的密钥
resave: false,
saveUninitialized: true
}));
// 模拟用户数据
const users = [
{
id: 1,
username: 'admin',
password: bcrypt.hashSync('password123', 10) // 使用 bcrypt 加密密码
}
];
// 登录接口
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user || !bcrypt.compareSync(password, user.password)) {
return res.status(401).send({ message: 'Invalid credentials' });
}
req.session.user = user; // 将用户信息存储到 session 中
res.send({ message: 'Login successful', user: user.username });
});
// 获取用户信息接口
app.get('/profile', (req, res) => {
if (!req.session.user) {
return res.status(401).send({ message: 'Unauthorized' });
}
res.send({ message: 'Welcome to your profile', user: req.session.user.username });
});
// 登出接口
app.post('/logout', (req, res) => {
req.session.destroy();
res.send({ message: 'Logout successful' });
});
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
方案 2:使用 JWT 认证
1.安装依赖
bash
npm install express jsonwebtoken bcryptjs body-parser
2.配置 Express 和 JWT
javascript
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const bodyParser = require('body-parser');
const app = express();
const port = 3000;
// 中间件
app.use(bodyParser.json());
// 模拟用户数据
const users = [
{
id: 1,
username: 'admin',
password: bcrypt.hashSync('password123', 10) // 使用 bcrypt 加密密码
}
];
// JWT 密钥
const secretKey = 'your_secret_key'; // 替换为你的密钥
// 登录接口
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user || !bcrypt.compareSync(password, user.password)) {
return res.status(401).send({ message: 'Invalid credentials' });
}
// 生成 JWT
const token = jwt.sign({ id: user.id, username: user.username }, secretKey, { expiresIn: '1h' });
res.send({ message: 'Login successful', token });
});
// 验证 JWT 的中间件
const authenticateToken = (req, res, next) => {
const token = req.headers['authorization'];
if (!token) {
return res.status(401).send({ message: 'Unauthorized' });
}
jwt.verify(token, secretKey, (err, user) => {
if (err) {
return res.status(403).send({ message: 'Forbidden' });
}
req.user = user;
next();
});
};
// 获取用户信息接口
app.get('/profile', authenticateToken, (req, res) => {
res.send({ message: 'Welcome to your profile', user: req.user.username });
});
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
两种方案的对比
特性 | Session 认证 | JWT 认证 |
---|---|---|
状态存储 | 服务器端存储(Session) | 客户端存储(Token) |
适用场景 | 适合需要服务器端维护用户状态的场景 | 适合无状态、前后端分离的场景 |
安全性 | 需要保护 Session ID | 需要保护 JWT 密钥 |
扩展性 | 可能需要额外的存储(如 Redis) | 无状态,易于扩展 |
实现复杂度 | 中等 | 较低 |
总结
- 如果你的应用是传统的服务端渲染应用,或者需要服务器端维护用户状态,可以使用Session 认证。
- 如果你的应用是前后端分离的现代 Web 应用,推荐使用JWT 认证,因为它无状态、易于扩展,且实现简单。
根据你的具体需求选择合适的认证方式即可!
支持项目的基础搭建及储备工作已经基本完成,软件环境支持如下:
以下内容需要大家结合实际情况自行去搜索安装,后续所有的实战开发内容需要下边的软件环境做支撑否则无法进行。
- ollama如何将deepseek模型本地化部署
- MongoDb安装及客户端链接工具(MongoDB Compass)
- 知识库工具AnythingLLM的安装