本文为大家分享了node和vue商城用户地址模块的实现,供大家参考,具体内容如下
server/models/user.js
var mongoose = require(\'mongoose\')
var userSchema = mongoose.Schema({
\"userId\":String, // 用户Id
\"userName\":String, // 用户名
\"userPwd\":String, // 用户密码
\"orderList\":Array, // 订单列表
\"cartList\":[ // 购物车列表
{
\"productId\": String, // 商品Id
\"productName\": String, // 商品名称
\"salePrice\":String, // 商品价格
\"productImage\":String, // 图片地址
\"checked\":String, // 是否选中
\"productNum\":String // 商品数量
}
],
\"addressList\":[ // 用户地址列表
{
\"addressId\": String,
\"userName\": String,
\"streetName\": String,
\"postCode\": Number,
\"tel\": Number,
\"isDefault\": Boolean
}
]
});
// 通过module.exports进行输出,这样才能加载到 三个参数分别是 模型名,userSchema名,管理数据库集合名
module.exports = mongoose.model(\"User\",userSchema,\"users\");
server/routes/users.js
var express = require(\'express\');
var router = express.Router();
var User = require(\'./../models/user\');
/* GET users listing. */
router.get(\'/\', function(req, res, next) {
res.send(\'respond with a resource\');
});
router.get(\'/test\', function(req, res, next) {
res.send(\'test\');
});
// 登录
router.post(\'/login\', function(req, res, next) {
// 获取前端传过来的参数 post方式用req.Body形式获取参数
var param = {
userName:req.body.userName,
userPwd:req.body.userPwd
}
User.findOne(param, function(err,doc) {
if(err){
res.json({
status:\"1\",
msg:err.message
});
}else{
if(doc){
res.cookie(\"userId\",doc.userId,{ // 将用户信息存入cookie
path:\'/\',
maxAge: 1000*60*60
});
res.cookie(\"userName\",doc.userName, {
path:\'/\',
maxAge: 1000*60*60
});
// req.session.user = doc; // 将用户信息存入session
res.json({
status:\'0\',
msg:\'\',
result:{
userName:doc.userName
}
})
}
}
});
});
// 登出接口
router.post(\"/logout\", function (req,res,next) {
res.cookie(\"userId\", \"\", { // 登出将userId设置为\"\"
path:\"/\",
maxAge:-1 // 设置位过期
})
res.json({
status:\"0\",
msg:\'\',
result:\'\'
})
})
// 校验用户信息
router.get(\"/checkLogin\", function (req,res,next) {
if(req.cookies.userId){
res.json({
status:\'0\',
msg:\'\',
result: req.cookies.userName || \'\' // 获取cookeie req.cookies.属性
});
}else{ // 取不到就说明当前没有登录
res.json({
status:\'1\',
msg:\'未登录\',
result:\'\'
});
}
})
// 查询当前用户的购物车数据
router.get(\"/cartList\", function (req,res,next) {
var userId = req.cookies.userId;
console.log(userId);
User.findOne({userId:userId}, function (err,doc) {
if(err){
res.json({
status:\'1\',
msg:err.message,
result:\'\'
});
}else{
if(doc){
res.json({
status:\'0\',
msg:\'\',
result:doc.cartList
});
}
}
});
});
// 购物车删除
router.post(\"/cartDel\", function (req,res,next) {
// 获取浏览器的cookie 以及用户传递过来的参数 productId
var userId = req.cookies.userId,productId = req.body.productId;
User.update({
userId:userId
},{
$pull:{
\'cartList\':{
\'productId\':productId
}
}
}, function (err,doc) {
if(err){
res.json({
status:\'1\',
msg:err.message,
result:\'\'
});
}else{
res.json({
status:\'0\',
msg:\'\',
result:\'suc\'
});
}
});
});
//修改购物车商品数量
router.post(\"/cartEdit\", function (req,res,next) {
var userId = req.cookies.userId, // 获取用户客户端的userId
productId = req.body.productId, // 获取用户传的参数商品id
productNum = req.body.productNum, // 获取用户传的参数商品id
checked = req.body.checked; // 获取用户传的参数是否选中
User.update({
\"userId\":userId,
\"cartList.productId\":productId},{
\"cartList.$.productNum\":productNum,
\"cartList.$.checked\":checked,
}, function (err,doc) {
if(err){
res.json({
status:\'1\',
msg:err.message,
result:\'\'
});
}else{
res.json({
status:\'0\',
msg:\'\',
result:\'suc\'
});
}
})
});
// 商品全选不选
router.post(\"/editCheckAll\", function (req,res,next) {
var userId = req.cookies.userId,
checkAll = req.body.checkAll?\'1\':\'0\';
User.findOne({userId:userId}, function (err,user) {
if(err){
res.json({
status:\'1\',
msg:err.message,
result:\'\'
});
}else{
if(user){
user.cartList.forEach((item)=>{
item.checked = checkAll;
})
user.save(function (err1,doc) {
if(err1){
res.json({
status:\'1\',
msg:err1,message,
result:\'\'
});
}else{
res.json({
status:\'0\',
msg:\'\',
result:\'suc\'
});
}
})
}
}
});
});
// 用户地址模块开始-----------------------------------------------------------------------------------------------
// 查询用户地址接口
router.get(\"/addressList\", function (req,res,next) {
var userId = req.cookies.userId;
User.findOne({userId:userId}, function (err,doc) {
if(err){
res.json({
status:\'1\',
msg:err.message,
result:\'\'
});
}else{
res.json({
status:\'0\',
msg:\'\',
result:doc.addressList
});
}
})
});
// 设置默认地址
router.post(\"/setDefault\", function (req,res,next) {
var userId = req.cookies.userId,
addressId = req.body.addressId;
if(!addressId){
res.json({
status:\'1003\',
msg:\'addressId is null\',
result:\'\'
});
}else{
User.findOne({userId:userId}, function (err,doc) {
if(err){
res.json({
status:\'1\',
msg:err.message,
result:\'\'
});
}else{
var addressList = doc.addressList;
addressList.forEach((item)=>{
if(item.addressId == addressId){
item.isDefault = true;
}else{
item.isDefault = false;
}
});
doc.save(function (err1,doc1) {
if(err){
res.json({
status:\'1\',
msg:err.message,
result:\'\'
});
}else{
res.json({
status:\'0\',
msg:\'\',
result:\'\'
});
}
})
}
});
}
});
// 删除地址接口
router.post(\"/delAddress\", function (req,res,next) {
var userId = req.cookies.userId,addressId = req.body.addressId;
User.update({
userId:userId
},{
$pull:{ // 删除子文档元素
\'addressList\':{
\'addressId\':addressId
}
}
}, function (err,doc) {
if(err){
res.json({
status:\'1\',
msg:err.message,
result:\'\'
});
}else{
res.json({
status:\'0\',
msg:\'\',
result:\'\'
});
}
});
});
// 用户地址模块开始-----------------------------------------------------------------------------------------------
module.exports = router;
vue 前端部分
src/router/index.js 路由
import Vue from \'vue\'
import Router from \'vue-router\'
import GoodsList from \'./../views/GoodsList.vue\'
import Cart from \'@/views/Cart.vue\'
import Address from \'@/views/Address\'
Vue.use(Router);
export default new Router({
routes: [
{
path: \'/\',
name: \'GoodsList\',
component:GoodsList
},
{
path: \'/cart\',
name: \'Cart\',
component:Cart
},
{
path: \'/address\',
name: \'Address\',
component:Address
}
]
})
src/views/Address.vue
<template>
<div>
<nav-header></nav-header>
<nav-bread>
<span>Address</span>
</nav-bread>
<div class=\"checkout-page\">
<svg style=\"position: absolute; width: 0; height: 0; overflow: hidden;\" version=\"1.1\" ns=\"http://www.w3.org/2000/svg\" ns:x =\"http://www.w3.org/1999/x \">
<defs>
<symbol id=\"icon-add\" viewBox=\"0 0 31 32\">
< >add</ >
<path class=\"path1\" d=\"M30.745 15.152h-14.382v-14.596c0-0.308-0.243-0.557-0.543-0.557s-0.543 0.249-0.543 0.557v14.596h-14.665c-0.3 0-0.543 0.249-0.543 0.557s0.243 0.557 0.543 0.557h14.665v15.177c0 0.307 0.243 0.557 0.543 0.557s0.543-0.249 0.543-0.557v-15.177h14.382c0.3 0 0.543-0.249 0.543-0.557s-0.243-0.557-0.543-0.557z\"></path>
</symbol>
<symbol id=\"icon-ok\" viewBox=\"0 0 32 32\">
< >ok</ >
<path class=\"path1\" d=\"M14.084 20.656l-7.845-9.282c-1.288-1.482-3.534-1.639-5.016-0.351s-1.639 3.534-0.351 5.016l10.697 12.306c1.451 1.669 4.057 1.623 5.448-0.096l18.168-22.456c1.235-1.527 0.999-3.765-0.528-5.001s-3.765-0.999-5.001 0.528l-15.573 19.337z\"></path>
</symbol>
<symbol id=\"icon-edit\" viewBox=\"0 0 32 32\">
< >edit</ >
<path class=\"path1\" d=\"M28.287 8.51l-4.805-4.806 0.831-0.831c0.472-0.472 1.086-0.777 1.564-0.777 0.248 0 0.452 0.082 0.622 0.253l3.143 3.144c0.539 0.54 0.133 1.529-0.524 2.186l-0.831 0.831zM26.805 9.992l-1.138 1.138-4.805-4.806 1.138-1.138 4.805 4.806zM24.186 12.612l-14.758 14.762-4.805-4.806 14.758-14.762 4.805 4.806zM7.379 28.288l-4.892 1.224 1.223-4.894 3.669 3.67zM31.123 4.011l-3.143-3.144c-0.567-0.567-1.294-0.867-2.103-0.867-1.036 0-2.174 0.52-3.045 1.391l-20.429 20.436c-0.135 0.134-0.23 0.302-0.276 0.487l-2.095 8.385c-0.089 0.355 0.017 0.736 0.276 0.995 0.198 0.198 0.461 0.307 0.741 0.307 0.085 0 0.171-0.010 0.254-0.031l8.381-2.096c0.185-0.047 0.354-0.142 0.487-0.276l20.43-20.436c1.409-1.41 2.042-3.632 0.524-5.15v0z\"></path>
</symbol>
<symbol id=\"icon-del\" viewBox=\"0 0 32 32\">
< >delete</ >
<path class=\"path1\" d=\"M11.355 4.129v-2.065h9.29v2.065h-9.29zM6.194 29.935v-23.742h19.613v23.742h-19.613zM30.968 4.129h-8.258v-3.097c0-0.569-0.463-1.032-1.032-1.032h-11.355c-0.569 0-1.032 0.463-1.032 1.032v3.097h-8.258c-0.569 0-1.032 0.463-1.032 1.032s0.463 1.032 1.032 1.032h3.097v24.774c0 0.569 0.463 1.032 1.032 1.032h21.677c0.569 0 1.032-0.463 1.032-1.032v-24.774h3.097c0.569 0 1.032-0.463 1.032-1.032s-0.463-1.032-1.032-1.032v0z\"></path>
<path class=\"path2\" d=\"M10.323 9.806c-0.569 0-1.032 0.463-1.032 1.032v14.452c0 0.569 0.463 1.032 1.032 1.032s1.032-0.463 1.032-1.032v-14.452c0-0.569-0.463-1.032-1.032-1.032z\"></path>
<path class=\"path3\" d=\"M16 9.806c-0.569 0-1.032 0.463-1.032 1.032v14.452c0 0.569 0.463 1.032 1.032 1.032s1.032-0.463 1.032-1.032v-14.452c0-0.569-0.463-1.032-1.032-1.032z\"></path>
<path class=\"path4\" d=\"M21.677 9.806c-0.569 0-1.032 0.463-1.032 1.032v14.452c0 0.569 0.463 1.032 1.032 1.032s1.032-0.463 1.032-1.032v-14.452c0-0.569-0.463-1.032-1.032-1.032z\"></path>
</symbol>
<symbol id=\"icon-clock\" viewBox=\"0 0 32 32\">
< >clock</ >
<path class=\"path1\" d=\"M29.333 16c0-7.364-5.97-13.333-13.333-13.333s-13.333 5.97-13.333 13.333c0 7.364 5.97 13.333 13.333 13.333s13.333-5.97 13.333-13.333v0 0 0 0 0 0zM0 16c0-8.837 7.163-16 16-16s16 7.163 16 16c0 8.837-7.163 16-16 16s-16-7.163-16-16zM14.667 14.667v1.333h2.667v-10.667h-2.667v9.333zM24 18.667h1.333v-2.667h-10.667v2.667h9.333z\"></path>
</symbol>
<symbol id=\"icon-question\" viewBox=\"0 0 32 32\">
< >question</ >
<path class=\"path1\" d=\"M16 2.56c7.411 0 13.44 6.029 13.44 13.44s-6.029 13.44-13.44 13.44c-7.411 0-13.44-6.029-13.44-13.44s6.029-13.44 13.44-13.44zM16 0c-8.822 0-16 7.178-16 16s7.178 16 16 16c8.822 0 16-7.178 16-16s-7.178-16-16-16z\"></path>
<path class=\"path2\" d=\"M16 22.080c-1.059 0-1.92 0.861-1.92 1.92s0.861 1.92 1.92 1.92c1.059 0 1.92-0.861 1.92-1.92s-0.861-1.92-1.92-1.92z\"></path>
<path class=\"path3\" d=\"M12.16 12.48c0.706 0 1.28-0.574 1.28-1.28 0-1.412 1.148-2.56 2.56-2.56s2.56 1.148 2.56 2.56c0 1.412-1.148 2.56-2.56 2.56-0.706 0-1.28 0.574-1.28 1.28v3.84c0 0.706 0.574 1.28 1.28 1.28s1.28-0.574 1.28-1.28v-2.723c2.224-0.575 3.84-2.616 3.84-4.957 0-2.823-2.297-5.12-5.12-5.12s-5.12 2.297-5.12 5.12c0 0.706 0.574 1.28 1.28 1.28z\"></path>
</symbol>
</defs>
</svg>
<div class=\"container\">
<div class=\"checkout-addr\">
<div class=\"page- -normal\">
<h2 class=\"page- -h2\"><span>check out</span></h2>
</div>
<!-- process step -->
<div class=\"check-step\">
<ul>
<li class=\"cur\"><span>Confirm</span> address</li>
<li><span>View your</span> order</li>
<li><span>Make</span> payment</li>
<li><span>Order</span> confirmation</li>
</ul>
</div>
<!-- address list -->
<div class=\"page- -normal checkout- \">
<h2><span>Shipping address</span></h2>
</div>
<div class=\"addr-list-wrap\">
<div class=\"addr-list\">
<ul>
<li v-for=\"(item,index) in addressListFilter\" :key=\"index\" v-bind:class=\"{\'check\':checkIndex==index}\" @click=\"checkIndex=index;selectedAddrId=item.addressId\">
<dl>
<dt>{{item.userName}}</dt>
<dd class=\"address\">{{item.streetName}}</dd>
<dd class=\"tel\">{{item.tel}}</dd>
</dl>
<div class=\"addr-opration addr-del\">
<a href=\" :;\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" class=\"addr-del-btn\" @click=\"delAddressConfirm(item.addressId)\">
<svg class=\"icon icon-del\"><use x :href=\"#icon-del\" rel=\"external nofollow\" ></use></svg>
</a>
</div>
<div class=\"addr-opration addr-set-default\">
<a href=\" :;\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" class=\"addr-set-default-btn\" v-if=\"!item.isDefault\" @click=\"setDefault(item.addressId)\"><i>Set default</i></a>
</div>
<div class=\"addr-opration addr-default\" v-if=\"item.isDefault\">Default address</div>
</li>
<li class=\"addr-new\">
<div class=\"add-new-inner\">
<i class=\"icon-add\">
<svg class=\"icon icon-add\"><use x :href=\"#icon-add\" rel=\"external nofollow\" ></use></svg>
</i>
<p>Add new address</p>
</div>
</li>
</ul>
</div>
<div class=\"shipping-addr-more\">
<a class=\"addr-more-btn up-down-btn\" href=\" :;\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" @click=\"expand\" v-bind:class=\"{\'open\':limit>3}\">
more
<i class=\"i-up-down\">
<i class=\"i-up-down-l\"></i>
<i class=\"i-up-down-r\"></i>
</i>
</a>
</div>
</div>
<!-- shipping method-->
<div class=\"page- -normal checkout- \">
<h2><span>Shipping method</span></h2>
</div>
<div class=\"shipping-method-wrap\">
<div class=\"shipping-method\">
<ul>
<li class=\"check\">
<div class=\"name\">Standard shipping</div>
<div class=\"price\">Free</div>
<div class=\"shipping-tips\">
<p>Once shipped,Order should arrive in the destination in 1-7 business days</p>
</div>
</li>
</ul>
</div>
</div>
<div class=\"next-btn-wrap\">
<router- class=\"btn btn--m btn--red\" v-bind:to=\"{path:\'orderConfirm\',query:{\'addressId\':selectedAddrId}}\">Next</router- >
</div>
</div>
</div>
</div>
<modal :mdShow=\"isMdShow\" @close=\"closeModal\">
<p slot=\"message\">
您是否确认要删除此地址?
</p>
<div slot=\"btnGroup\">
<a class=\"btn btn--m\" href=\" :;\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" @click=\"delAddress\">确认</a>
<a class=\"btn btn--m btn--red\" href=\" :;\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" rel=\"external nofollow\" @click=\"isMdShow=false\">取消</a>
</div>
</modal>
<nav-footer></nav-footer>
</div>
</template>
<style>
</style>
< >
import NavHeader from \'./../components/NavHeader\'
import NavFooter from \'./../components/NavFooter\'
import NavBread from \'./../components/NavBread\'
import Modal from \'./../components/Modal\'
import {currency} from \'./../util/currency\'
import axios from \'axios\'
export default{
data(){
return{
limit:3,
checkIndex:0, // 默认选中第一个地址
selectedAddrId:\'\',
addressList:[],
isMdShow:false,
addressId:\'\'
}
},
mounted(){
this.init();
},
computed:{
addressListFilter(){ // 动态计算,默认显示3条地址数据
return this.addressList.slice(0,this.limit); // 默认显示3条数据
}
},
components:{
NavHeader,
NavFooter,
NavBread,
Modal
},
methods:{
init(){
axios.get(\"/users/addressList\").then((response)=>{ // 获取用户地址列表
let res = response.data;
this.addressList = res.result;
this.selectedAddrId = this.addressList[0].addressId;
for(var i=0;i<this.addressList.length;i++){
if(this.addressList[i].isDefault){
this.checkIndex = i;
}
}
});
},
expand(){ // 展开和收起地址列表
if(this.limit==3){
this.limit = this.addressList.length;
}else{
this.limit = 3;
}
},
setDefault(addressId){ // 设置默认地址
axios.post(\"/users/setDefault\",{
addressId:addressId
}).then((response)=>{
let res = response.data;
if(res.status==\'0\'){
console.log(\"set default\");
this.init();
}
})
},
closeModal(){ // 管理删除地址提示弹框
this.isMdShow = false;
},
delAddressConfirm(addressId){ // 弹出模态框 选中删除的地址
this.isMdShow = true;
this.addressId = addressId;
},
delAddress(){ // 删除该地址
axios.post(\"/users/delAddress\",{
addressId:this.addressId
}).then((response)=>{
let res = response.data;
if(res.status==\"0\"){
console.log(\"del suc\");
this.isMdShow = false;
this.init();
}
})
}
}
}
</ >
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
继续阅读与本文标签相同的文章
上一篇 :
人工智能有可能超越人类大脑?
-
独家 | 关于数据湖架构、战略和分析的8大错误认知(附链接)
2026-05-19栏目: 教程
-
独家|手把手教你赋能Jupyter Notebooks!(附代码)
2026-05-19栏目: 教程
-
万字长文详解文本抽取:从算法理论到实践(附“达观杯”官方baseline实现解析及答疑)
2026-05-19栏目: 教程
-
干货 | 关于机器学习的知识点,全在这篇文章里了
2026-05-19栏目: 教程
-
独家|利用OpenCV,Python和Ubidots来构建行人计数器程序(附代码&解析)
2026-05-19栏目: 教程
