網站怎么做才被收錄快google play下載安裝
目錄
主頁面
功能簡介
系統簡介
api
數據庫表結構
代碼目錄
運行命令
主要代碼
server
apis.js
encry.js
mysql.js
upload.js
client3
index.js
完整代碼
主頁面
功能簡介
-
多用戶系統,用戶可以在系統中注冊、登錄及管理自己的賬號、相冊及照片。
-
每個用戶都可以管理及維護相冊,及相冊的備注。
-
每個用戶都可以管理及維護照片,及照片的備注。
-
相冊需要可設置是否公開。
-
照片是可評論的,只有一級評論(不需要評論分級)。
-
分享界面(前臺界面),需要圖片放大預覽及輪播圖功能。
-
圖片刪除需要回收站。
系統簡介
系統采用前后端分離的方式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 |
數據庫表結構
users | ||
---|---|---|
id | 無序號、遞增 | |
createAt | 創(chuàng)建時間 | |
updateAt | 最后更新時間 | |
username | 用戶名 | |
password | 密碼 |
pics | ||
---|---|---|
id | 無序號、遞增 | |
createAt | 創(chuàng)建時間 | |
updateAt | 最后更新時間 | |
url | 存放圖片上傳后相對服務器訪問的地址(相對地址) | |
ps | 圖片的備注 | |
removed | 圖片是否被移除 | |
userid | 照片隸屬于哪個用戶 |
albums | ||
---|---|---|
id | 無序號、遞增 | |
createAt | 創(chuàng)建時間 | |
updateAt | 最后更新時間 | |
title | 相冊名稱 | |
ps | 備注 | |
userid | 相冊隸屬于哪個用戶 |
comments | ||
---|---|---|
id | 無序號、遞增 | |
createAt | 創(chuàng)建時間 | |
updateAt | 最后更新時間 | |
content | 評論內容 | |
userid | 發(fā)表評論的用戶 | |
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.bodyconst 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.bodyconst userid = req.auth.useridconst 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中獲取useridconst userid = req.auth.useridconst 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.queryalbumid = Number(albumid)//獲取useridconst userid = req.auth.userid//判斷當前相冊是否為空const result = await query(//可以限制為1,不用查詢很多,此處用'limit 1'進行優(yōu)化'select * from pics where albumid=? limit 1',[albumid])if (result.length == 0) {//如果為空則刪除//刪除工作不需要賦值,直接waitawait 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.queryconst 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.authlet 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.picidawait 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.picidawait 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.bodyconst { userid } = req.authawait 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* 刪除前需保證當前用戶是這條評論的發(fā)表人才能刪除*/
router.get('/deleteComment', async (req, res) => {//獲取參數const { commentid } = req.queryconst { userid } = req.authconst 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) => {//獲取參數,需要后續(xù)修改,不使用constlet { picid, pageIndex, pageRecord } = req.queryconst 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 => {//創(chuàng)建一個加密對象sha1、md5const encry = crypto.createHash('sha1')//將要加密的字符串存入加密對象中encry.update(password)//將解密后的結果以hex的方式輸出,hex就是將數字以字符+數字的方式進行輸出return encry.digest('hex')
}
mysql.js
//封裝mysql的連接,導出一個執(zhí)行sql語句并返回結果的函數//先引入mysql的驅動--也就是mysql2 //const用來替代var
const mysql = require(`mysql2`)//創(chuàng)建連接池 {}是存對象的
const pool = mysql.createPool({//極限值connectionLimit: 10, //默認最大的連接數量host: `127.0.0.1`,user: `root`,password: '123456', //填自己的密碼database: 'album',
})
//query函數用來執(zhí)行sql語句
//參數是sql語句及參數 //nodejs是一個弱類型 //lamada表達式
const query = (sql, params) =>new Promise(//promise對象將異步操作包裝成可控的結果//resolve,reject兩個參數,一個成功,一個失敗(resolve, reject) => {//先從連接池中取出一條連接 //異步操作pool.getConnection((err, connection) => {//如果失敗。執(zhí)行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')//創(chuàng)建磁盤存儲引擎
const storage = multer.diskStorage({ destination, filename })//創(chuàng)建上傳對象
const upload = multer({ storage })//它是上傳時目錄的生成函數
function destination(req, res, callback) {const date = new Date()//創(chuàng)建動態(tài)目錄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() + '.' + extnamecallback(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'),},],
})//全局路由前置守衛(wèi)
router.beforeEach(async (to, from) => {//從本地存儲中嘗試獲取tokenif (to.name == 'home' && !localStorage.getItem('token')) {return { name: 'login' }}
})
export default router
完整代碼
更新ing~