1.项目所用技术栈
-
arkTS
-
node.js express
-
mongoDB
2.效果图
3.源码
Index.ets(登录页)
登陆时让前端访问数据库中已经存好的账号密码,如果可以查询到数据库中的数据,则账号密码正确,登录成功,否则登录失败。
import axios from '@ohos/axios'
import router from '@ohos.router'@Entry
@Component
struct Index {// 上传数据@State zhanghao: string = ''@State mima: string = ''@State zhanghao_find:string =''@State mima_find:string =''build() {Column() {Text('淼学通讯录').margin({top:70}).fontWeight(FontWeight.Bold).fontSize(30)Image('./components/img/appimg.jpg').width(200).height(200).borderRadius(20).margin({top:50,bottom:20})// 账号登录Row(){Image('./components/img/zhanghao.png').width(20).height(20)TextInput({placeholder:'账号'}).backgroundColor('#00FFFFFF').width('75%').onChange(value =>{console.log(value)this.zhanghao_find = value})}.borderRadius(12).borderWidth(1).borderColor('#E4DFDF').padding(5).margin({top:10})Row(){Image('./components/img/mima.png').width(20).height(20)TextInput({placeholder:'密码'}).backgroundColor('#00FFFFFF').width('75%').onChange(value =>{console.log(value)this.mima_find = value})}.borderRadius(12).padding(5).margin({top:10})Button('登录',{type:ButtonType.Normal,stateEffect:true}).borderRadius(10).margin({top:40}).width(250).height(55).onClick(()=>{axios({method: "get",url: 'http://localhost:3000/users/find/'+this.zhanghao_find+ '/' + this.mima_find,}).then(res => {console.info('result:' + JSON.stringify(res.data));// 获取data数组中的第一个元素const zhanghao = res.data.data[0].zhanghao;console.log(JSON.stringify(zhanghao))// 获取data数组中的第一个元素// 获取zhanghao字段的值router.pushUrl({url: 'pages/App_one',params: {zhanghao}})}).catch(error => {console.error(error);})})}.width('100%').height('100%')}
}
App_one.ets(功能页)
显示该登录的用户所添加的联系人。
import { Header } from '../components/Toubu'
import router from '@ohos.router'
import axios from '@ohos/axios'@Entry
@Component
struct App_one {@State items:Array<Object> = []@State Ondata: object = router.getParams()onPageShow(){axios({method: "get",url: 'http://localhost:3000/lianxirens/find/'+this.Ondata?.['zhanghao'],}).then(res => {console.info('result:123123123' + JSON.stringify(res.data));this.items = res.data.data;}).catch(error => {console.error(error);})}build() {Column() {Header().margin(20)Row(){Text('我的联系人').fontSize(20).padding({left:10,right:10})}.borderRadius(5).borderWidth(1).borderColor('#E4DFDF').padding(10).margin({top:10})Image('./components/img/add.png').height(70).width(70).position({ x: 280, y: 500,}).zIndex(20).onClick(()=>{router.pushUrl({url: 'pages/add',params: {zhanghao:this.Ondata?.['zhanghao']}})})// Scroll(){Column() {List({ space: 20, initialIndex: 0 }) {ForEach(this.items, (item) => {ListItem() {Row(){Image('./components/img/one.png').width(30).onClick(()=>{// 获取点击物品的numberconsole.log(JSON.stringify(item.number))router.pushUrl({url: 'pages/detail',params: {id: item._id,zhanghao:this.Ondata?.['zhanghao']}})}).margin({right:50,left:25})if (item.sex == '男') {Image('./components/img/nan.png').width(20)} else {Image('./components/img/nv.png').width(20)}Text(item.name).fontWeight(800).margin({ left: 20 })Text(item.number).fontWeight(800).margin({ left: 20 })}}.borderColor('#E4DFDF').margin({ top: 10 })})}}.height(700)// .backgroundColor('#E4DFDF')}.width('100%')}
}
add.ets(功能页)
根据上个页面的跳转传递过来的账号参数,并向该账号的数据表的该条数据中添加数据。
import { Header } from '../components/Toubu'
import axios from '@ohos/axios'
import router from '@ohos.router'
@Entry
@Component
struct Add {@State number:string =''@State name:string =''@State sex:string ='男'@State Ondata: object = router.getParams()build() {Column() {Header().margin(20)// 手机号TextInput({placeholder:'手机号',}).backgroundColor('#00FFFFFF').width('75%').onChange(value =>{console.log(value)this.number= value}).border({width: { bottom: '3' },}).borderColor('#E4DFDF').padding(15).margin({top:20})// 姓名TextInput({placeholder:'姓名',}).backgroundColor('#00FFFFFF').width('75%').onChange(value =>{console.log(value)this.name= value}).border({width: { bottom: '3' },}).borderColor('#E4DFDF').padding(15).margin({top:20})// 性别Row(){Column(){Radio({ value: 'nan', group: 'radioGroup' }).checked(true).height(30).width(30).onChange((isChecked: boolean) => {console.log('Radio1 status is ' + isChecked)this.sex = '男'})Text('男')}Column(){Radio({ value: 'nv', group: 'radioGroup' }).checked(false).height(30).width(30).onChange((isChecked: boolean) => {console.log('Radio2 status is ' + isChecked)this.sex = '女'})Text('女')}.margin({left:20})}.borderRadius(12).borderWidth(1).borderColor('#E4DFDF').padding({left:20,right:20}).margin({top:30})Button('添加',{type:ButtonType.Normal,stateEffect:true}).borderRadius(10).margin({top:100}).width(250).height(55).onClick(()=>{axios({method: "post",url: 'http://localhost:3000/lianxirens/add',data:{number:this.number,name:this.name,sex:this.sex,zhanghao:this.Ondata?.['zhanghao']}}).then(res => {console.info('result:' + JSON.stringify(res.data));router.back()}).catch(error => {console.error(error);})})}.width('100%')}
}
detail.ets(功能页)
在页面刷新之前首先通过组件生命周期函数访问数据表中的数据,拿到数据库之后复制在输入框中,根据上个页面传过来的参数对该登录的账号的用户所设计的联系人进行修改信息和删除。
import { Header } from '../components/Toubu'
import router from '@ohos.router'
import axios from '@ohos/axios'
@Entry
@Component
struct Detail {@State _id: string = ''@State number:string =''@State name:string =''@State sex:string ='男'// @State number: string = ''@State shuju: object = router.getParams()onPageShow(){console.info('result:' + JSON.stringify(this.shuju?.['id']));axios({method: "get",url: 'http://localhost:3000/lianxirens/find1/'+this.shuju?.['id'],}).then(res => {console.info('result:' + JSON.stringify(res.data.data[0]));this.number = res.data.data[0].numberthis.name = res.data.data[0].name// this.sex = res.data.data[0].sex}).catch(error => {console.error(error);})}build() {Column() {Header().margin(20)TextInput({placeholder:'手机号',text:this.number}).backgroundColor('#00FFFFFF').width('75%').onChange(value =>{console.log(value)this.number= value}).border({width: { bottom: '3' },}).borderColor('#E4DFDF').padding(15).margin({top:20})// 姓名TextInput({placeholder:'姓名',text:this.name}).backgroundColor('#00FFFFFF').width('75%').onChange(value =>{console.log(value)this.name= value}).border({width: { bottom: '3' },}).borderColor('#E4DFDF').padding(15).margin({top:20})// 性别Row(){Column(){Radio({ value: 'nan', group: 'radioGroup' }).checked(true).height(30).width(30).onChange((isChecked: boolean) => {console.log('Radio1 status is ' + isChecked)this.sex = '男'})Text('男')}Column(){Radio({ value: 'nv', group: 'radioGroup' }).checked(false).height(30).width(30).onChange((isChecked: boolean) => {console.log('Radio2 status is ' + isChecked)this.sex = '女'})Text('女')}.margin({left:20})}.borderRadius(12).borderWidth(1).borderColor('#E4DFDF').padding({left:20,right:20}).margin({top:30})Button('修改',{type:ButtonType.Normal,stateEffect:true}).borderRadius(10).margin({top:100}).width(250).height(55).onClick(()=>{axios({method: "post",url: 'http://localhost:3000/lianxirens/upd',data:{number:this.number,name:this.name,sex:this.sex,_id:this.shuju?.['id']}}).then(res => {console.info('result:' + JSON.stringify(res.data));router.back()}).catch(error => {console.error(error);})})Button('删除',{type:ButtonType.Normal,stateEffect:true}).borderRadius(10).margin({top:30}).width(250).height(55).onClick(()=>{axios({method: "post",url: 'http://localhost:3000/lianxirens/delete',data:{_id:this.shuju?.['id']}}).then(res => {console.info('result:' + JSON.stringify(res.data));router.back()}).catch(error => {console.error(error);})})}.width('100%').height('100%')}
}
后端node.js文件架构
主要代码:
db.js
负责创建数据库中数据表的结构,并连接数据库,为数据表中的键值创建模型。负责创建数据库中数据表的结构,并连接数据库,为数据表中的键值创建模型。
const mongoose = require('mongoose')//连接mongodb数据库
mongoose.connect("mongodb://localhost:27017/tongxunlu").then(() => {console.log("数据库连接成功!")}).catch((err) => {console.log("数据库连接失败!", err)})// 创建表用户表
const Users = new mongoose.Schema({number: {type: String,},name: {type: String},sex:{type:String},list:{type:Array}
})
const Lianxirens = new mongoose.Schema({zhanghao:{type: String,},number: {type: String,},name: {type: String},sex:{type:String},
})const users = mongoose.model("Users", Users);
const lianxirens = mongoose.model("Lianxirens", Lianxirens);
module.exports = {users,lianxirens
}
index.js
后端主入口程序,引用自定义组件进行进一步的模块封装。
// index.js
const express = require('express');
const app = express();
const userApi = require('./user_ctrl/user_api');
const lianxirensApi = require('./lianxiren_ctrl/lianxiren_api');app.use('/users', userApi);
app.use('/lianxirens', lianxirensApi);app.listen(3000, () => {console.log('server running');
});
user_api.js
负责该登录用户的联系人的查询以及账号的登录。
// user_api.js
const express = require('express');
const router = express.Router();
const { users } = require('../db');router.use(express.urlencoded({ extended: true }));
router.use(express.json());// 查询联系人
router.get("/findusers/:zhanghao", async (req, res) => {try {const zhanghao = req.params.zhanghao;// 使用 find 查询匹配指定 zhanghao 的数据记录const result = await users.findOne({ zhanghao });if (result) {// 如果找到匹配的记录,则返回该记录res.json({ data: result, message: "登录成功!" });} else {res.status(404).json({ message: "未找到匹配的记录" });}} catch (error) {res.status(500).json({ message: "服务器内部错误" });}
});// 账号登录
router.get("/find/:zhanghao/:mima", async (req, res) => {try {const zhanghao = req.params.zhanghao;const mima = req.params.mima;// 使用 find 查询所有匹配指定 name 的数据记录const results = await users.find({ zhanghao, mima });if (results.length > 0) {// 如果找到匹配的记录,则返回所有匹配的记录res.json({ data: results, message: "登录成功!" });} else {res.status(404).json({ message: "未找到匹配的记录" });}} catch (error) {res.status(500).json({ message: "服务器内部错误" });}
});module.exports = router;
lianxiren_api.js
负责修改该登录的用户所涉及的联系人的信息,以及删除联系人的信息数据。
// user_api.js
const express = require('express');
const router = express.Router();
const { lianxirens } = require('../db');router.use(express.urlencoded({ extended: true }));
router.use(express.json());// 修改联系人信息
router.post("/add", async (req, res) => {try {const { zhanghao ,number,name,sex } = req.body;await lianxirens.create({zhanghao,number,name,sex}); res.send("success"); } catch (error) {res.send(error, "error");}});
//router.get("/find/:zhanghao", async (req, res) => {try {const zhanghao = req.params.zhanghao;// 使用 find 查询所有匹配指定 name 的数据记录const results = await lianxirens.find({ zhanghao });if (results.length > 0) {// 如果找到匹配的记录,则返回所有匹配的记录res.json({ data: results, message: "登录成功!" });} else {res.json({ data: results, message: "None" });}} catch (error) {res.status(500).json({ message: "服务器内部错误" });}});
// 查找联系人router.get("/find1/:_id", async (req, res) => {try {const _id = req.params._id;// 使用 find 查询所有匹配指定 name 的数据记录const results = await lianxirens.find({ _id });if (results.length > 0) {// 如果找到匹配的记录,则返回所有匹配的记录res.json({ data: results, message: "查找联系人成功!" });} else {res.json({ data: results, message: "None" });}} catch (error) {res.status(500).json({ message: "服务器内部错误" });}});// 修改联系人信息
router.post("/upd", async (req, res) => {try {const { _id, number, name, sex } = req.body;// 使用 updateOne 更新指定 _id 的数据记录的 number、name 和 sex 字段const result = await lianxirens.updateOne({ _id }, { $set: { number, name, sex } });res.json({ message: "修改联系人信息成功!", result });} catch (error) {res.status(500).json({ error: error.message });}
});
// 删除联系人
router.post("/delete", async (req, res) => {try {const { _id } = req.body;// 使用 deleteOne 删除指定 _id 的数据记录const result = await lianxirens.deleteOne({ _id });res.json({ message: "删除联系人信息成功!", result });} catch (error) {res.status(500).json({ error: error.message });}
});module.exports = router;