파일명 routes/shop.js
// 파일명 : routes/shop.js
var express = require('express');
var router = express.Router();
const db = require('mongodb').MongoClient;
const dburl = require('../config/mongodb').URL;
const dbname = require('../config/mongodb').DB;
// 토큰 발행을 위한 필요 정보 가져오기
const checkToken = require('../config/auth').checkToken;
const itemCount = 16; // 페이지에 보여줄 개수
// 주문 목록
// localhost:3000/shop/orderlist
router.get('/orderlist', checkToken, async function(req, res, next){
try{
const email = req.body.uid;
// DB접속
const dbconn = await db.connect(dburl);
// 2. DB선택 및 컬렉션 선택
const collection = dbconn.db(dbname).collection('order1');
const result = await collection.find(
{ orderid : email },
{ projection : { orderstep : 0, orderid : 0 } },
).toArray();
const collection1 = dbconn.db(dbname).collection('item1');
for(let i=0; i<result.length; i++){
const result1 = await collection1.findOne(
{ _id : result[i].itemcode },
{ projection : { name : 1, price : 1 } },
);
result[i]['itemname'] = result1['name'];
result[i]['itemprice'] = result1['price'];
}
return res.send({status:200, result:result });
}
catch(e){
console.error(e);
return res.send({status : -1, message:e });
}
});
// 시간대별 주문수량
// localhost:3000/shop/grouphour
router.get('/grouphour', async function(req, res, next) {
try{
const dbconn = await db.connect(dburl);
const collection = dbconn.db(dbname).collection('order1');
const result = await collection.aggregate([
{
$project : {
orderdate: 1, //주문일자
ordercnt : 1, //주문수량
month : {$month : '$orderdate'}, // 주문일자를 이용해서 달
hour : {$hour : '$orderdate'}, // 주문일자를 이용해서 시
minute : {$minute : '$orderdate'} // 주문일자를 이용해서 분
}
},
{
$group : {
_id : '$hour', // 그룹할 항목
count : { $sum : '$ordercnt' }
}
},
]).toArray();
return res.send({status:200, result:result});
}
catch(e){
console.error(e);
return res.send({status:-1, message:e});
}
});
// 상품별 주문수량
// localhost:3000/shop/groupitem
router.get('/groupitem', async function(req, res, next) {
try{
const dbconn = await db.connect(dburl);
const collection = dbconn.db(dbname).collection('order1');
// 그룹별 통계 aggregate
const result = await collection.aggregate([
{ $match : { itemcode : 1052 } },
{ $project : { _id : 1, itemcode : 1, ordercnt : 1 } },//가져올 항목( 물품코드, 주문수량 )
{
$group : {
_id : '$itemcode', // 그룹할 항목
count : { $sum : '$ordercnt' }
}
},
]).toArray();
return res.send({status:200, result:result});
}
catch(e){
console.error(e);
return res.send({status:-1, message:e});
}
});
// 주문하기
// localhost:3000/shop/order
// _id : (PK) 주문번호 시퀀스사용
// itemcode : (FK) 물품내역 (물품번호, 물품과 관련된 모든 정보)
// ordercnt : 주문수량
// orderid : (FK) 주문자 ( 이메일, 고객과 관련된 모든 정보 )
// orderdate : 주문일자
// orderstep : 100(카트), 101(주문), 102(결제), 103(배송중), 104(배송완료
// 주문목록(조인) : member1 + item1 + order1
// 주문하기 위해서 로그인시 사용자의 토큰, 물품번호, 주문수량 필요
router.post('/order', checkToken, async function(req, res, next){
try{
// DB접속
const dbconn = await db.connect(dburl);
// 2. DB선택 및 컬렉션 선택
const collection = dbconn.db(dbname).collection('sequence');
const result = await collection.findOneAndUpdate(
{ _id : 'SEQ_ORDER1_NO' }, // sequence 값( _id 자동 생성)
{ $inc : {seq : 1} } // 1씩 증가
);
const obj = {
_id : result.value.seq, //주문번호
itemcode : Number( req.body.itemcode ), // 물품번호
ordercnt : Number( req.body.ordercnt ), // 주문수량
orderid : req.body.uid, // 주문자
orderdate : new Date(), // + ( 1000 * 60 *60 * 9 ), // 9시간 더하기
orderstep : 101,
};
const collection1 = dbconn.db(dbname).collection('order1');
const result1 = await collection1.insertOne(obj);
console.log(result1);
if(result1.insertedId === obj._id) {
return res.send({status:200});
}
return res.send({status:0});
}
catch(e){
console.error(e);
return res.send({status : -1, message:e });
}
});
// 상세화면 페이지
// localhost:3000/shop/selectone?code=1050
router.get('/selectone',async function(req, res, next) {
try{
const code = Number(req.query.code);
// DB접속
const dbconn = await db.connect(dburl);
// 2. DB선택 및 컬렉션 선택
const collection = dbconn.db(dbname).collection('item1');
const result = await collection.findOne(
{ _id : code },// 조건없음 (천체 가지고 오기)
{ projection : { filename:0, filedata:0, filesize:0, filetype:0, regdate:0 } },
);
// find [{},{},{}]
// findOne {}
result['imageUrl'] = `/shop/image?code=${code}`;
// 서브이미지 정보가 필요함
// 물품 1개를 조회할때 서브 이미지의 정보를 전송하는 부분
const collection1 = dbconn.db(dbname).collection('itemimg1');
const result1 = await collection1.find(
{ itemcode : code },
{ projection : { _id : 1 } }
).sort({ _id : 1 }).toArray();
// 수동으로 서버이미지 PK정보를 저장함
// result1 => [ { "_id": 10009 }, { "_id": 10010 }, { "_id": 10011 } ]
let arr = [];
for(let i=0; i<result1.length; i++){
arr.push({
imageUrl : `/shop/image1?code=${result1[i]._id}`
});
}
result['subImage'] = arr;
return res.send({status:200, result:result});
}
catch(e){
console.error(e);
return res.send({status : -1, message:e });
}
});
// 메인화면 페이지
// localhost:3000/shop/select?page=1
router.get('/select',async function(req, res, next) {
try{
const page = Number(req.query.page);
// DB접속
const dbconn = await db.connect(dburl);
// 2. DB선택 및 컬렉션 선택
const collection = dbconn.db(dbname).collection('item1');
// SQL문 DB가 알아듣는 문법 (INSERT, UPDATE, DELETE, SELECT)
// SQL문을 이용해서 DB연동 mybatis
// SQL문을 저장소(함수)를 DB연동 jpa
const result = await collection.find(
{},// 조건없음 (천체 가지고 오기)
{ projection : { filename:0, filedata:0, filesize:0, filetype:0, regdate:0 } },
).sort({ _id : 1 }) // 정렬(물품코드를 오름차순으로)
.skip( (page-1)*itemCount )// 생략할 개수
.limit(itemCount)
.toArray();
// result === [ { },{ },{ } ]
// for => [ { },{ },{ } ] => 위치를 i로 반복
// for(let i=0; i<result.length; i++){
// result[i]['imageUrl'] = `/shop/image?code=${result[i]._id}`
// }
// foreach [ { },{ },{ } ] => 내용을 tmp 로 반복
for(let tmp of result){
tmp['imageUrl'] = `/shop/image?code=${tmp._id}`
}
return res.send({status:200, result:result})
}
catch(e){
console.error(e);
return res.send({status : -1, message:e });
}
});
// 이미지 가지고 오기
// localhost:3000/shop/image?code=1050
router.get('/image', async function(req, res, next) {
try{
const code = Number(req.query.code);
const dbconn = await db.connect(dburl);
const collection = dbconn.db(dbname).collection('item1');
// 조회하면 나오는 키정보확인
const result = await collection.findOne(
{ _id : code },
{ projection : { filename:1, filedata:1, filesize:1, filetype:1 } }
);
console.log(result);
res.contentType(result.filetype);
return res.send(result.filedata.buffer);
}
catch(e){
console.error(e);
res.send({status : -1, message:e});
}
});
// localhost:3000/shop/image1?code=1050
router.get('/image1', async function(req, res, next) {
try{
const code = Number(req.query.code);
const dbconn = await db.connect(dburl);
const collection = dbconn.db(dbname).collection('itemimg1');
const result = await collection.findOne(
{ _id : code },
{ projection : { filename:1, filedata:1, filesize:1, filetype:1 } }
);
console.log(result);
res.contentType(result.filetype);
return res.send(result.filedata.buffer);
}
catch(e){
console.error(e);
res.send({status : -1, message:e});
}
});
module.exports = router;
===================================================
파일명 src/component/Home.vue
<template>
<div style="padding:5px">
<h3>src/component/Home.vue</h3>
<vueper-slides autoplay>
<vueper-slide v-for="tmp in state.slides" :key="tmp" :title="state.title" :image="tmp.image"></vueper-slide>
</vueper-slides>
<div v-if="state.items" style="margin-top:10px;">
<el-row :gutter="20" v-for="(i, idx1) in state.items.length/4" :key="i" >
<el-col :span="6" :gutter="10" v-for="(j, idx2) in 4" :key="j">
<div style="border:1px solid #cccccc; padding:20px; cursor:pointer; "
@click="handleDetailPage(state.items[(idx1 * 4) + idx2]._id)" >
{{ idx1 }}
{{ idx2 }}
{{ (idx1 * 4) + idx2 }}<br />
<img :src="state.items[ (idx1 * 4) + idx2 ].imageUrl" style="width:100%; height:200px;" />
{{ state.items[(idx1 * 4) + idx2 ].name }} <br />
{{ state.items[(idx1 * 4) + idx2 ].price }} <br />
{{ state.items[(idx1 * 4) + idx2 ].content }} <br />
</div>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import { VueperSlides, VueperSlide } from 'vueperslides';
import 'vueperslides/dist/vueperslides.css';
import {onMounted, reactive} from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';
export default {
components:{
VueperSlides, VueperSlide
},
setup () {
const router = useRouter();
const state = reactive({
slides : [
{ title : 'a', image : 'https://picsum.photos/500/300?image=10'},
{ title : 'b', image : 'https://picsum.photos/500/300?image=20'},
{ title : 'c', image : 'https://picsum.photos/500/300?image=30'},
{ title : 'd', image : 'https://picsum.photos/500/300?image=40'},
{ title : 'e', image : 'https://picsum.photos/500/300?image=50'},
],
page:1,
});
const handleLoadData = async() => {
const url = `/shop/select?page=${state.page}`;
const headers = {"Content-Type":"application/json"};
const response = await axios.get(url,{headers});
console.log(response.data);
if(response.data.status === 200){
state.items = response.data.result;
// 15 % 4 => 3 => 1
// 14 % 4 => 2 => 2
// 13 % 4 => 1 => 3
// 12 % 4 => 0 => 0
const mod = Math.floor(state.items.length % 4);
if(mod !== 0 ){
for(let i=0;i< 4-mod ;i++){
state.items.push({
content: "준비중입니다.",
imageUrl: require('../assets/default.jpg'),
name: "준비중",
price: 0,
quantity: 0,
seller: "",
_id: 0,
});
}
}
}
}
const handleDetailPage = (code) => {
router.push({name:'ItemContent', query:{code:code}});
}
onMounted( async()=>{
await handleLoadData();
})
return { state, handleDetailPage }
}
}
</script>
<style lang="scss" scoped>
.el-row {
margin-bottom: 20px;
}
.el-row:last-child {
margin-bottom: 0;
}
.el-col {
border-radius: 4px;
}
.bg-purple-dark {
background: #99a9bf;
}
.bg-purple {
background: #d3dce6;
}
.bg-purple-light {
background: #e5e9f2;
}
.grid-content {
border-radius: 4px;
min-height: 36px;
}
.row-bg {
padding: 10px 0;
background-color: #f9fafc;
}
</style>
===================================================
파일명 /src/components/ItemContent.vue
<template>
<div v-if="state.items">
<h3>/src/components/ItemContent.vue</h3>
{{state.items}}
<table border="1">
<thead>
<tr>
<th>이미지</th>
<td>
<img :src="state.items.imageUrl" style="width:300px; heigh:300px" />
</td>
</tr>
<tr>
<th>이름</th>
<td>{{state.items.name}}</td>
</tr>
<tr>
<th>가격</th>
<td>{{state.items.price}}</td>
</tr>
<tr>
<th>수량</th>
<td>{{state.items.quantity}}</td>
</tr>
<tr>
<th>내용</th>
<td>{{state.items.content}}</td>
</tr>
<tr>
<th>서브이미지</th>
<td>
<div v-for="tmp in state.items.subImage" :key="tmp" style="display:inline-block">
<img :src="tmp.imageUrl" style="width:50px; heigh:50px" />
</div>
</td>
</tr>
</thead>
</table>
</div>
</template>
<script>
import { onMounted, reactive } from 'vue'
import { useRoute } from 'vue-router';
import axios from 'axios';
export default {
setup () {
const route = useRoute();
const state = reactive({
code : route.query.code,
})
const handleLoadData = async() => {
const url = `/shop/selectone?code=${state.code}`;
const headers ={"Content-Type":"application/json"};
const response = await axios.get(url, {headers});
console.log(response.data);
if(response.data.status === 200 ){
state.items = response.data.result;
}
console.log(state.items);
};
onMounted( async()=>{
await handleLoadData();
});
return {state}
}
}
</script>
<style lang="scss" scoped>
</style>