파일명 src/component/Login.vue
<template>
<div>
<h3>src/component/Login.vue</h3>
{{state}}
<hr />
<input type="text" v-model="state.userid"/>
<input type="password" v-model="state.userpw"/>
<input type="button" value="로그인" @click="handleLogin"/>
</div>
</template>
<script>
import { reactive } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
export default {
// ver 3.0
setup () { // this를 사용할 수 없음
const router = useRouter();
const store = useStore();
const state = reactive({
userid : 'aaa@gmail.com',
userpw : 'aaa',
});
const handleLogin = async() => {
const url = `/member/select`;
const headers = {"Content-Type":"application/json"};
const body = {
email : state.userid,
password : state.userpw
};
const response = await axios.post(url, body, {headers});
console.log(response.data.status);
if(response.data.status === 200){
console.log(response.data.token)
// 저장소에 보관하기
sessionStorage.setItem("TOKEN", response.data.token);
alert('로그인 되었습니다.');
const curl = sessionStorage.getItem("CURL");
if(curl === null){
// 주소창만 바뀜
router.push({name:'Home'});
// app.vue에 메뉴의 선택항목을 변경하도록 알려줌
// 메뉴 활성화
store.commit("setMenu","/");
}
else {
const query = JSON.parse(sessionStorage.getItem("CURL_QUERY"));
const params = JSON.parse(sessionStorage.getItem("CURL_PARAMS"));
router.push({name:curl, query:query, params:params});
}
// 로그인 상태
store.commit("setLogged",true);
}
else{
alert('아이디와 암호를 확인하세요');
}
};
return {state, handleLogin}
},
}
</script>
<style lang="scss" scoped>
</style>
================================================
파일명 /src/components/ItemContent.vue
<template>
<div v-if="state.items" style="padding:10px">
<h3>/src/components/ItemContent.vue</h3>
{{state}}
<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>
<select v-model="state.items.ordercnt">
<option v-for="no in Number(state.items.quantity)" :key="no">
{{ no }}
</option>
</select>
</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>
<hr />
<button @click="handleOrderAction">주문하기</button>
</div>
</template>
<script>
import { onMounted, reactive } from 'vue'
import { useRoute } from 'vue-router';
import { useRouter } from 'vue-router';
import axios from 'axios';
export default {
setup () {
const route = useRoute();
const router = useRouter();
const state = reactive({
code : route.query.code,
});
const handleOrderAction= async() =>{
router.push({name:'Order',
query : {
code : state.items._id,
cnt : state.items.ordercnt
}
});
}
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, handleOrderAction}
}
}
</script>
<style lang="scss" scoped>
</style>
================================================
파일명 /src/components/Order.vue
<template>
<div style="padding:10px">
<h3>/src/components/Order.vue</h3>
<hr />
{{state}}
<hr />
<table border="1">
<thead>
<tr>
<th>물품 번호</th>
<td>{{state.code}}</td>
</tr>
<tr>
<th>주문 수량</th>
<td>{{state.cnt}}</td>
</tr>
</thead>
</table>
<hr />
<button @click="handleOrderAction">결제하기</button>
</div>
</template>
<script>
import { useRoute } from 'vue-router';
import { useRouter } from 'vue-router';
import { reactive } from 'vue'
import axios from 'axios';
export default {
setup () {
const route = useRoute();
const router = useRouter();
const state = reactive({
code : route.query.code, // 주문번호
cnt : route.query.cnt, // 물품수량
token : sessionStorage.getItem("TOKEN"),
});
const handleOrderAction = async() =>{
const url = `/shop/order`;
const headers = { "Content-Type":"application/json", "token" : state.token };
const body = {
itemcode : Number(state.code),
ordercnt : Number(state.cnt),
};
const response = await axios.post(url, body, {headers});
console.log(response);
if(response.data.status === 200 ){
alert('주문 되었습니다.')
router.push({name : 'Mypage', query:{ menu:4 }});
}
// 마이페이지의 주문내역페이지 이동
}
return {state, handleOrderAction}
}
}
</script>
<style lang="scss" scoped>
</style>
================================================
파일명 src/component/Seller.vue
<template>
<div style="padding:10px">
<h3>src/component/Seller.vue</h3>
<hr />
<button @click="handleMenu(1)">물품관리</button>
<button @click="handleMenu(2)">회원관리</button>
<button @click="handleMenu(3)">주문관리</button>
<hr />
<menu-1 v-if="state.menu === 1"></menu-1>
<menu-2 v-if="state.menu === 2"></menu-2>
<menu-3 v-if="state.menu === 3"></menu-3>
</div>
</template>
<script>
import {onMounted, reactive} from 'vue';
import Menu1 from './seller/Menu1.vue';
import Menu2 from './seller/Menu2.vue';
import Menu3 from './seller/Menu3.vue';
import { useRouter } from 'vue-router';
import { useRoute } from 'vue-router';
export default {
components:{
Menu1,Menu2,Menu3
},
setup () {
const router = useRouter();
const route = useRoute();
const state = reactive({
menu: Number(route.query.menu)
});
onMounted( ()=>{
console.log('menu Number ===',route.query.menu)
if(typeof route.query.menu === 'undefined'){
state.menu = 1
}
})
const handleMenu = (idx)=> {
router.push({name:'Seller', query:{menu:idx}});
state.menu = idx;
}
return {state, handleMenu}
}
}
</script>
<style lang="scss" scoped>
</style>
================================================
파일명 src/component/Mypage.vue
<template>
<div style="padding:10px">
<h3>src/component/Mypage.vue</h3>
<button @click="handleMenu(1)">정보수정</button>
<button @click="handleMenu(2)">암호변경</button>
<button @click="handleMenu(3)">회원탈퇴</button>
<button @click="handleMenu(4)">주문내역</button>
<hr />
<menu-1 v-if="state.menu === 1"></menu-1>
<menu-2 v-if="state.menu === 2"></menu-2>
<menu-3 v-if="state.menu === 3"></menu-3>
<menu-4 v-if="state.menu === 4"></menu-4>
</div>
</template>
<script>
import {onMounted, reactive} from 'vue';
import Menu1 from './mypage/Menu1.vue';
import Menu2 from './mypage/Menu2.vue';
import Menu3 from './mypage/Menu3.vue';
import Menu4 from './mypage/Menu4.vue';
import { useRouter } from 'vue-router';
import { useRoute } from 'vue-router';
export default {
components:{
Menu1, Menu2, Menu3, Menu4,
},
setup () {
const router = useRouter();
const route = useRoute();
const state = reactive({
menu: Number(route.query.menu)
});
onMounted( ()=>{
console.log('menu Number ===',route.query.menu)
if(typeof route.query.menu === 'undefined'){
state.menu = 1
}
})
const handleMenu = (idx)=> {
router.push({name:'Mypage', query:{menu:idx}});
state.menu = idx;
}
return {state, handleMenu}
}
}
</script>
<style lang="scss" scoped>
</style>
================================================
파일명 src/routes/index.js
// npm install vue-router@next --save
// 파일명 src/routes/insex.js
import { createWebHashHistory, createRouter } from "vue-router";
// 라우터 설정하기
import Home from '@/components/Home.vue';
import Board from '@/components/Board.vue';
import Login from '@/components/Login.vue';
import Logout from '@/components/Logout.vue';
import Admin from '@/components/Admin.vue';
import Join from '@/components/Join.vue';
import BoardContent from '@/components/BoardContent.vue';
import Chart from '@/components/Chart.vue';
import Chart1 from '@/components/Chart1.vue';
import BoardWrite from '@/components/BoardWrite.vue';
import BoardTest from '@/components/BoardTest.vue';
import BoardUpdate from '@/components/BoardUpdate.vue';
import Mypage from '@/components/Mypage.vue';
import Seller from '@/components/Seller.vue';
import Menu1Insert from '@/components/seller/Menu1Insert.vue';
import Menu1Detail from '@/components/seller/Menu1Detail.vue';
import ItemContent from '@/components/ItemContent.vue';
import Order from '@/components/Order.vue';
const routes = [
{ path : '/', name: "Home", component:Home },
{ path : '/login', name: "Login", component:Login },
{ path : '/logout', name: "Logout", component:Logout },
{ path : '/board', name: "Board", component:Board },
{ path : '/admin', name: "Admin", component:Admin },
{ path : '/join', name: "Join", component:Join },
{ path : '/boardcontent', name: "BoardContent", component:BoardContent },
{ path : '/chart', name: "Chart", component:Chart },
{ path : '/chart1', name: "Chart1", component:Chart1 },
{ path : '/boardwrite', name: "BoardWrite", component:BoardWrite },
{ path : '/boardtest', name: "BoardTest", component:BoardTest },
{ path : '/boardupdate', name: "BoardUpdate", component:BoardUpdate },
{ path : '/mypage', name: "Mypage", component:Mypage },
{ path : '/seller', name: "Seller", component:Seller },
{ path : '/menu1insert', name: "Menu1Insert", component:Menu1Insert },
{ path : '/menu1detail', name: "Menu1Detail", component:Menu1Detail },
{ path : '/itemcontent', name: "ItemContent", component:ItemContent },
{ path : '/order', name: "Order", component:Order },
]
// localhost : 8080/#/
// 127.0.0.1:8080/#/login
// 127.0.0.1:8080/#/board
// 라우터 생성 (주소표기방식, 라우터 설정 변수)
const router = createRouter({
history : createWebHashHistory(), // 127.0.0.1:8080/#/login
routes : routes
})
// 라우터 이동 경로 확인 (이동하는 페이지, 이동전 페이지, 다음페이지로 이동)
router.beforeEach((to, from, next) => {
//console.log('이동하는 페이지:', to.path);
console.log( to , from );
// 저장소에 보관하기
// sessionStorage.setItem('CURL',to.path);
// console.log('이동전 페이지:', from);
const token = sessionStorage.getItem("TOKEN");
console.log('token있나 없나???',token);
// 추가) 토큰이 유효한 상태인지를 주기적 확인후에 페이지 진입
// axios를 이용해서 백엔드 연동 할 예정
// 로그인이 되어야만 이동하는 페이지 조건
// ex) 주문페이지, 회원용 게시판의 글쓰기...
if(to.name === 'Order' || to.name === 'BoardWrite'){
if(token === null){
// 로그인이 성공하고 이동하고자 하는 페이지를 알려주는 역할
// 로그인 페이지에 CURL을 꺼내서 원하는 페이지로 이동
sessionStorage.setItem('CURL',to.name);
// sessionStorage는 object자체를 보관할 수 없음.
// 방법은 object -> string으로 변환해서 보관함
sessionStorage.setItem("CURL_QUERY", JSON.stringify(to.query));
sessionStorage.setItem("CURL_PARAMS", JSON.stringify(to.params));
return next({ name : 'Login' }); // 로그인페이지로 이동
}
}
// 원래 이동하고자 하는 페이지
if(to.name !== 'Login'){
sessionStorage.removeItem("CURL");
}
next();
})
export default router;
================================================
파일명 src/components/mypage/Menu4.vue
<template>
<div v-if="state.items">
<h3>Menu4</h3>
{{state}}
<hr />
<button @click="handleOrderDelete">주문 삭제</button>
<hr />
<table border="1" style="text-align : center">
<thead>
<tr>
<th>주문번호</th>
<th>체크</th>
<th>물품명</th>
<th>주문가격</th>
<th>주문수량</th>
<th>주문일자</th>
<th>주문별 총액</th>
</tr>
</thead>
<tbody>
<tr v-for="item in state.items" :key="item">
<td><input type="checkbox" :value="item._id" v-model="state.chk"/></td>
<td>{{ item._id }}</td>
<td>{{ item.itemname }}</td>
<td>{{ item.itemprice }}</td>
<td>{{ item.ordercnt }}</td>
<td>{{ item.orderdate }}</td>
<td>{{ item.total}}</td>
</tr>
<tr>
<th colspan="6">총 합계</th>
<td>{{state.total1}}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import { onMounted, reactive } from 'vue';
import axios from 'axios';
export default {
setup () {
const state = reactive({
chk: [],
total1 : 0,
token : sessionStorage.getItem("TOKEN")
});
const handleOrderDelete = async() =>{
if(state.chk.length === 0 ){
alert('항목을 선택하세요');
return false;
}
if(confirm('주문 취소 할까요?')){
const url = `/shop/orderdelete`;
const headers = {"Content-Type":"application/json", "token" : state.token};
const response = await axios.delete(url, {headers:headers, data:{chk:state.chk}});
console.log(response.data);
if(response.data.status === 200){
alert('주문 취소 되었습니다.');
state.chk = [];
handleLoadData();
}
}
}
const handleLoadData = async() => {
const url = `/shop/orderlist`;
const headers ={"Content-Type":"application/json", "token" : state.token};
const response = await axios.get(url, {headers});
console.log(response.data);
if(response.data.status === 200 ){
state.items = response.data.result;
for(let i=0; i<state.items.length; i++){
state.items[i]['total'] = Number(state.items[i]['itemprice']) * Number(state.items[i]['ordercnt']);
state.total1 += Number(state.items[i]['total']);
}
}
console.log(state.items);
};
onMounted( async()=>{
await handleLoadData();
});
return {state, handleOrderDelete}
}
}
</script>
<style lang="scss" scoped>
</style>
================================================
파일명 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/orderdelete
router.delete('/orderdelete', checkToken, async function(req, res, next) {
try{
const code = req.body.chk;
console.log('code===========',code);
// DB접속
const dbconn = await db.connect(dburl);
// DB선택 및 컬렉션 선택
const collection = dbconn.db(dbname).collection('order1');
// { $in : [ 1,2,3,4 ] } 포함 된 항목
const result = await collection.deleteMany(
{ _id : {$in : code}, orderid:req.body.uid }
)
console.log(result);
if(result.deletedCount === code.length){
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/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;