차트, 잘못된 접근 차단, 검색, 검색어 풀림, 메뉴 활성화, 삭제, 수정

keep_going·2022년 12월 29일
0

vue

목록 보기
9/13

조회(select) => await axios.get(url, {headers});
추가(insert) => await axios.post(url, body, {headers});
수정(update) => await axios.put(url, body, {headers});
삭제(delete) => await axios.delete(url, {headers:headers, data:body});


** 게시판 글쓰기 => http://1.234.5.158:23000/board101/insertimage.json  => {title:'', content:'b', writer:'c', image:'이미지'}
** 게시판 목록 => http://1.234.5.158:23000/board101/selectlistimage.json?page=페이지번호&text=검색어 => 기본값 1, ''
** 게시판 상세 => http://1.234.5.158:23000/board101/selectoneimage.json?no=글번호

** 답글 조회 => http://1.234.5.158:23000/board101/selectreply.json?brdno=게시글번호
** 답글 작성 => http://1.234.5.158:23000/board101/insertreply.json  => { brdno:게시글번호, content:답글내용, writer:'답글작성자' }
** 답글 삭제 => http://1.234.5.158:23000/board101/deletereply.json?no=답글번호

** 조회수증가 => http://1.234.5.158:23000/board101/updatehit.json?no=글번호
**수정 => http://1.234.5.158:23000/board101/update.json?no=글번호 => {title:'a', content:'b', writer:'c'}
**삭제 => http://1.234.5.158:23000/board101/delete.json?no=글번호

// 파일명 :routes/index.js_수정: 게시글목록에 쿼리 없이 접근했을때도 쿼리 있는 형태로 바꿔주기 위해서


router.beforeEach((to, from, next)=>{
    console.log(to, from);
	// 로그인, 로그아웃 상황 구분해주기 위해 만들어 뒀던것
    if(to.path !== '/login' && to.path !== '/logout')
        sessionStorage.setItem("CURRENT_PATH", to.path);
        sessionStorage.setItem("QUERY", JSON.stringify(to.query));
  
        if(to.path === '/order' || to.path === "/test" ) { 
            const logged = store.getters.getLogged;
            console.log(logged);
            if(logged === false) { 
                next({path:'/login'}); /
                return; /
            }
        }

        // /boardselect  => /boardselect?page=1&text= (변경해야 함)
        // /boardselect?page=2&text=a  (변경하면 안됨)            
        console.log(to.query);
        if((to.path === '/boardselect') && (Object.keys(to.query).length===0)) {
            next({path:'/boardselect', query:{page:1, text:''}});
            return;
        }
  		// if문 안에 들어간 이유는 현재 로그인, 로그아웃 창으로 이동하는 상황이 아니기 때문에 if문 돌아가게 되어있음
        // 이 상황에서 if문 바깥에 짜면 안에서 return되는 상황이라 boardselect로 보낼 수가 없다!

    }

    next(); // () 기존에 이동하고자하는 페이지로 이동
});


// 파일명: BoardSelect.vue_수정: 게시판 검색 기능 추가

<template>
        <div style="text-align: left;">
            <input type="text" @keyup.enter="handleSearch()" 
                v-model="state.text" placeholder="검색어 입력" />
        </div>
        
<script>
export default {
    setup () {
        // 상태 변수, f5를 누르면 초기화 된다-------------
        const state = reactive({
            page : Number(route.query.page), // 1
            text : String(route.query.text), // treim이 문자로 인식하게 하기 위해서 강제로 형태 부여
        });
        //-------------------------------------------

        // 페이지 네이션 ---------------------------
        const handlePage = (tmp) => {
            state.page = tmp;
            router.push({path:'/boardselect',query:{page:tmp, text:state.text}});

            handleData();
        };
        //-----------------------------------------

        // 새로고침 (F5)누를 때 호출됨, state변수 보다 뒤에 실행됨.
        onMounted(() => {
            handleData();
        });
      	// if(typeof(page) ==='undefined' && typeof(text) ==='undefined') {router.push({path:'/boardselect', query:{page:1, text:''}});}
        // 이렇게 처리하니까 state변수 값을 먼저 읽어서 status가 -1  뜨고 한번 더 새로고침 해야 값을 불러옴
        // 해결-> menu에서 index 주소 자체에 query 붙은걸로 바꿔줌 -> 문제: 글 등록하고 나서 게시글 목록으로 router.push하는 과정에서 -1이뜸 
        // push하는 주소에 ?page=1&text= 붙여도 해결 안됨
        // 해결-> routes에서 if문 처리 해서 query 없이 접근했을때는 query를 부여함 

        // 검색어 처리
        const handleSearch = () => {
            // 문자열.trim() => 앞 뒤의 공백 제거
            state.text = state.text.trim();
            state.page = 1; // 검색하면 1페이지로 이동
            router.push({path:'/boardselect', query:{page:1, text:state.text}});
            handleData();
        };

        return {
            handleContent,
            handleSearch,
        };
    }
}
</script>

// 파일명: BoardContetntPage.vue_수정: 게시글 수정(v-show이용), 삭제

<template>
    <div>
        <div v-show="state.div === 1" v-if="state.row">
        // 평상시 보이는 화면, 답글도 포함 시켜야 함
            <p>글번호: {{ state.row._id }}</p>
            <p>제목: {{ state.row.title }}</p>
            <p>내용: {{ state.row.content }}</p>
            <p>이미지: <img :src="state.row.img" style="height: 400px;"/></p>
            <p>작성자: {{ state.row.writer }}</p>
            <p>조회수: {{ state.row.hit }}</p>
            <p>등록일: {{ state.row.regdate }}</p>
            <button @click="state.div = 2">수정</button>
            <button @click="handleDelete()">삭제</button>
            <button @click="handlePrev()">이전글</button>
            <button @click="handleNext()">다음글</button>
            <hr />

            <table border="1">
                <tr v-for="tmp of state.reply" :key="tmp">
                    <td>{{ tmp._id }}</td>
                    <td>{{ tmp.content }}</td>
                    <td>{{ tmp.writer }}</td>
                    <td>{{ tmp.regdate }}</td>
                    <td><button @click="handleReplyDelete(tmp._id)">삭제</button></td> 
                    // 답글이 여러개이면 삭제하는 버튼도 여러개이므로 어떤 답글을 삭제할지 parmeter필요
                    <hr />
                </tr>
            </table>
            <input type="text" v-model="state.recontent" placeholder="답글쓰기" />
            <input type="text" v-model="state.rewriter" placeholder="답글작성자" />
            <button @click="handleReplyInsert()">답글 쓰기</button>
        </div>

        <div v-show="state.div === 2" v-if="state.row1">
        // 수정시 보이는 화면
            <p>작성자: <input type="text" v-model="state.row1.writer" /></p>
            <p>제목: <input type="text" v-model="state.row1.title" /></p>
            <p>내용: <textarea v-model="state.row1.content"></textarea></p>
            <button @click="handleUpdate()">수정완료</button>
            <button @click="state.div = 1">취소</button>
        </div>
       
    </div>
</template>

<script>
export default {
    setup () {
        const state = reactive({
            no      : Number(route.query.no), // 현재 글번호
        
            row1    : null, // 수정시 사용할 게시글 1개 정보
            div     : 1, // 기본으로 보여주는 화면, 수정시 2로 보이게함

            row     : null, // 게시글 1개정보
            prev    : 0, // 이전글
            next    : 0, // 다음글

            reply    : [], // 답글 목록
            recontent : '', // 답글 내용
            rewriter: '', // 답글 작성자
            // 답글 내용과 답글 작성자는 v-model과 연결하기 위해 변수 설정이 필요. 답글 번호나 작성일자는 작성하는게 아니므로 변수 설정할 필요 없다!
            // 필요한 답글 정보는 reply가 배열 형태로 이미 다 받아옴
        });

       const handleDelete = async() => {
            if(confirm('삭제할까요?')) { // 삭제 전 확인하는거 까먹지마!
                const url = `/board101/delete.json?no=${state.no}`;
                const headers = {"Content-Type":"application/json"};
                const body = {};
                const { data } = await axios.delete(url, {headers:headers, data:body});
                console.log('handleDelete', data);
                if(data.status === 200) {
                    router.push({path:'/boardselect'});
                }
            }
        };
      
      
       const handleUpdate = async() => {
          const url =`/board101/update.json?no=${state.no}`;
          const headers = {"Content-Type":"application/json"};
          const body = { // row1의 값을 ''로 받으니까 밑줄생기네. null로 하니 됨
            title : state.row1.title, 
            content : state.row1.content,
            writer : state.row1.writer,
          }
          const { data } = await axios.put(url, body, {headers});
          console.log('handleUpdate', data);
          if(data.status === 200) {
            handleData();
            state.div = 1;
          }
      };

       const handleReplyDelete = async(reno) => {
          if(confirm('삭제할까요?')) {
            const url = `/board101/deletereply.json?no=${reno}`;
            const headers = {"Content-Type":"application/json"};
            const body = {};

            const { data } = await axios.delete(url, {headers:headers, data:body});
            console.log('handleReplyDelete', data);
            if(data.status === 200) {
              handleData1();
            }
          }
       };

       return {
            handleReplyDelete,
            handleUpdate,
            handleDelete,
       };
    }
}
</script>

====================================
//파일명: ChartPage.vue

  • 차트 연습 하기 위해 라이브러리 설치
  • npm install chart.js@2
  • 공식 홈페이지는 구글에서 chart.js 검색
<template>
    <div>
        <h3>chart</h3>
        <div class="box">
            
            <div class="item">
                <button @click="handleChartData2_1(), handleChartData2_2()">차트데이터변경</button>
				// 이렇게 하면 버튼 한번 클릭에 함수 2개가 출력됨. 약간의 시간차가 있긴 하지만 거의 동시에...
				// parameter를 부여하면 하나씩 실행될까? 고민해보기
                <div style="height: 300px;">
                    <canvas id="myChart2" width="100%" height="100%"></canvas>
                </div>
            </div>

            <div class="item">
                <button @click="handleChartData()">차트데이터변경</button>
                <div style= "height: 300px;">
                    <canvas id="myChart" width="100%" height="100%"></canvas>
                </div>
            </div>

            <div class="item">
                <button @click="handleChartData1()">차트데이터변경</button>
                <div style="height: 300px;">
                    <canvas id="myChart1" width="100%" height="100%"></canvas>
                </div>
            </div>

            <div class="item">
                <button @click="handleChartData3()">차트데이터변경</button>
                <div style="height: 300px;">
                    <canvas id="myChart3" width="100%" height="100%"></canvas>
                </div>
            </div>

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

<script>
import Chart from 'chart.js'// 수동으로 라이브러리 임포트시킴
import { onMounted, reactive } from '@vue/runtime-core';
import axios from 'axios';
export default {
    setup () {

        // 공통변수. 함수간에 사용하거나, template에 사용하거나
        const state = reactive({
            chart : '',
            chart1: '',
            chart2: '',
            chart3: '',
        });

        // 차트의 종류, 데이터, 색상 등 설정...
        const config = {
            type: 'bar',
            data: { // 데이터 구조 정확히 유지해야한다. key를 바꾼다거나 대소문자나... 임의로 변경하면 안됨!
                labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
                datasets: [ // 배열형태
                    {
                        label: '# of Votes',
                        data: [12, 19, 3, 5, 2, 3],
                        borderWidth: 1,
                        backgroundColor : [ // rgb 색상표 검색해서 rgb값 참조
                            'rgba(255, 99, 132, 0.2)',
                            'rgba(47, 186, 255, 0.2)',
                            'rgba(255, 247, 0, 0.2)',
                            'rgba(63, 249, 57, 0.2)',
                            'rgba(205, 57, 249, 0.2)',
                            'rgba(255, 205, 0, 0.2)',
                        ]
                    } 
                ]
            },
            options: {
                scales: {
                    y: {
                        beginAtZero: true
                    }
                }
            }
        };

        const config1 = {
            type : 'line',
            data : {
                labels : ['1', '2', '3', '4',' 5'],
                datasets: [
                    {
                        label:'라인테스트',
                        data:[34, 123, 26, 457, 63],
                    }
                ]
            }
        };

        const config2 = {
            type : 'bar',
            data : {
                labels : [],
                datasets: [
                    {
                        label:'',
                        data:[],
                        borderWidth:2,
                    }
                ]
            }
        };

        const config3 = {
            type : 'line',
            data : {
                labels : [],
                datasets: [
                    {
                        label:'시간대별 게시글 수',
                        data:[],
                    }
                ]
            }
        };

        const initChart = () => {
            const ctx = document.getElementById('myChart').getContext('2d'); // js부분. 찾아서
            state.chart = new Chart(ctx, config); // 이게 있어서 차트 그려진것, 원래는 앞에 const chart = 있는건데 생략하고 new Chart(ctx, config)로 했다가
            // chart 데이터 변경함수에서 chart 사용하기 위해 공통변수로 만듬-> state.chart로 담는 순간 오류 발생...
            // 해결-> 담는게 문제가 아니라 위애 {{state}}로 출력하는것 때문에 오류 난것. 객체도아니고 스트링도아니고..그래서 오류났나봐

            const ctx1 = document.getElementById('myChart1').getContext('2d');
            state.chart1 = new Chart(ctx1, config1);

            const ctx2 = document.getElementById('myChart2').getContext('2d');
            state.chart2 = new Chart(ctx2, config2);

            const ctx3 = document.getElementById('myChart3').getContext('2d');
            state.chart3 = new Chart(ctx3, config3);

        };

        onMounted(() => {
            initChart();
            handleChartData2();
            handleChartData3();
        });

        const handleChartData = () => {
            // chart 데이터 변경
            state.chart.data.datasets[0].data = [12, 34, 97, 36, 22, 73];
            state.chart.data.labels = ['a', 'b', 'c', 'd', 'e', 'f']
            state.chart.update();
        };

        const handleChartData1 = () => {
            state.chart1.data.datasets[0].data = [22, 663, 1, 123, 62, 163];
            state.chart1.data.labels = ['a', 'b', 'c', 'd', 'e', 'f']
            state.chart1.update();
        };

        const handleChartData2 = async() => {
            const url = `/member101/agg.json`;
            const headers = {"Content-Type":"application/json"};

            const { data } = await axios.get(url, {headers});
            console.log('연령대', data);
            if(data.status === 200) { // 변수선언 따로 안하고 위에 있는 변수 그대로 안에 넣으니까 변수명이 너무 길다. 그래서 따로 선언해주기
                let tmpLabel = [];
                let tmpData = [];
                
                for(let i = 0; i< data.result.length; i++) {
                    tmpLabel.push(data.result[i]._id);
                    tmpData.push(data.result[i].count);
                }

                state.chart2.data.labels = tmpLabel;
                state.chart2.data.datasets[0].data = tmpData;
                state.chart2.data.datasets[0].label = '연령대별 가입자 수';
                state.chart2.update();
            }
        };

        const handleChartData2_1 = async() => {
            const url = `/member101/agg.json`;
            const headers = {"Content-Type":"application/json"};

            const { data } = await axios.get(url, {headers});
            console.log('연령대', data);
            if(data.status === 200) { 
                let tmpLabel = [];
                let tmpData1 = [];

                for(let i = 0; i< data.result.length; i++) {
                    tmpLabel.push(data.result[i]._id);
                    tmpData1.push(data.result[i].sum);
                }

                state.chart2.data.labels = tmpLabel;
                state.chart2.data.datasets[0].data = tmpData1;
                state.chart2.data.datasets[0].label = '연령대별 가입자 연령 합';

                state.chart2.update();
            }
        };

        const handleChartData2_2 = async() => {
            const url = `/member101/agg.json`;
            const headers = {"Content-Type":"application/json"};

            const { data } = await axios.get(url, {headers});
            console.log('연령대', data);
            if(data.status === 200) { 
                let tmpLabel = [];
                let tmpData2 = [];
                
                for(let i = 0; i< data.result.length; i++) {
                    tmpLabel.push(data.result[i]._id);
                    tmpData2.push(data.result[i].avg);
                }

                state.chart2.data.labels = tmpLabel;
                state.chart2.data.datasets[0].data = tmpData2;
                state.chart2.data.datasets[0].label = '연령대별 가입자 연령 평균';

                state.chart2.update();
            }
        };
        
        const handleChartData3 = async() => {
            const url = `/board101/agg.json`;
            const headers = {"Content-Type":"application/json"};

            const { data } = await axios.get(url, {headers});
            console.log('시간대', data);
            if(data.status === 200) { 
                let tmpLabel = [];
                let tmpData = [];

                
                for(let i = 0; i< data.result.length; i++) {
                    tmpLabel.push(data.result[i]._id);
                    tmpData.push(data.result[i].count);
                }

                state.chart3.data.labels = tmpLabel;
                state.chart3.data.datasets[0].data = tmpData;

                state.chart3.update();
            }
        };

        

        return {
            state,
            handleChartData,
            handleChartData1,
            handleChartData2,
            handleChartData2_1,
            handleChartData2_2,
        }
    }
}
</script>

<style lang="css" scoped>
    .box {
        display: grid;
        grid-template-columns:  1fr 1fr 1fr;
        grid-auto-rows: minmax(400px, auto);
    }

    .item {
        padding: 5px;
        border: 1px solid #cccccc;
    }
</style>

// 파일명: BoardInsertPage.vue_수정: 글쓰기 이후 메뉴활성화를 게시글 목록으로 보내기

if(data.status === 200) {
  store.commit('setDefaultActive', '/boardselect1');
  // 글쓰기 이후 메뉴 활성화를 게시글목록으로 보내기 위함
  // 로그인, 아웃의 경우 이전페이지가 어디일지 모르고 그 이전페이지로 돌아가야 했기 때문에 route를 이용해서 storage에 값을 보관하고 받고 해야했지만
  // 지금 경우에는 게시글 작성후 무조건! 게시글 목록으로 가게 되있음
  // route 이용할 필요 없고 그냥 값을 지정해주면 된다.
  router.push({path:'/boardselect'});
}

// 파일명: Menu.vue_수정: 게시판 검색 후 게시글 목록 눌렀을때 검색어 설정 풀리도록

<el-menu-item index="/boardselect1">게시판목록</el-menu-item>
//게시판 검색 이후 게시판 목록 눌렀을때 검색어가 풀린 상태로 해주기 위해서 
//이동만 시키는 페이지(boardselect1)을 만들었고 게시판 목록을 누르면 무조건 이동만 시키는 페이지로 가도록 함. 
//이렇게 하면 어떤 경우라도 이동용페이지->게시판목록을 받아오는거기때문에 딜레이는 좀 있지만... 새로고침한 효과를 받을수 있다

// 파일명: BoardSelectPage1.vue

<template>
    <div>
        게시판목록 이동용 페이지
    </div>
</template>

<script>
import { useRouter } from 'vue-router';


export default {
    setup () {
        const router = useRouter();
        router.push({path:'/boardselect'});

        return {}
    }
}
</script>

<style lang="scss" scoped>

</style>
profile
keep going

3개의 댓글

comment-user-thumbnail
2022년 12월 29일

문제) 게시글 검색 후 다시 게시판 목록을 눌렀을때도 검색 상태가 유지됨(BoardSelectPage.vue)

해결) 게시판 검색 이후 게시판 목록 눌렀을때 검색어가 풀린 상태로 해주기 위해서 이동만 시키는 페이지(boardselect1)를 만들었고 게시판 목록을 누르면 무조건 이동만 시키는 페이지로 가도록 함. 이렇게 하면 어떤 경우라도 이동용페이지->게시판목록을 받아오는거기때문에 딜레이는 좀 있겠지만...(체감상 없음) 새로고침 한 효과를 받을수 있다 (BoardSelectPage1.vue, MenuPage.vue)

답글 달기
comment-user-thumbnail
2022년 12월 29일

문제 ) 게시판 글쓰기 이후 게시글 목록으로 이동되지만 메뉴 활성화는 게시판 글쓰기에 그대로 고정되어 있음(BoardInsertPage.vue)

해결) 로그인, 아웃의 경우 이전페이지가 어디일지 모르고 그 이전페이지로 돌아가야 했기 때문에 route를 이용해서 storage에 값을 보관하고 받고 해야했지만 지금 경우에는 게시글 작성후 무조건! 게시글 목록으로 가게 되있으므로 route 이용할 필요 없고 그냥 값을 지정해주면 된다.

답글 달기
comment-user-thumbnail
2022년 12월 29일

router와 next의 차이점????

평가
글쓰기 목록, 검색, 페이지네이션, 수정, 삭제, 이전글, 다음글, 조회수 증가
수정: 제목 내용 작성자

답글 달기