🎼个人主页:【Y小夜】
😎作者简介:一位双非学校的大三学生,编程爱好者,
专注于基础和实战分享,欢迎私信咨询!
🎈热门专栏:🎊【Python,Javaweb,Springboot】
感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持!❤️
目录
🎈项目背景
🎊需求分析
在当今这个快节奏、高互联的时代,旅游已不仅仅是简单的出行,它更是一种生活方式的探索与体验。随着科技的进步和人们生活品质的提升,现代旅行者对旅游服务的需求日益多元化和个性化。他们渴望在旅途中不仅能够享受到便捷的预订服务,还希望能获得深度的文化体验、实时的信息更新以及社交互动的乐趣。因此,设计一款集景区展示、购票服务、景点推荐、AI答疑等功能于一体的微信小程序“景途无忧”,显得尤为迫切和必要。
🎊性能需求
- 正确性需求
管理员应能够进行有关的旅游信息准确地添加到数据库中。系统用户登录后,系统应能正确地读取用户个人信息。系统的操作结果与预期的结果应该是一致。
- 安全性需求
无重大安全漏洞,用户数据得到妥善保护。
- 及时性需求
即时可见:对旅游信息的处理(包括添加、删除、修改)将立即在后台数据库中进行更新,达到“即时操作、即时生效”的功能
- 稳定性需求
系统部署后,在硬件条件和支持软件条件没有变化的情况下,能够一直保持运行状态,直到系统被升级或代替。
- 扩展性需求
系统应该支持功能扩展与支持环境的扩展。功能扩展就是在现有的功能模块的基础上可以添加信息的功能模块。
- 故障处理能力需求
系统可能遇到的软件故障是数据库与应用程序服务器。为了满足信息处理的需求,可以采取数据恢复来解决。
🎊功能需求
- 运行景途无忧小程序后,进入登录/注册页面,用户如已有账号,可以直接输入账号和密码进行登录,若无账号,可以先进行注册,然后再进行登录操作。
- 首页对数据库中的景点数据进行展示,并且用户可以根据景点名称对感兴趣的景点进行搜索,用户也可以对景点的门票进行购买,加入订单中。
- 小助手页面是调用智能云平台创建的旅游小助手智能体,用于对用户提出的关于旅游的问题进行回答。
- 订单页面可以查看自己购买的门票,并且可以对订单进行取消操作。
- 我的页面用户可以进行查看个人信息、快速注册、退出登录或查看关于我们的界面
🎈登录功能
🎊创建数据库
先自己创建一个MySQL的tour数据库
🎊后台服务端
🎄初始化服务端
先自己创建一个文件夹,用于存放服务端代码
然后开始搭建服务器
- 下载Node.js(https://nodejs.org),选择长期支持版下载。
- 安装Node.js,双击下载的安装包安装即可,安装选项全部使用默认值。
- “用户登录”文件夹下创建文件夹“服务器端”来存放小程序项目对应的服务器端文件。
- “服务器端”文件夹下进入DOS命令界面。
- 初始化服务器端项目,将会自动创建package.json配置文件。 npm init -y
- 安装Express框架,用于快速搭建HTTP服务器。 npm install express --save
- 安装request模块。npm install request --save
- 安装nodemon监控代码修改。 npm install nodemon -g
安装完之后再在根目录下创建一个app.js文件,如下图
最后使用vscode将自己创建的文件加打开
🎄功能实现
🎁utils文件夹
首先,在根目录下创建utils文件夹
在utils文件夹下创建BaseDB.js文件用于导入MySQL的模块
//导入mysql的模块
const mysql=require("mysql")
//创建数据库连接
const db=mysql.createConnection({
//数据库的主机地址
host:"localhost",
//数据库账号
user:"root",
//数据库密码
password:"密码",
//操作的目标数据库
database:"数据库名"
})
//导出数据库连接对象
module.exports=db
在utils文件夹下创建Result.js文件用于封装结果类
//定义Result结果封装类
class Result{
//返回成功的对象的方法
static success(data){
//返回结果
return{
code:0,
msg:"成功",
data:data
}
}
//返回失败时的对象的方法
static fail(data){
return{
code:1,
msg:"失败",
data:data
}
}
}
module.exports=Result
🎁routers文件夹
在routers文件下创建userRouter.js文件,开始编写用户登陆的功能
//导入express用模块
const express=require("express")
//导入获得路由实例
const router=express.Router()
//导入数据库连接对象
const db=require("../utils/BaseDB")
//导入结果封装类
const Result=require("../utils/Result")
//用户登录
router.post('/user/login',(req,resp)=>{
//获取用户名和密码
let username=req.body.username
let password=req.body.password
//定义sql语句
let sql=`
select * from user where username=? and password=?
`
//定义参数
let params=[username,password]
//查询数据库
db.query(sql,params,(err,res)=>{
//错误
if(err){
resp.send(Result.fail(err))
}else{
//成功
resp.send(Result.success(res))
}
})
})
//进行导出
module.exports=router
🎁app.js文件
在app.js文件中编写代码,将路由引入
//导入express模块
const express = require('express')
//创建express的实例
const app=express()
//解析提交的json数据
app.use(express.json())
//解析提交表单的的数据
app.use(express.urlencoded({extended:true}))
//导入用户模块
const userRouter=require('./routers/userRouter')
//配置路由
app.use(userRouter)
// 监听3000端口
app.listen(3000,()=>{
console.log("服务端启动成功.....")
})
🎄测试功能
先打开终端,再根目录下使用node app 命令运行程序,但是不出意外的话这里要出意外了,请看下图报错
这里是意思是指没有引入mysql模块,我们查看一下node_modules文件夹,发现确实没有mysql
这时我们可以在终端使用 npm install mysql 命令下载mysql模块
下载完毕后,再次运行,发现运行成功。
但是使用node命令运行,当代码发生改变时,页面并不会自动刷新。所以我们可以使用nodemon,nodemon可以实现对代码的监控,使得当代码更新时,不需要重启服务器,就可以自动更新代码。
使用npm install -g nodemon
然后我们使用nodemon app运行,这时候可能还会出现一种错误,如下图
解决方案:
先使用管理员身份登录vscode
- 在终端运行:get-ExecutionPolicy,显示Restricted(表示状态是禁止的)
- 在终端执行,set-ExecutionPolicy RemoteSigned
- 在终端运行:get-ExecutionPolicy,显示RemoteSigned
- 再次运行nodemon app就可不会报错了
接下来使用apifox进行登录测试
请求:
响应:
到此我们的登录接口测试成功。
🎊前台微信小程序
🎄功能实现
🎁index.html
<!-- 登录界面 -->
<view class="one">景途无忧</view>
<view class="conent">
<view class="field">
<view class="title">账号</view>
<view>
<input type="text" bind:input="usernameInput"
placeholder="请输入账号"/>
</view>
</view>
<view class="hr"></view>
<view class="field">
<view class="title">密码</view>
<view>
<input password="{{true}}" bind:blur="pwdBlur" placeholder="请输入密码"/>
</view>
</view>
<view class="hr"></view>
<view class="btns">
<button type="primary" bind:tap="login" disabled="{{disabled}}">登陆</button>
<button type="default" bind:tap="regist">注册</button>
</view>
</view>
<!-- 重影 -->
<view class="shadow shadow-1"></view><view class="shadow shadow-2"></view>
<view class="two">在这里,我们致力于为您提供一个无忧无虑的旅行体验。无论您是想要探索美丽的自然风光,还是体验丰富的文化之旅,景途无忧都能满足您的需求。我们深知旅行中的每一个细节都至关重要,因此,从购票、规划行程到应对突发情况,我们都为您提供全方位的保障和支持。选择景途无忧,让我们一起开启一段美好的旅程吧!</view>
🎁index.js
// 登录界面的js
//获取全局的app对象
const app = getApp()
Page({
data: {
disabled:true,
username:"",
password:""
},
//账号文本框输入事件
usernameInput(e){
//获取当前输入的账号
let username = e.detail.value
if(username){
//让登陆按钮处于启用状态
this.setData({
disabled:false,
username:username
})
}else{
this.setData({
disabled:true,
username:username
})
}
},
//绑定密码的失焦函数
pwdBlur(e){
//将密码框中的数据,设置到data.password上
this.setData({
password:e.detail.value
})
},
//点击登陆按钮
login(e){
let that=this
//判断是否输入密码
if(!this.data.password){
wx.showToast({
title: '请输入密码',
icon:"error"
})
return
}
//发起服务端请求,进行登陆操作
wx.request({
//请求地址
url:app.globalData.baseUrl+'/user/login',
//请求方法
method:"POST",
//提交到后端的数据
data:{
"username":this.data.username,
"password":this.data.password
},
success:(res)=>{
//请求成功之后,获取后端响应的结果
if(res.data.code==0 &&
res.data.data.length>0){
wx.showToast({
title: '登陆成功',
icon:"success"
})
//设置全局用户名
app.globalData.username=that.data.username
//登陆成功,将用户数据,存储到本地
//跳转到商品首页
setTimeout(()=>{
wx.switchTab({
url: '/pages/home/home',
})
},200)
}else{
wx.showToast({
title: '登陆失败',
icon:"error"
})
}
}
})
},
//注册事件
// 导航到注册
regist(e){
wx.navigateTo({
url: '/pages/regist/regist',
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
🎄 页面展示
🎈注册功能
🎊后台服务端
// 用户注册
router.post('/user/regist', (req, resp) => {
console.log(req.body);
//获取获取一系列信息
const { username, password, phone, address } = req.body;
//定义sql语句
let sql = `
insert into user(username,password,phone,address) values(?,?,?,?)
`;
//定义参数
let params = [username, password, phone, address];
//执行sql语句
db.query(sql, params, (err, res) => {
//错误
if (err) {
resp.send(result.fail(err));
}
//成功
else {
resp.send(result.success(res));
}
});
});
🎊前端服务端
🎄功能实现
🎁regist.js
// pages/regist/regist.js
const app=getApp();
Page({
/**
* 页面的初始数据
*/
data: {
username: "",
password: "",
phone: "",
address: "",
region: ['河南省', '洛阳市', '涧西区'],
customItem: '全部'
},
// 绑定账号输入
bindUsernameInput: function(e) {
this.setData({
username: e.detail.value
});
},
// 绑定密码输入
bindPasswordInput: function(e) {
this.setData({
password: e.detail.value
});
},
// 绑定电话输入
bindPhoneInput: function(e) {
this.setData({
phone: e.detail.value
});
},
// 改变地区
bindRegionChange: function(e) {
this.setData({
region: e.detail.value
// 更新地址为选中的地区
});
},
regist(e) {
const { username, password, phone, region } = this.data;
// 由于picker组件的值是数组,我们需要将其转换为字符串
const address = region.join('');
// 查看表格是否为空
if (!username || !password || !phone || !address) {
wx.showToast({
title: '请填写完整信息',
icon: 'error'
});
return;
}
// 提交数据
wx.request({
url: app.globalData.baseUrl + "/user/regist",
method: "POST",
data:{
"username":username,
"password":password,
"phone":phone,
"address":address
},
success: (res) => {
if ((res.data.code==0)) {
wx.showToast({
title: '注册成功',
icon: "success",
});
setTimeout(() => {
wx.redirectTo({
url: '/pages/index/index',
});
}, 100); // 0.1秒后跳转
} else {
wx.showToast({
title: '注册失败',
icon: 'error'
});
}
}
});
}
});
🎁regist.wxml
<!--pages/regist/regist.wxml-->
<form bindsubmit="regist">
<view class="conent">
<view class="field">
<view class="title">账号</view>
<view>
<input type="text" name="username" placeholder="请输入账号" bindinput="bindUsernameInput"/>
</view>
</view>
<view class="hr"></view>
<!-- 密码 -->
<view class="field">
<view class="title">密码</view>
<view>
<input password="{{true}}" name="password" placeholder="请输入密码" bindinput="bindPasswordInput"/>
</view>
</view>
<view class="hr"></view>
<!-- 电话号 -->
<view class="field">
<view class="title">电话</view>
<view>
<input type="text" name="phone" placeholder="请输入手机号" bindinput="bindPhoneInput"/>
</view>
</view>
<view class="hr"></view>
<view class="field">
<view class="title">地址</view>
<picker mode="region" bindchange="bindRegionChange" value="{{region}}" custom-item="{{customItem}}">
<view class="picker">
{{region[0]}},{{region[1]}},{{region[2]}}
</view>
</picker>
</view>
<view class="hr"></view>
<button class="btns" form-type="submit">注册</button>
</view>
</form>
🎄页面展示
今天的讲解就到这里,预知后续如何,请看下篇文章!