0


网络云相册实现--nodejs后端+vue3前端

主页面

功能简介

  • 多用户系统,用户可以在系统中注册、登录及管理自己的账号、相册及照片。
  • 每个用户都可以管理及维护相册,及相册的备注。
  • 每个用户都可以管理及维护照片,及照片的备注。
  • 相册需要可设置是否公开。
  • 照片是可评论的,只有一级评论(不需要评论分级)。
  • 分享界面(前台界面),需要图片放大预览及轮播图功能。
  • 图片删除需要回收站。

系统简介

系统采用前后端分离的方式b-s方式,后台使用nodejs技术,数据库采用MySQL系统。前端使用vue3框架搭建。

后端是负责提供接口(api)

api

用户管理用户注册(post)/api/userlogin用户名、密码修改密码(post)/api/userpasswordmodify原始密码、新密码相册管理新建相册(get)/api/addalbum相册名及简介修改相册(get)/api/modifyalbum移除相册(get)/api/removealbum相册必须为空才可以移除照片管理上传照片(*)/api/addpic加上备注修改(备注)(post)/api/modifyps修改备注删除照片(get)/api/removepic评论新增评论(get)/api/addcomment移除评论(get) /api/removecomment

数据库表结构

usersid无序号、递增createAt创建时间updateAt最后更新时间username用户名password密码picsid无序号、递增createAt创建时间updateAt最后更新时间url存放图片上传后相对服务器访问的地址(相对地址)ps图片的备注removed图片是否被移除userid照片隶属于哪个用户albumsid无序号、递增createAt创建时间updateAt最后更新时间title相册名称ps备注userid相册隶属于哪个用户commentsid无序号、递增createAt创建时间updateAt最后更新时间content评论内容userid发表评论的用户picid被评论的照片

代码目录

运行命令

后端启动命令:npm start

前端启动命令:npm run dev

主要代码

server

apis.js

var express = require('express')
var router = express.Router()
//引入封装的mysql访问函数
const query = require('../utils/mysql')
const encry = require('../utils/encry')
const jwt = require('jsonwebtoken')
const { expressjwt } = require('express-jwt')
const key = 'yuaner'
//引入上传对象
const upload = require('../utils/upload')

//用户注册的api
router.post('/userreg', async (req, res) => {
  //来自url的参数,使用req.query来获取
  //来自请求体的参数,使用req.body来获取

  const { username, password } = req.body

  //判断用户名是否重复
  const result = await query('select * from users where username=?', [
    username,
  ])
  if (result.length > 0) {
    res.json({
      flag: false,
      msg: '用户名已存在',
    })
  } else {
    //插入
    await query(
      'insert into users (username,password,createAt,updateAt) values (?,?,?,?)',
      [username, encry(password), new Date(), new Date()]
    )
    res.json({
      flag: true,
      msg: '用户注册成功',
    })
  }
})

//用户登录
router.post('/userlogin', async (req, res) => {
  //获取参数
  const { username, password } = req.body

  const result = await query(
    'select * from users where username=? and password=?',
    [username, encry(password)]
  )
  if (result.length > 0) {
    res.json({
      flag: true,
      msg: '登录成功',
      token: jwt.sign({ userid: result[0].id }, key, {
        expiresIn: '10h',
      }),
    })
  } else {
    res.json({
      flag: false,
      msg: '用户名或密码错误',
    })
  }
})

router.all(
  '*',
  expressjwt({ secret: key, algorithms: ['HS256'] }),
  function (req, res, next) {
    next()
  }
)

/**
 * 新建相册
 */
router.post('/addalbum', async (req, res) => {
  //获取数据
  const { title, ps } = req.body
  const userid = req.auth.userid
  const result = await query(
    'select * from albums where title=? and userid=?',
    [title, userid]
  )
  if (result.length > 0) {
    res.json({
      flag: false,
      msg: '同名相册已存在',
    })
  } else {
    await query(
      'insert into albums (title,ps,userid,createAt,updateAt) Values(?,?,?,?,?)',
      [title, ps, userid, new Date(), new Date()]
    )
    res.json({
      flag: true,
    })
  }
})

/**
 * 修改相册
 * /modifyalbum
 * 参数 title ,ps,albumid
 */
router.post('/modifyalbum', async (req, res) => {
  const { title, ps, albumid } = req.body
  //从token中获取userid
  const userid = req.auth.userid
  const result = await query(
    'select * from albums where title=? and userid=? and id<>?',
    [title, userid, Number(albumid)]
  )
  if (result.length > 0) {
    res.json({
      flag: true,
      msg: '相册名已存在',
    })
  } else {
    //进行修改的查询
    await query('update albums set title=?,ps=? where id=?', [
      title,
      ps,
      albumid,
    ])
    res.json({
      flag: true,
      msg: '修改成功',
    })
  }
})

/**
 * 移除相册
 * /removealbum
 * 参数 albumid,userid
 */

/**
router.get('/removealbum', async (req, res) => {
  //获取参数
  const { albumid } = req.body //判断当前相册是否为空
  const result = await query(
    'select COUNT(*) as count from pics where albumid = ?',
    [albumid]
  )
  if (result[0].count > 0) {
    res.json({
      flag: false,
      msg: '相册不为空,请先移除所有照片再删除相册',
    })
  }
  await query('delete from albums where id = ?', [albumid])
  res.json({
    flag: true,
    msg: '相册已删除',
  })
})
 */

router.get('/removealbum', async (req, res) => {
  //获取参数
  let { albumid } = req.query
  albumid = Number(albumid)
  //获取userid
  const userid = req.auth.userid
  //判断当前相册是否为空
  const result = await query(
    //可以限制为1,不用查询很多,此处用'limit 1'进行优化
    'select * from pics where albumid=? limit 1',
    [albumid]
  )
  if (result.length == 0) {
    //如果为空则删除
    //删除工作不需要赋值,直接wait
    await query('delete from albums where id=? and userid=?', [
      albumid,
      userid,
    ])
    res.json({
      flag: true,
      msg: '删除成功!',
    })
  }
  //如果不为空则不能删除
  else {
    res.json({
      flag: false,
      msg: '相册不为空',
    })
  }
})

/**
 * 分页查看相册内的图片列表
 * /getPiclist
 * 参数:albumid、pageIndex、pageRecord、、
 * 需要每一页记录数
 * 当前页数
 */
router.get('/getPicList', async (req, res) => {
  //获取参数
  let { albumid, pageIndex, pageRecord } = req.query

  const result = await query(
    'select * from pics where albumid=? and removed =0 ORDER BY updateAt desc limit ?,? ',
    [
      Number(albumid),
      (pageIndex - 1) * pageRecord,
      Number(pageRecord),
    ]
  )

  const result2 = await query(
    'select count(*) as t from pics where albumid=? and removed =0 ',
    [Number(albumid)]
  )

  res.json({
    flag: true,
    result: result,
    pageCount: Math.ceil(result2[0].t / pageRecord),
  })
})

/**
 * 获取当前用户的相册列表
 * /getAlbumList
 */ router.get('/getAlbumList', async (req, res) => {
  //获取参数
  const { userid } = req.auth

  let result = await query(
    'select a.id,a.title,a.ps,count(a.id) as t,max(b.url) as url from albums a ' +
      'left join pics b on a.id = b.albumid ' +
      'where a.userid=? ' +
      'group by a.id,a.title,a.ps,a.userid',
    [Number(userid)]
  )

  result = result.map(item => {
    if (!item.url) {
      item.t = 0 //'/default.jpg' 是 public作为根 // item.url='/default.jpg'
    }
    return item
  })

  res.json({
    flag: true,
    result: result,
  })
})
/**
 * 上传图片
 */
router.post('/addpic', upload.single('pic'), async (req, res) => {
  // 图片上传的路径由multer写入req.file对象中
  const path = req.file.path.split('\\').join('/').slice(6)
  //除了上传的文件之外,其他的表单数据也被multer存放在req.body中
  const ps = req.body.ps
  //userid由token解析得到
  const albumid = req.body.albumid

  //存储到数据库中
  await query(
    'insert into pics (url,ps,removed,albumid,createAt,updateAt) values (?,?,?,?,?,?)',
    [path, ps, 0, Number(albumid), new Date(), new Date()]
  )
  res.json({
    flag: true,
    msg: '照片添加成功! very good!',
  })
})

/**
 * 照片删除
 * 接口地址:deletepic
 * 客户端参数:picid
 * 接受客户端参数,将数据库中的记录删除,并返回客户端删除成功
 * /api/removepic
 */
router.get('/deletepic', async (req, res) => {
  const { picid } = req.query //or const picid =req.query.picid
  await query(
    'delete  from pics where id=?',
    [Number(picid)],
    res.json({
      flag: true,
      msg: '照片删除成功',
    })
  )
})

/**
 * 照片放入回收站
 * 接口地址:removepic
 * 客户端参数:picid
 * 接受客户端参数,将数据库中的记录删除,并返回客户端删除成功
 * /api/removepic
 */
router.get('/removepic', async (req, res) => {
  const { picid } = req.query //or const picid =req.query.picid
  await query(
    'update pics set removed=1 where id=?',
    [picid],
    res.json({
      flag: true,
      msg: '照片已放入回收站',
    })
  )
})

/**
 * 修改照片备注
 * modifyps,ps
 * 参数picid
 */
router.post('/modifyps', async (req, res) => {
  //获取参数
  const { picid, ps } = req.body
  //修改,调用数据库
  await query('update pics set ps=?,updateAt=? where id=?', [
    ps,
    new Date(),
    Number(picid),
  ])

  res.json({
    flag: true,
    msg: '备注修改成功!',
  })
})

/**
 * 新增评论
 * addComment
 * 参数:content,userid(req.auth)、picid
 * 类型:post
 */

router.post('/addComment', async (req, res) => {
  //获取参数
  const { picid, content } = req.body
  const { userid } = req.auth

  await query(
    'insert into comments (content,createAt,updateAt,userid,picid) values (?,?,?,?,?)',
    [content, new Date(), new Date(), Number(userid), Number(picid)]
  )
  res.json({
    flag: true,
    msg: '评论成功',
  })
})

/**
 * 删除评论
 * deleteComment
 * 参数:commitid,content,userid(req.auth)
 * 类型:get
 * 删除前需保证当前用户是这条评论的发表人才能删除
 */
router.get('/deleteComment', async (req, res) => {
  //获取参数
  const { commentid } = req.query
  const { userid } = req.auth

  const result = await query(
    'select a.id from comments a' +
      ' left join pics b ON a.picid=b.id' +
      ' left join albums c ON b.albumid=c.id' +
      ' where a.id=? and (a.userid=? or c.userid=?)',
    [Number(commentid), Number(userid), Number(userid)]
  )
  if (result.length > 0) {
    await query('delete from comments where id=?', [
      Number(commentid),
    ])
    res.json({
      flag: true,
      msg: '删除成功',
    })
  } else {
    res.json({
      flag: false,
      msg: '权限不足',
    })
  }
  // let key=false

  // const result = await query(
  //   'delete * from comments where id=? and userid =? limit 1)',
  //   [Number(userid),Number(commentid)]
  // )
  // if(result.length>0){
  //   key=true
  // }
  // if(key==false){
  //   const result=await query('select *from comment where id=?',[Number(commentid)])
  //   const picid=result[0].picid
  // }
  // res.json({
  //   flag: true,
  //   msg: '删除成功',
  // })
})

/**
 * 评论列表
 * /getCommentList
 * 参数:picid、pageIndex、pageRecord
 * get
 */
router.get('/getCommentList', async (req, res) => {
  //获取参数,需要后续修改,不使用const
  let { picid, pageIndex, pageRecord } = req.query
  const result = await query(
    'select * from comments a' +
      ' left join pics b ON a.picid=b.id' +
      ' where picid=? and b.removed=0 order by a.updateAt desc limit ?,? ',
    [
      Number(picid),
      Number(pageIndex - 1) * pageRecord,
      Number(pageRecord),
    ]
  )
  res.json({
    flag: true,
    result: result,
  })
})

module.exports = router

encry.js

//导入包
const crypto = require('crypto')

//导出一个函数(方法)
module.exports = password => {
  //创建一个加密对象sha1、md5
  const encry = crypto.createHash('sha1')
  //将要加密的字符串存入加密对象中
  encry.update(password)
  //将解密后的结果以hex的方式输出,hex就是将数字以字符+数字的方式进行输出
  return encry.digest('hex')
}

mysql.js

//封装mysql的连接,导出一个执行sql语句并返回结果的函数

//先引入mysql的驱动--也就是mysql2      //const用来替代var
const mysql = require(`mysql2`)

//创建连接池   {}是存对象的
const pool = mysql.createPool({
  //极限值
  connectionLimit: 10, //默认最大的连接数量
  host: `127.0.0.1`,
  user: `root`,
  password: '123456', //填自己的密码
  database: 'album',
})
//query函数用来执行sql语句
//参数是sql语句及参数    //nodejs是一个弱类型    //lamada表达式
const query = (sql, params) =>
  new Promise(
    //promise对象将异步操作包装成可控的结果
    //resolve,reject两个参数,一个成功,一个失败
    (resolve, reject) => {
      //先从连接池中取出一条连接  //异步操作
      pool.getConnection((err, connection) => {
        //如果失败。执行reject,表示失败
        if (err) {
          reject(err) //拿取连接失败,则直接返回失败
        } else {
          connection.query(sql, params, (err, result) => {
            //查询完成后,无论结果是什么,都不再需要连接
            //将连接换回连接池
            connection.release()
            if (err) {
              reject(err)
            } else {
              resolve(result)
            }
          })
        }
      })
    }
  )

//导出query函数
module.exports = query

upload.js

//引入multer、fs(读写操作模块)、path(路径模块)、
const multer = require('multer')
const fs = require('fs')
const path = require('path')

//创建磁盘存储引擎
const storage = multer.diskStorage({ destination, filename })

//创建上传对象
const upload = multer({ storage })

//它是上传时目录的生成函数
function destination(req, res, callback) {
  const date = new Date()
  //创建动态目录
  const path = `public/upload/${date.getFullYear()}/${
    date.getMonth() + 1
  }`
  //生成路径
  fs.mkdirSync(path, { recursive: true })

  callback(null, path)
}
//用来自动生成随机的不重复的文件名
function filename(req, file, callback) {
  //file.originalname 是上传文件的原始名
  //a.jpg a.b.c.jpg

  //a.b.c.jpg   >['a','b','c','jpg']   扩展名
  console.log(file)
  const arr = file.originalname.split('.')
  const extname = arr[arr.length - 1]
  const date = new Date()
  const filename = date.getTime() + Math.random() + '.' + extname

  callback(null, filename)
}

module.exports = upload

client3

index.js

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      //有人访问根,我导向/home(重定向)
      redirect: '/home',
    },
    {
      path: '/home',
      name: 'home',
      component: HomeView,
      children: [
        {
          path: 'albummanage',
          component: () => import('../views/AlbumManage.vue'),
        },
        {
          path: 'album/:id',
          component: () => import('../views/PicsView.vue'),
        },
      ],
    },
    {
      path: '/login',
      name: 'login',
      component: () => import('../views/LoginView.vue'),
    },
    {
      path: '/reg',
      name: 'reg',
      component: () => import('../views/RegView.vue'),
    },
  ],
})

//全局路由前置守卫
router.beforeEach(async (to, from) => {
  //从本地存储中尝试获取token
  if (to.name == 'home' && !localStorage.getItem('token')) {
    return { name: 'login' }
  }
})
export default router

完整代码

更新ing~

标签: 前端 node.js vue.js

本文转载自: https://blog.csdn.net/m0_69724592/article/details/140895809
版权归原作者 八月正好an 所有, 如有侵权,请联系我们删除。

“网络云相册实现--nodejs后端+vue3前端”的评论:

还没有评论