🎼个人主页:【Y小夜】
😎作者简介:一位双非学校的大三学生,编程爱好者,
专注于基础和实战分享,欢迎私信咨询!
🎆入门专栏:🎇【MySQL,Java基础,Rust】
🎈热门专栏:🎊【Python,Javaweb,Springboot】
感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持!❤️
目录
🎈项目背景
🎊需求分析
🎊性能需求
🎊功能需求
🎈登录功能
🎊创建数据库
🎊后台服务端
🎄初始化服务端
🎄功能实现
🎁utils文件夹
🎁routers文件夹
🎁app.js文件
🎄测试功能
🎊前台微信小程序
🎄功能实现
🎁index.html
🎁index.js
🎄 页面展示
🎈注册功能
🎊后台服务端
🎊前端服务端
🎄功能实现
🎁regist.js
🎁regist.wxml
🎄页面展示
🎈项目背景
🎊需求分析
在当今这个快节奏、高互联的时代,旅游已不仅仅是简单的出行,它更是一种生活方式的探索与体验。随着科技的进步和人们生活品质的提升,现代旅行者对旅游服务的需求日益多元化和个性化。他们渴望在旅途中不仅能够享受到便捷的预订服务,还希望能获得深度的文化体验、实时的信息更新以及社交互动的乐趣。因此,设计一款集景区展示、购票服务、景点推荐、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.usernamelet 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.valueif(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>
🎄页面展示
今天的讲解就到这里,预知后续如何,请看下篇文章!