카테고리 없음

5. 0827_ Vue 더 알아보기

55yudi 2021. 8. 30. 08:46

* ubi폴더의 customer-index.html을 싱글페이지로 쪼개는 작업

* onclick은 html에서 작동하고, vue에서는 작동하지 않는다. --> @click 으로 작성해야 함

 

Ex07

- 터미널 yarn.cmd serve 서버 실행

- 포트넘버 브라우저에 띄우기

 

 

<CustomerList.vue>

- 추가하기 버튼 누를 때 넘어가도록 하기

온클릭 --> @클릭 으로

함수넣기 showAddPage() {}

 

<CustomerAdd.vue>

card-body 안의 이름, 나이, 전화번호 input 태그마다 속성 코드 추가 : v-model="item.name"

script태그 추가, 안에 객체 생성 추가

 

- 수정 버튼 기능 추가

온클릭 --> @클릭 으로

index로 변수 지정, item.index와 함수()안에 index

함수넣기 showModifyPage() {}

 

수정하고 다시  list페이지로 넘어갈 수 있도록 add페이지에 mounted설정

 

<input> 이름, 나이, 전화번호 태그 속성에

ref="nameInput" --> id같은 역할을 함

 

this.$refs.nameInput.focus()

this.$refs.ageInput.focus()

this.$refs.mobileInput.focus()

 


ubi폴더의 > <customer-index.html> 파일업로드

<form id="FILE_FORM" method="post" enctype="multipart/form-data">
                            <label>파일 업로드</label>
                            <input type="file" id="pictureInput">
                            <img src="person.png" id="pictureImage">
                            <input type="button" value="업로드하기" onclick="uploadFile()">
                        </form>
function uploadFile() {
                const form = $('#FILE_FORM')[0] //제이쿼리로 폼태그를 찾아서 넣어줌
                const formData = new FormData(form)
                formData.append('photo', $('#pictureInput')[0].files[0]) //url에 컨트롤러로 받아준다(?)

                axios.post(
                    '/profile/upload', //url등을 안쓰고 그냥 쓰면 된다
                    formData,
                    {
                        headers: {
                            'Content-Type':'multipart/form-data;charset=utf-8;'
                        },
                        onUploadProgress: (e) => { //업로드 진행상황을 알게 해주는 함수, e:event
                            console.log('onUploadProgress 호출됨')
                        }
                    }
                ).then((response) => {
                    console.log(`응답 : ${response.data}`)

                }).catch((err) => {
                    console.log(`에러 : ${err}`)
                })
            }

- 파일선택 버튼을 눌러서 이미지 파일을 선택하고 업로드하기 버튼을 누르면

person.png 픽셀이 너무 컸나 보다,,,

 

터미널 창에도 로그가 찍힌 것을 볼 수 있다

코드 수정하고 나서 다시 업로드하기 버튼을 누르면

 <div>
                            <img id="imageOutput" src="person.png">
                        </div>
console.log(`응답 : ${JSON.stringify(response.data)}`)

                    $('#imageOutput').attr('src', response.data.output.filename)

코드 추가 후 다시 업로드하기 버튼 누르면 이미지가 뜬다!

 

- 이미지파일 선택했을 때 미리보기

document.ready() {}안에 코드 추가하고, 함수 만들기

$('#pictureInput').change(() => {
                    previewPicture()
function previewPicture() {
                const input = $('#pictureInput')[0]
                if (input.files && input.files[0]) {
                    const reader = new FileReader()

                    reader.onload = (event) => { //비동기로 실행, 파일을 읽어서 event로 넘어옴
                        $('#pictureImage').attr('src', event.target.result)
                    }

                    reader.readAsDataURL(input.files[0]) //읽으라고 명령
                }
            }

 

- 이 아이콘 이미지를 서버에서 가져온 이미지로 넣어보기

와 귀여웡

 

- 추가 페이지에서 정보와 이미지 추가하고 저장버튼을 누르면 넘어오도록 파일업로드 부분 코드 전부 추가하기

saveItem함수 코드 수정

function saveItem() {

                //1. 파일 업로드
                uploadFile((err, filename) => {
                    if (err) {
                        return
                    }

                    //2. 고객정보 추가 / 수정
                    if (mode == 'add') {
                        requestAdd(filename) //파일을 추가하는 경우
                    } else if (mode == 'modify') {
                        requestModify()
                    }

                    showPage(0)
                })

            }

uploadFile함수 코드 수정

function uploadFile(callback) {
                const form = $('#FILE_FORM')[0] //제이쿼리로 폼태그를 찾아서 넣어줌
                const formData = new FormData(form)
                formData.append('photo', $('#pictureInput')[0].files[0]) //url에 컨트롤러로 받아준다(?)

                axios.post(
                    '/profile/upload', //url등을 안쓰고 그냥 쓰면 된다
                    formData,
                    {
                        headers: {
                            'Content-Type':'multipart/form-data;charset=utf-8;'
                        },
                        onUploadProgress: (e) => { //업로드 진행상황을 알게 해주는 함수, e:event
                            console.log('onUploadProgress 호출됨')
                        }
                    }
                ).then((response) => {
                    console.log(`응답 : ${JSON.stringify(response.data)}`)

                    $('#imageOutput').attr('src', response.data.output.filename)

                    callback(null, response.data.output.filename)

                }).catch((err) => {
                    console.log(`에러 : ${err}`)

                    callback(err, null)
                })
            }

requestAdd 함수 코드 수정

function requestAdd(filename) {
                console.log('requestAdd 함수 호출됨')

                const name = $('#nameInput').val()
                const age = $('#ageInput').val()
                const mobile = $('#mobileInput').val()

                axios({
                    method: 'post',
                    url: '/customer',
                    data: {
                        name: name,
                        age: age,
                        mobile: mobile,
                        filename: filename
                    },
                    responseType:'json'
                }).then((response) => {
                    console.log(`응답 : ${JSON.stringify(response.data)}`)

                    requestList(1, 3)
                }).catch((err) => {
                    console.log(`에러 : ${err}`)
                })
            }

- 과제,,


ubi폴더에 > users-controller.js 생성

- database폴더에 > sql 폴더에 > 파일 생성 : users_sql.js

module.exports = {
    users_find: {
        sql: "select id, name, age, mobile, filename \
                from test.customer \
                where id = :id" //:id 는 파라미터임 (아이디를 다른데에서 넣어준다~)
    }
}

 

- postman을 실행

- users라는 콜렉션을 만들어 준다.

- find라는 request를 만들고 get방식을 이용하여 주소를 가져온다.

 

- 응답 부분 코드 작성

const util= require('../util/util')
const param = require('../util/param')
const Database = require('../database/database_mysql')

/**
 * @constroller(path="/users")
 */
class Users {

    constructor() {
        this.database = new Database('database_mysql')
    }
    /**
     *
     * @RequestMapping(path="/find", method="get")
     */
    async find(req, res) {
        const params = param.parse(req)

        const queryParams = {
            sqlName: 'users_find',
            params: params,
            paramType: {}
        }

        try {
            const rows = await this.database.execute(queryParams)
            console.log(`sql 실행 결과 : ${JSON.stringify(rows)}`)

            util.sendRes(res, 200, 'ok', rows)
        } catch(err) {
            console.log(`sql 실행 시 에러 : ${err}`)

            util.sendError(res, 400, `Error -> ${err}`)
        }
    }

}

module.exports = Users

- postman에서 응답이 온다.

- ,,,

 


[최종 코드]

 

public > index.html

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>

    <!-------------부트스트랩을 위한 CSS 파일 불러오기-------------->
    <!--  Font Awesome  -->
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.11.2/css/all.css">
    <!-- Bootstrap core CSS -->
    <link href="./mdb/css/bootstrap.min.css" rel="stylesheet">
    <!-- Material Design Bootstrap -->
    <link href="./mdb/css/mdb.min.css" rel="stylesheet">
    <!------------------------------------------------------------->


  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->

    <!------------부트스트랩을 위한 JS 파일 불러오기------------->
        <!-- JQuery -->
        <script type="text/javascript" src="./mdb/js/jquery.min.js"></script>
        <!-- Bootstrap tooltips -->
        <script type="text/javascript" src="./mdb/js/popper.min.js"></script>
        <!-- Bootstrap core JavaScript -->
        <script type="text/javascript" src="./mdb/js/bootstrap.min.js"></script>
        <!-- MDB core JavaScript -->
        <script type="text/javascript" src="./mdb/js/mdb.min.js"></script>
        <!------------------------------------------------------------------>

  </body>
</html>

src > main.js

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import VueAxios from 'vue-axios'
import Axios from 'axios'


createApp(App).use(store).use(router).use(VueAxios, Axios).mount('#app')

scr > App.vue

<template>
  <div>
    <div id="nav">
      <router-link to="/">고객목록</router-link> |
      <router-link to="/CustomerAdd">고객추가</router-link>
    </div>
    <router-view/>
  </div>
</template>

<style>
#app {
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;
}

#nav a {
  font-weight: bold;
  color: #2c3e50;
}

#nav a.router-link-exact-active {
  color: #42b983;
}
</style>

src > router > index.js

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

const routes = [
  {
    path: '/',
    name: 'CustomerList',
    component: CustomerList
  },
  {
    path: '/CustomerAdd',
    name: 'CustomerAdd',
    component: CustomerAdd
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

src > views > CustomerList.vue

<template>
  <div class="home">
    <h1>고객목록 입니다 </h1>

      <div class="container-fluid">
            <div class="card" style="margin: 1em;">
                <div class="card-body">

                    <!-- 리스트-->
                    <ul class="list-group" id="list1">
                        <li class="list-group-item active">
                            <div class="row">
                                <div class="col-sm-1 text-center align-self-center">
                                    <img src="../assets/group.png" style="width: 2em">
                                </div>
                                <div class="col-sm-2 text-center align-self-center">
                                    <span class="font-weight-bold">이름</span>
                                </div>
                                <div class="col-sm-2 text-center align-self-center">
                                    <span class="font-weight-bold">나이</span>
                                </div>
                                <div class="col-sm-3 text-center align-self-center">
                                    <span class="font-weight-bold">전화번호</span>
                                </div>
                                <div class="col-sm-4 text-center align-self-center">
                                    <span class="font-weight-bold">기능</span>
                                </div>
                            </div>
                        </li>

                        <!-- v-for : 데이터를 반복해서 여러 번 만들어 줌 -->
                        <li v-for="(item, index) in items" :key="item.id" class="list-group-item waves-effect" >
                            <div class="row">
                              <div class="col-sm-1 text-center align-self-center">
                                  <img src="../assets/person.png" style="width: 2em">
                              </div>
                              <div class="col-sm-2 text-center align-self-center">
                                  <span class="font-weight-bold">{{ item.name }}</span>
                              </div>
                              <div class="col-sm-2 text-center align-self-center">
                                  <span class="font-weight-bold">{{ item.age }}</span>
                              </div>
                              <div class="col-sm-3 text-center align-self-center">
                                  <span class="font-weight-bold">{{ item.mobile }}</span>
                              </div>
                              <div class="col-sm-4 text-center align-self-center">
                                  <button type="button" class="btn btn-outline-default btn-sm waves-effect font-weight-bold" @click="showModifyPage(index)">수정</button>
                                  <button type="button" class="btn btn-outline-danger btn-sm waves-effect font-weight-bold"  @click="deleteItem(index)">삭제</button>
                              </div>
                          </div>
                        </li>


                    </ul>
                </div>
                <div class="card-footer">

                    <div id="pagination">

                        <nav aria-label="Page navigation example">
                            <ul class="pagination pagination-circle pg-blue">
                                <li class="page-item" :class="{ 'disabled' : pagination.page == 1 }">
                                    <a v-if="pagination.page == 1" class="page-link" href="#">First</a>
                                    <a v-else @click="requestList(1, pagination.perPage)" class="page-link" href="#">First</a>
                                </li>
                                <li class="page-item" :class="{ 'disabled' : pagination.page == 1 }">
                                    <a v-if="pagination.page == 1" class="page-link" aria-label="Previous" href="#">
                                        <span aria-hidden="true">&laquo;</span>
                                        <span class="sr-only">Previous</span>
                                    </a>
                                    <a v-else @click="requestList(pagination.page-1, pagination.perPage)" class="page-link" aria-label="Previous" href="#">
                                        <span aria-hidden="true">&laquo;</span>
                                        <span class="sr-only">Previous</span>
                                    </a>
                                </li>

                                <!-- v-for, v-if, v-else             :key가 의미하는 것 : v-bind           :class속성(active가 true라면)-->
                                <li v-for="item in pagination.pages" :key="item.pageNo" class="page-item" :class="{ 'active' : item.pageActive }">
                                    <a v-if="item.pageActive" class="page-link" href="#">{{ item.pageNo }}</a>
                                    <a v-else @click="requestList(item.pageNo, pagination.perPage)" class="page-link" href="#">{{ item.pageNo }}</a>
                                </li>

                                <li class="page-item" :class="{ 'disabled' : pagination.page >= pagination.pageCount }">
                                    <a v-if="pagination.page < pagination.pageCount" @click="requestList(pagination.page + 1, pagination.perPage)" class="page-link" aria-label="Next" href="#">
                                        <span aria-hidden="true">&raquo;</span>
                                        <span class="sr-only">Next</span>
                                    </a>
                                    <a v-else class="page-link" aria-label="Next" href="#">
                                        <span aria-hidden="true">&raquo;</span>
                                        <span class="sr-only">Next</span>
                                    </a>
                                </li>
                                <li class="page-item" :class="{ 'disabled' : pagination.page >= pagination.pageCount }">
                                    <a v-if="pagination.page < pagination.pageCount" @click="requestList(pagination.pageCount, pagination.perPage)" class="page-link" href="#">Last</a>
                                    <a v-else class="page-link" href="#">Last</a>
                                </li>
                            </ul>
                        </nav>

                    </div>

                    <div class="row" style="margin-top: 1em;">
                        <div class="col-sm-8">

                        </div>
                        <div class="col-sm-4 text-right">
                            <button type="button" class="btn btn-outline-primary waves-effect" @click="showAddPage()">추가하기</button>
                        </div>
                    </div>
                </div>
            </div>

        </div>


  </div>
</template>

<script>

export default {
  name: 'Home',
  components: {

  },
  data: () => ({
      items: [],
      pagination: { //데이터 넣어줌
          page:0,
          perPage:0,
          total:0,
          pages:[],
          pageCount:0
      }
  }),
  methods : {
     requestList(page, perPage) {
       console.log(`requestList 함수 호출됨 : ${page}, ${perPage}`)

        this.axios({
            method: 'get',
            url: `http://localhost:8001/customer?page=${page}&perPage=${perPage}`,
            responseType: 'json'
        }).then((response) => {
            console.log(`응답 : ${JSON.stringify(response.data)}`)

            this.items = response.data.output.body

            this.setPagination(response.data.output.header)

        }).catch((err) => {
            console.log(`에러 : ${err}`)
        })
     },
     //원래 제이쿼리 해줬던 부분을 다 없애고 setPagination 이용함 --> 코드 양이 더 줄음
     setPagination(header) { //테이블을 만드는 데 필요한 데이터만 계산해준다(?)
        console.log(`setPagination 호출됨 : ${JSON.stringify(header)}`)

        let curPage = Number(header.page)
        let perPage = Number(header.perPage)
        let totalRecords = header.total
        console.log(`header : ${curPage}, ${perPage}, ${totalRecords}`)

        let pageCount = Math.ceil(totalRecords / perPage)
        console.log(`pageCount : ${pageCount}`)

        // 한 번에 보여줄 페이지 개수
        let unitPage = 10

        // 시작 페이지의 인덱스
        let startPageIndex = Math.floor((curPage-1)/unitPage) * unitPage

        let endPage = pageCount - startPageIndex
        if (endPage > unitPage) {
            endPage = startPageIndex + unitPage
        } else {
            endPage = startPageIndex + endPage
        }
        console.log(`시작 페이지 인덱스와 끝 페이지 : ${startPageIndex}, ${endPage}`)

        const pages = []
        for (let i = startPageIndex; i < endPage; i++) {
            if ((i+1) == curPage) {
                pages.push({
                    pageNo: i+1,
                    pageActive: true
                })
            } else {
                pages.push({
                    pageNo: i+1,
                    pageActive: false
                })
            }
        }

        //pagination에 데이터를 넣어줌
        this.pagination.page = curPage
        this.pagination.perPage = perPage
        this.pagination.total = totalRecords

        this.pagination.pages = pages
        this.pagination.pageCount = pageCount
     },
     showAddPage() {
         this.$router.push('/CustomerAdd')
     },
     showModifyPage(index) {
        console.log(`showModifyPage 호출됨 : ${index}`)

        const params = this.items[index]
        this.$router.push({name:'CustomerAdd', params:params})
     },
     deleteItem(index) {
         console.log(`deleteItem 호출됨 : ${index}`) //몇 번째 인덱스가 넘어오게 할지 적어줘야함

         this.axios({
            method:'delete',
            url:`http://localhost:8001/customer/${this.items[index].id}`, //여기에 요청파라미터를 ?붙이는 게 안돼서 data를 따로 넣어야 함
            data: {

            },
            responseType:'json'
        }).then((response) => {
            console.log(`응답 : ${JSON.stringify(response.data)}`)

            // 고객목록 새로고침
            let page = this.pagination.page

            if (this.items.length == 1) {
                page -= 1
            }

            if (page == 0) {
                page = 1
            }

            const perPage = 3
            this.requestList(page, perPage) //화면이 바뀔 필요는 없어서 데이터만 바꿔지게 함

        }).catch((err) => {
            console.log(`에러 : ${err}`)
        })
     }
  },
  mounted() { // 제이쿼리의 ready랑 같은역할
    console.log('mounted 함수 호출됨')

    const page = 1
    const perPage = 3

    this.requestList(page, perPage)
  }

}
</script>

src > views > CustomerAdd.vue

<template>
  <div class="about">
    <h1>고객추가 페이지입니다</h1>
      <div class="container-fluid">
            <div class="card" style="margin: 3em;">
                <div class="card-header">
                    <div class="font-weight-bold">고객 추가</div>
                </div>

                <div class="card-body">
                    <form class="text-center" style="color:#888888; padding: 4em;" action="#">
                        <div class="md-form form-group mt-5">
                            <input type="hidden" class="form-control" id="idInput">
                        </div>

                        <div class="md-form form-group mt-5">
                            <input ref="nameInput" v-model="item.name" type="text" class="form-control" id="nameInput" placeholder="">
                            <label for="nameInput">이름</label>
                        </div>

                        <div class="md-form form-group mt-5">
                            <input ref="ageInput" v-model.number="item.age" type="text" class="form-control" id="ageInput" placeholder="">
                            <label for="ageInput">나이</label>
                        </div>

                        <div class="md-form form-group mt-5">
                            <input ref="mobileInput" v-model="item.mobile" type="text" class="form-control" id="mobileInput" placeholder="">
                            <label for="mobileInput">전화번호</label>
                        </div>
                    </form>

                    <div class="row" style="margin-top: 3em;">
                        <div class="col-sm-1">

                        </div>
                        <div class="col-sm-5">
                            <button class="btn btn-outline-primary waves-effect" @click="saveItem()">저장</button>
                        </div>
                        <div class="col-sm-5">
                            <button class="btn btn-outline-primary waves-effect" @click="goBack()">닫기</button>
                        </div>
                        <div calss="col-sm-1">

                        </div>
                    </div>
                </div>
            </div>
        </div>

  </div>
</template>

<script>
export default { //객체(붕어빵) 넣어주기
    name:'CustomerAdd',
    data: () => ({
        mode:'',
        item: {
            id:0,   //비어있는 것도 값이 있는 것임 (0이나 ''대신에 undefined로 적어도 됨)
            name:'',
            age:0,
            mobile:''
        }
    }),
    methods: {
        saveItem() {
            console.log('CustomerAdd 안에서 saveItem 함수 호출됨.')

            //이제 mode로 구분할 것임
            if (this.mode == 'modify') {
                this.requestModify()
            } else {
                this.requestAdd()
            }

        },
        requestAdd() {
            console.log('requestAdd 함수 호출됨')

            this.axios({
                method:'post',
                url:'http://localhost:8001/customer',  //여기에 요청파라미터를 ?붙이는 게 안돼서 data를 따로 넣어야 함
                data: {
                    name: this.item.name,
                    age: this.item.age,
                    mobile: this.item.mobile
                },
                responseType:'json'
            }).then((response) => {
                console.log(`응답 : ${JSON.stringify(response.data)}`)

                // 고객목록 화면으로 이동
                this.$router.push('/')

            }).catch((err) => {
                console.log(`에러 : ${err}`)
            })
        },
        requestModify() {
            console.log('requestModify 함수 호출됨')

            this.axios({
                method:'put',
                url:`http://localhost:8001/customer/${this.item.id}`,
                data: {
                    name: this.item.name,
                    age: this.item.age,
                    mobile: this.item.mobile
                },
                responseType:'json'
            }).then((response) => {
                console.log(`응답 : ${JSON.stringify(response.data)}`)

                // 고객목록 화면으로 이동
                this.$router.push('/')

            }).catch((err) => {
                console.log(`에러 : ${err}`)
            })
        },
        goBack() {
            this.$router.push('/') //이 경로로 이동할 수 있다
        }
    },
    mounted() {
        console.log(`CustomerAdd의 mounted 함수 호출됨 : ${JSON.stringify(this.$route.params)}`)

        if (typeof(this.$route.params) == 'undefined') { //params가 있는지 없는지 변수 크기로 확인해줌
            this.mode = 'add'
        } else {
            this.mode = 'modify'
            this.item = this.$route.params //한 사람에 대한 고객정보를 넣어줌
        }

        this.$refs.nameInput.focus()
        this.$refs.ageInput.focus()
        this.$refs.mobileInput.focus()
    }
}
</script>