vue

 

SQL

CREATE TABLE `t_product` (
  `product_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `product_name` varchar(200) NOT NULL,
  `product_price` int(11) NOT NULL DEFAULT 0,
  `delivery_price` int(11) NOT NULL DEFAULT 0,
  `add_delivery_price` int(11) NOT NULL DEFAULT 0,
  `tags` varchar(100) DEFAULT NULL,
  `outbound_days` int(2) NOT NULL DEFAULT 5,
  `seller_id` int(10) unsigned NOT NULL,
  `category_id` int(10) unsigned DEFAULT NULL,
 `active_yn` char(1) NOT NULL DEFAULT 'Y',
  `created_date` datetime NOT NULL DEFAULT current_timestamp(),
  PRIMARY KEY (`product_id`),
  KEY `t_product_FK` (`seller_id`),
  KEY `t_product_FK_1` (`category_id`),
  CONSTRAINT `t_product_FK` FOREIGN KEY (`seller_id`) REFERENCES `t_seller` (`seller_id`),
  CONSTRAINT `t_product_FK_1` FOREIGN KEY (`category_id`) REFERENCES `t_category` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `t_seller` (
  `seller_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `email` varchar(50) NOT NULL,
  `phone` varchar(20) NOT NULL,
  PRIMARY KEY (`seller_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4;


CREATE TABLE `t_category` (
  `category_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `category1` varchar(100) NOT NULL,
  `category2` varchar(100) NOT NULL,
  `category3` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `t_image` (
  `image_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `product_id` int(10) unsigned NOT NULL,
  `type` int(1) NOT NULL DEFAULT 1 COMMENT '1-썸네일,2-제품이미지,3-제품설명이미지',
  `path` varchar(100) NOT NULL,
  PRIMARY KEY (`image_id`),
  KEY `t_image_FK` (`product_id`),
  CONSTRAINT `t_image_FK` FOREIGN KEY (`product_id`) REFERENCES `t_product` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `t_user` (
  `email` varchar(50) NOT NULL,
  `type` int(1) NOT NULL DEFAULT 1 COMMENT '1-buyer,2-seller',
  PRIMARY KEY (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

 

 

 

Node 서버

sql.js

module.exports = {
   
   

    productDetail: {
        query: `
            SELECT tp.*, tc.category1 , tc.category2 , tc.category3 , ti.path, ti.type
                FROM t_product tp, t_category tc , t_image ti
            WHERE tp.category_id =tc.category_id  AND tp.product_id =ti.product_id AND ti.type=3 AND tp.product_id=?
         `
    },

    productMainImages: {
        query: `SELECT * from t_image ti WHERE  ti.type=2 AND product_id =? `
    },


    productInsert: {
        
        query: `INSERT INTO t_product set ?`

    },

    productImageInsert: {
      
        query: `insert into t_image set ?`
    },

    imageList: {
        query: `select * from t_image where product_id=?`
    },
    imageDelete: {
        query: `delete from t_image where image_id=?`
    },






}

 

 

app.js
 

const express = require('express');
const app = express();
const session = require('express-session');
const fs = require("fs");


const mysql = require('mysql');
const db = {
    connectionLimit: 10,
    host: '192.168.174.1',
    port: '3308',
    user: 'dev',
    password: '1111',
    database: 'dev'
}
const dbPool = mysql.createPool(db);


app.use(session({
    secret: 'secret code',
    resave: false,
    saveUninitialized: false,
    cookie: {
        secure: false,
        maxAge: 1000 * 60 * 60 //쿠기 유효시간 1시간
    }
}));
/*
Expressjs에서 JSON Request Body 파싱하기
expressjs에서 웹 서비스를 제작 했을때, json으로 이루어진 Request Body를 받았을 경우, 요청값을 제대로 받아오지 못하는 문제가 발생한다.
expressjs에서는 이러한 문제를 해결하는 방법으로 크게 2가지 방법을 사용할 수 있다.
body-parser 모듈 사용(4.16 이전 버전).
express.json() 사용
*/
//express 4.16  이전 버전
//app.use(bodyParser);
//express  4.16 이후 버전
app.use(express.json({
    limit: '50mb'
}));


app.use(session({
    secret: 'secret code',  //세션에 대한 키값
    resave: false,  //request 요청이 왔을때 session 수정사항을 다시 저장할 것인지
    saveUninitialized: false, // session 저장 내역이 없더라도 다시 저장할 것인지
    cookie: {
        secure: false,
        maxAge: 1000 * 60 * 60 //쿠키 유효시간 1시간
    }
}));




const server = app.listen(3000, () => {
    console.log('Server started. port 3000. ');
});






app.post('/api/:alias', async (request, res) => {

    if (!request.session.email) {
        //   return res.status(401).send({ error: "Youn need to login." });
    }

    try {


        /*
            다음과 같은 형식으로 데이터를 넘겨준다
            {"param":["1"]}
        */

        res.send(await req.db(request.params.alias, request.body.param));

    } catch (err) {
        console.log("err : " + err);
        res.status(500).send({
            error: err
        });
    }

});


app.post('/upload/:productId/:type/:fileName', async (request, res) => {

    let {
        productId,
        type,
        fileName
    } = request.params;
    const dir = `${__dirname}/uploads/${productId}`;
    const file = `${dir}/${fileName}`;
    if (!request.body.data) return fs.unlink(file, async (err) => res.send({
        err
    }));
    const data = request.body.data.slice(request.body.data.indexOf(';base64,') + 8);
    if (!fs.existsSync(dir)) fs.mkdirSync(dir);
    fs.writeFile(file, data, 'base64', async (error) => {
        await req.db('productImageInsert', [{
            product_id: productId,
            type: type,
            path: fileName
        }]);

        if (error) {
            res.send({
                error
            });
        } else {
            res.send("ok");
        }
    });
});

app.get('/download/:productId/:fileName', (request, res) => {
    const {
        productId,
        type,
        fileName
    } = request.params;
    const filepath = `${__dirname}/uploads/${productId}/${fileName}`;
    res.header('Content-Type', `image/${fileName.substring(fileName.lastIndexOf("."))}`);
    if (!fs.existsSync(filepath)) res.send(404, {
        error: 'Can not found file.'
    });
    else fs.createReadStream(filepath).pipe(res);
});




const req = {
    async db(alias, param = [], where = '') {
        return new Promise((resolve, reject) => dbPool.query(sql[alias].query + where, param, (error, rows) => {
            if (error) {
                if (error.code != 'ER_DUP_ENTRY')
                    console.log(error);
                resolve({
                    error
                });
            } else resolve(rows);
        }));
    }
};







 

 

 

Vue

vue.config.js

업로드 다운로드 프록시 설정

const target = "http://127.0.0.1:3000";

module.exports = {
    devServer: {
        port: 8080,
        proxy: {
            '^/api': {
                target,
                changeOrigin: true
            },
            '^/upload': {
                target,
                changeOrigin: true,
            },
            '^/download': {
                target,
                changeOrigin: true,
            }
        }
    }
}

 

 

mixins.js

 mixins 파일에 $base64 추가

base64로 인코딩하는가?

데이터베이스에 전송할 때 이미지인 경우 base64 타입을 요구하는 경우가 있을 수 있습니다. 보통 이미지는 base64로 인코딩 후 blob DB에 업로드합니다. 또는 인증과 관련된 정보 역시 base64로 인코딩 후 전송하기도 합니다.

 

    $base64(file) {
      return new Promise(resolve => {
        var a = new FileReader();
        a.onload = e => resolve(e.target.result);
        a.readAsDataURL(file);
      });
    },

 

 

ImageInsert.vue

 

<template>
    <div>


    <main class="mt-3">
        <div class="container">
            <h2 class="text-center">제품 사진 등록</h2>

             <div class="mb-3 row">
                <label class="col-md-3 col-form-label">제품ID</label>
                <div class="col-md-9">
                  {{productId}}
                </div>
            </div>


            <div class="mb-3 row">
                <label class="col-md-3 col-form-label">제품명</label>
                <div class="col-md-9">
                  {{productDetail.product_name}}
                </div>
            </div>


                <div class="mb-3 row">
                        <label class="col-md-3 col-form-label">썸네일이미지</label>
                        <div class="col-md-9">
                        <div class="row">
                            <div class="col-lg-3 col-md-4 col-sm-2" :key="i" v-for="(m,i) in productImage.filter(c=>c.type==1)">
                            <div class="position-relative">
                                <img :src="`/download/${productId}/${m.path}`" class="img-fluid" />
                                <div class="position-absolute top-0 end-0" style="cursor:pointer;" @click="deleteImage(m.image_id)">X</div>
                            </div>
                            </div>
                        </div>
                        <input type="file" class="form-control" accept="image/png,image/jpeg" @change="uploadFile($event.target.files, 1)">
                        <div class="alert alert-secondary" role="alert">
                            <ul>
                            <li>이미지 사이즈 : 350*350</li>
                            <li>파일 사이즈 : 1M 이하</li>
                            <li>파일 확장자 : png, jpg만 가능</li>
                            </ul>
                        </div>
                        </div>
            </div>


            <div class="mb-3 row">
                <label class="col-md-3 col-form-label">제품이미지</label>
                <div class="col-md-9">

                <div class="row">
                        <div class="col-lg-3 col-md-4 col-sm-2" :key="i" v-for="(m,i) in productImage.filter(c=>c.type==2)">
                        <div class="position-relative">
                            <img :src="`/download/${productId}/${m.path}`" class="img-fluid" />
                            <div class="position-absolute top-0 end-0" style="cursor:pointer;" @click="deleteImage(m.image_id)">X</div>
                        </div>
                        </div>
                    </div>


                      <input type="file" class="form-control" accept="image/png,image/jpeg"  @change="uploadFile($event.target.files, 2)">
                    <div class="alert alert-secondary" role="alert">
                        <ul>
                            <li>최대 5개 가능</li>
                            <li>이미지 사이즈 : 700*700</li>
                            <li>파일 사이즈 : 1M 이하</li>
                            <li>파일확장자 : png,jpg 만 가능</li>
                        </ul>
                    </div>
                </div>
            </div>


 <div class="mb-3 row">
        <label class="col-md-3 col-form-label">제품설명이미지</label>
        <div class="col-md-9">
          <div class="row">
            <div class="col-lg-6 col-md-8">
              <input type="file" class="form-control" accept="image/png,image/jpeg" @change="uploadFile($event.target.files, 3)">
              <div class="alert alert-secondary" role="alert">
                <ul>
                  <li>파일 사이즈 : 5M 이하</li>
                  <li>파일 확장자 : png, jpg만 가능</li>
                </ul>
              </div>
            </div>
            <div class="col-lg-6 col-md-4" :key="i" v-for="(m,i) in productImage.filter(c=>c.type==3)">
              <div class="position-relative">
                <img :src="`/download/${productId}/${m.path}`" class="img-fluid" />
                <div class="position-absolute top-0 end-0" style="cursor:pointer;color:white;" @click="deleteImage(m.image_id)">X</div>
              </div>
            </div>
          </div>
          
        </div>
      </div>

            <div class="mb-3 row m-auto">
                 <div class="mb-3 row m-auto">
                    <button type="button" class="btn btn-lg btn-dark" @click="goToList">확인</button>
                </div>
            </div>



        </div>
    </main>



    </div>
</template>

<script>
    export default {
        data(){
            return {
                productId:0,
                proudctName:"",
                productDetail: {},
                productImage: [],
                
            }
        },

        computed:{
            user(){
                return this.$store.state.user;
            }
        },
        created() {
            this.productId = this.$route.query.product_id;
            this.getProductDetail();
            this.getProductImage()
        },

        mounted() {
            if(this.user.email==undefined){
                //로그인 되어 있지 않으면 첫화면으로
               alert("로그인을 해야 이용할 수 있습니다.");
               this.$router.push({path:'/'});

            }
        },

        methods: {
            goToList(){
                this.$router.push({path:'/salesList'}); 
            },
         

             async getProductDetail() {
                  console.log("상세페이지 - getProductDetail  : "  + this.productId);
                  
                let productDetail = await this.$api("/api/productDetail", "POST", {
                    param: [this.productId]
                });
               
               console.log(productDetail.category1);

                if (productDetail.length > 0) {
                    this.productDetail = productDetail[0];                             
                }else{
                    console.log("0");
                }
                console.log(productDetail.category1);
                
            },



        async getProductImage() {
            this.productImage = await this.$api("/api/imageList", "POST" ,{param:[this.productId]});
            console.log('this.productImage',this.productImage)
            },

           deleteImage(id) {
                this.$swal.fire({
                    title: '정말 삭제 하시겠습니까?',
                    showCancelButton: true,
                    confirmButtonText: `삭제`,
                    cancelButtonText: `취소`
                }).then(async (result) => {
                    if (result.isConfirmed) {
                    await this.$api("/api/imageDelete",  "POST" ,{param:[id]});
                    this.getProductImage();
                    this.$swal.fire('삭제되었습니다!', '', 'success');
                    } 
                });
             },

            async uploadFile(files, type) {
                    let name = "";
                    let data = null;
                    if (files) {
                        name = files[0].name;
                        data = await this.$base64(files[0]);
                    }
                    const { error } = await this.$api(`/upload/${this.productId}/${type}/${name}`, "POST", { data });
                    if (error) {
                        return this.$swal("이미지 업로드 실패했습니다. 다시 시도하세요.");
                    }

                    this.$swal("이미지가 업로드 되었습니다.");

                        setTimeout(() => {
                            this.getProductImage();
                        }, 1000);
                    }
           },
        
    }
</script>

   

<style scoped>

</style>

 

 

 

 

https://github.com/braverokmc79/my-vue-project-1.git

 

#npm init

node 디렉토리에서 서버 구동

#node app.js

 

client 디렉토리에서 vue 구동

#npm run serve

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

빼앗을 수 없는 뜻과 꺾이지 않는 기상과 흐르지 않는 앎을 늘 지니도록 하라. -퇴계 이황

댓글 ( 4)

댓글 남기기

작성












vue 목록    more