Store_Vuex init...
This commit is contained in:
parent
5ff12cab9d
commit
845c3eb97d
7
.env
7
.env
@ -8,9 +8,4 @@ VUE_APP_AUTH_HOST="http://localhost:2000"
|
|||||||
VUE_APP_BACKEND_HOST="http://localhost:3000"
|
VUE_APP_BACKEND_HOST="http://localhost:3000"
|
||||||
|
|
||||||
#LocalStorage는 Edge 브라우저의 개발툴 > 응용프로그램 > 로컬 저장소에서 확인가능
|
#LocalStorage는 Edge 브라우저의 개발툴 > 응용프로그램 > 로컬 저장소에서 확인가능
|
||||||
VUE_APP_SESSIONSTORAGE_JWT_NAME="access_token"
|
VUE_APP_TABLE_DEFAULT_PERPAGE = 10
|
||||||
VUE_APP_SESSIONSTORAGE_REDIRECT_NAME = "RedirectPATH"
|
|
||||||
|
|
||||||
|
|
||||||
VUE_APP_TABLE_DEFAULT_PERPAGE = 10
|
|
||||||
VUE_APP_TABLE_DEFAULT_TABLE_PAGE = 1
|
|
||||||
@ -42,18 +42,16 @@
|
|||||||
</b-input-group>
|
</b-input-group>
|
||||||
</validation-provider>
|
</validation-provider>
|
||||||
<b-button type="submit" variant="primary">Login</b-button>
|
<b-button type="submit" variant="primary">Login</b-button>
|
||||||
<b-button type="reset" variant="danger" @click="onReset()"
|
|
||||||
>Reset</b-button
|
|
||||||
>
|
|
||||||
</b-form>
|
</b-form>
|
||||||
</validation-observer>
|
</validation-observer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
// 참고 : https://kdydesign.github.io/2019/04/06/vuejs-vuex-helper/
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
//console.log(this.$route)
|
||||||
return {
|
return {
|
||||||
form: {
|
form: {
|
||||||
email: null,
|
email: null,
|
||||||
@ -81,66 +79,21 @@ export default {
|
|||||||
getValidationState({ dirty, validated, valid = null }) {
|
getValidationState({ dirty, validated, valid = null }) {
|
||||||
return dirty || validated ? valid : null
|
return dirty || validated ? valid : null
|
||||||
},
|
},
|
||||||
async onSubmit() {
|
onSubmit() {
|
||||||
const result = await this.callAPI('/auth/login', {
|
this.$store
|
||||||
email: this.form.email,
|
.dispatch('AuthStore/login', {
|
||||||
password: this.form.password
|
email: this.form.email,
|
||||||
})
|
password: this.form.password
|
||||||
console.log(result)
|
})
|
||||||
if (!result) {
|
|
||||||
console.log(result)
|
|
||||||
sessionStorage.removeItem(process.env.VUE_APP_SESSIONSTORAGE_JWT_NAME)
|
|
||||||
alert('로그인 실패...')
|
|
||||||
//this.$router.back()
|
|
||||||
} else {
|
|
||||||
// localStorage를 사용하면, 브라우저에 key-value 값을 Storage에 저장할 수 있습니다
|
|
||||||
sessionStorage.setItem(
|
|
||||||
process.env.VUE_APP_SESSIONSTORAGE_JWT_NAME,
|
|
||||||
result
|
|
||||||
)
|
|
||||||
alert('로그인 성공...')
|
|
||||||
//저장된 redirect path를 이용 이동시킴
|
|
||||||
this.$router.replace(
|
|
||||||
sessionStorage.getItem(
|
|
||||||
process.env.VUE_APP_SESSIONSTORAGE_REDIRECT_NAME
|
|
||||||
) || '/'
|
|
||||||
)
|
|
||||||
//저장된 정보 CLear
|
|
||||||
this.onReset()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onReset() {
|
|
||||||
// Reset our form values
|
|
||||||
this.form.email = ''
|
|
||||||
this.form.pasword = ''
|
|
||||||
//저장된 redirect path Clear
|
|
||||||
sessionStorage.removeItem(
|
|
||||||
process.env.VUE_APP_SESSIONSTORAGE_REDIRECT_NAME
|
|
||||||
)
|
|
||||||
// Trick to reset/clear native browser form validation state
|
|
||||||
// this.$nextTick(() => {
|
|
||||||
// this.$refs.observer.reset()
|
|
||||||
// })
|
|
||||||
},
|
|
||||||
async callAPI(url, params) {
|
|
||||||
console.log('CallAPI..', [url, params])
|
|
||||||
axios.defaults.baseURL = process.env.VUE_APP_AUTH_HOST
|
|
||||||
//전송 Header에 추가
|
|
||||||
const headers = {
|
|
||||||
Authorization:
|
|
||||||
'Bearer ' +
|
|
||||||
sessionStorage.getItem(process.env.VUE_APP_SESSIONSTORAGE_JWT_NAME)
|
|
||||||
}
|
|
||||||
return await axios
|
|
||||||
.post(url, params, headers)
|
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
// console.log(response)
|
console.log(response)
|
||||||
return response.status === 201 ? response.data : null
|
const return_url = this.$route.params.return_url || 'home'
|
||||||
})
|
alert('로그인 성공... -> ' + return_url)
|
||||||
.catch((err) => {
|
this.$router.push({ name: return_url }).catch((e) => {
|
||||||
console.log(err)
|
console.log(e)
|
||||||
return null
|
})
|
||||||
})
|
})
|
||||||
|
.catch(({ message }) => alert('로그인 실패...:' + message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
104
src/components/common/TableComponent
Normal file
104
src/components/common/TableComponent
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 참조: https://codesandbox.io/s/3v0m1?file=/src/components/board/BoardList.vue -->
|
||||||
|
<b-table
|
||||||
|
ref="BTable"
|
||||||
|
:page="page"
|
||||||
|
:items="items"
|
||||||
|
:fields="fields"
|
||||||
|
:per-page="perPage"
|
||||||
|
:sort-by.sync="sortBy"
|
||||||
|
:sort-desc.sync="sortDesc"
|
||||||
|
@sort-changed="sortClick"
|
||||||
|
selectable
|
||||||
|
select-mode="multi"
|
||||||
|
@row-selected="selectedRowToggle"
|
||||||
|
:sticky-header="commonTableAttributes.stickyHeader"
|
||||||
|
:no-border-collapse="commonTableAttributes.noBorderCollapse"
|
||||||
|
:striped="commonTableAttributes.striped"
|
||||||
|
:hover="commonTableAttributes.hover"
|
||||||
|
:no-local-sorting="commonTableAttributes.noLocalSorting"
|
||||||
|
:bordered="commonTableAttributes.bordered"
|
||||||
|
:dark="commonTableAttributes.dark"
|
||||||
|
:head-variant="commonTableAttributes.headVariant"
|
||||||
|
>
|
||||||
|
<!-- Field별 Filter용 -->
|
||||||
|
<template #head(is_done)="fields">
|
||||||
|
<b-form-select
|
||||||
|
:value="filterIsDone"
|
||||||
|
:options="fields.field.Options"
|
||||||
|
@change="isDoneClick"
|
||||||
|
></b-form-select>
|
||||||
|
</template>
|
||||||
|
<!-- 비/선택 Row 토글용 -->
|
||||||
|
<template #cell(rowSelect)="{ rowSelected }">
|
||||||
|
<template v-if="rowSelected">✓</template>
|
||||||
|
</template>
|
||||||
|
<!-- Row의 Index 이용한 번호 출력용 -->
|
||||||
|
<template #cell(id)="row">
|
||||||
|
<b-link ref="edit/{{row.item.id}}">
|
||||||
|
{{ total - (page * perPage + row.index) + parseInt(perPage) }}
|
||||||
|
</b-link>
|
||||||
|
</template>
|
||||||
|
<!-- 제목부분 출력용 -->
|
||||||
|
<template #cell(title)="row">
|
||||||
|
{{ row.item.title }}
|
||||||
|
</template>
|
||||||
|
<!-- 상세 내용 출력용 -->
|
||||||
|
<template #cell(content)="row">
|
||||||
|
<b-button size="sm" @click="row.toggleDetails" class="mr-2">
|
||||||
|
[{{ row.item.id }}] {{ row.detailsShowing ? '숨기기' : '보기' }}
|
||||||
|
</b-button>
|
||||||
|
</template>
|
||||||
|
<template #row-details="row">
|
||||||
|
<b-card>
|
||||||
|
<b-row>
|
||||||
|
<b-col v-html="row.item.content"></b-col>
|
||||||
|
</b-row>
|
||||||
|
<b-button size="sm" @click="row.toggleDetails">숨기기</b-button>
|
||||||
|
</b-card>
|
||||||
|
</template>
|
||||||
|
<!-- 사용여부 출력용 -->
|
||||||
|
<template #cell(is_done)="row">
|
||||||
|
{{ row.value }}
|
||||||
|
</template>
|
||||||
|
</b-table>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'TableComponent',
|
||||||
|
props: [
|
||||||
|
'fields',
|
||||||
|
'items',
|
||||||
|
'total',
|
||||||
|
'page',
|
||||||
|
'perPage',
|
||||||
|
'selectedRows',
|
||||||
|
'filterIsDone'
|
||||||
|
],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
perPageOptions: [
|
||||||
|
{ text: '5줄', value: 5 },
|
||||||
|
{ text: '10줄', value: 10 },
|
||||||
|
{ text: '30줄', value: 30 },
|
||||||
|
{ text: '60줄', value: 60 },
|
||||||
|
{ text: '100줄', value: 100 }
|
||||||
|
],
|
||||||
|
// 속성옵션
|
||||||
|
commonTableAttributes: {
|
||||||
|
stickyHeader: '50%',
|
||||||
|
//noBorderCollapse: false,
|
||||||
|
striped: true,
|
||||||
|
hover: true,
|
||||||
|
noLocalSorting: true,
|
||||||
|
bordered: true,
|
||||||
|
dark: false,
|
||||||
|
headVariant: 'light'
|
||||||
|
},
|
||||||
|
sortBy: 'id',
|
||||||
|
sortDesc: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
||||||
87
src/components/common/TableHeaderComponent
Normal file
87
src/components/common/TableHeaderComponent
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<b-container fluid>
|
||||||
|
<b-row>
|
||||||
|
<b-col class="border">
|
||||||
|
<b-input-group>
|
||||||
|
<label class="input-group-text">
|
||||||
|
<b-form-checkbox @change="selectedRowAllToggle"
|
||||||
|
>All</b-form-checkbox
|
||||||
|
>
|
||||||
|
</label>
|
||||||
|
</b-input-group>
|
||||||
|
</b-col>
|
||||||
|
<b-col cols="4" class="border">
|
||||||
|
<b-input-group>
|
||||||
|
<label class="input-group-text">
|
||||||
|
<b-form-checkbox-group
|
||||||
|
class="d-flex flex-nowrap"
|
||||||
|
v-model="searchFields"
|
||||||
|
:options="searchFieldOptions"
|
||||||
|
></b-form-checkbox-group>
|
||||||
|
</label>
|
||||||
|
<b-form-input
|
||||||
|
v-model="search"
|
||||||
|
type="text"
|
||||||
|
placeholder="검색어"
|
||||||
|
@keydown.enter="searchClick"
|
||||||
|
></b-form-input>
|
||||||
|
<b-button @click="searchClick">검색</b-button>
|
||||||
|
</b-input-group>
|
||||||
|
</b-col>
|
||||||
|
<b-col cols="4" class="border">
|
||||||
|
<b-input-group>
|
||||||
|
<b-form-select
|
||||||
|
class="d-flex flex-nowrap"
|
||||||
|
:value="filterDateField"
|
||||||
|
:options="filterDateFieldOptions"
|
||||||
|
></b-form-select>
|
||||||
|
<b-form-input :value="filterDateStart" type="date"></b-form-input>
|
||||||
|
~
|
||||||
|
<b-form-input :value="filterDateEnd" type="date"></b-form-input>
|
||||||
|
<b-button @click="dateClick">선택</b-button>
|
||||||
|
</b-input-group>
|
||||||
|
</b-col>
|
||||||
|
<b-col class="border">
|
||||||
|
<b-input-group class="justify-content-end">
|
||||||
|
<label class="input-group-text">줄수</label>
|
||||||
|
<b-form-select
|
||||||
|
:value="perPage"
|
||||||
|
:options="perPageOptions"
|
||||||
|
@change="perPageClick"
|
||||||
|
></b-form-select>
|
||||||
|
</b-input-group>
|
||||||
|
</b-col>
|
||||||
|
</b-row>
|
||||||
|
</b-container>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'TableHeaderComponent',
|
||||||
|
props: [
|
||||||
|
'searchFields',
|
||||||
|
'searchFieldOptions',
|
||||||
|
'filterDateField',
|
||||||
|
'filterDateFieldOptions',
|
||||||
|
'filterDateStart',
|
||||||
|
'filterDateEnd'
|
||||||
|
],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
perPageOptions: [
|
||||||
|
{ text: '5줄', value: 5 },
|
||||||
|
{ text: '10줄', value: 10 },
|
||||||
|
{ text: '30줄', value: 30 },
|
||||||
|
{ text: '60줄', value: 60 },
|
||||||
|
{ text: '100줄', value: 100 }
|
||||||
|
],
|
||||||
|
search: null,
|
||||||
|
filter: null,
|
||||||
|
filterField: null,
|
||||||
|
filterDateStart: null,
|
||||||
|
filterDateEnd: null,
|
||||||
|
selectedRowAll: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
||||||
20
src/components/common/TablePaginationComponent
Normal file
20
src/components/common/TablePaginationComponent
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<b-pagination
|
||||||
|
:total-rows="total"
|
||||||
|
:page="page"
|
||||||
|
:per-page="perPage"
|
||||||
|
@page-click="pageClick"
|
||||||
|
last-number
|
||||||
|
class="justify-content-center"
|
||||||
|
>
|
||||||
|
</b-pagination>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'TablePaginationComponent',
|
||||||
|
props: ['total', 'page', 'perPage', 'pageClick']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
||||||
@ -31,20 +31,51 @@
|
|||||||
<b-dropdown-item href="#">FA</b-dropdown-item>
|
<b-dropdown-item href="#">FA</b-dropdown-item>
|
||||||
</b-nav-item-dropdown> -->
|
</b-nav-item-dropdown> -->
|
||||||
|
|
||||||
<b-nav-item-dropdown right>
|
<template v-if="isAuthenticated">
|
||||||
<!-- Using 'button-content' slot -->
|
<b-nav-item-dropdown right>
|
||||||
<template #button-content>
|
<!-- Using 'button-content' slot -->
|
||||||
<em>User</em>
|
<template #button-content>
|
||||||
</template>
|
<em>User</em>
|
||||||
<b-dropdown-item href="#">Profile</b-dropdown-item>
|
</template>
|
||||||
<b-dropdown-item href="#">Sign Out</b-dropdown-item>
|
<b-dropdown-item href="/profile">Profile</b-dropdown-item>
|
||||||
</b-nav-item-dropdown>
|
<b-dropdown-item @click="logout()">Sign Out</b-dropdown-item>
|
||||||
|
</b-nav-item-dropdown>
|
||||||
|
</template>
|
||||||
|
<template v-else
|
||||||
|
><b-dropdown-item href="/login"
|
||||||
|
>Login {{ isAuthenticated }}</b-dropdown-item
|
||||||
|
>
|
||||||
|
</template>
|
||||||
</b-navbar-nav>
|
</b-navbar-nav>
|
||||||
</b-collapse>
|
</b-collapse>
|
||||||
</b-navbar>
|
</b-navbar>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { createNamespacedHelpers } from 'vuex'
|
||||||
|
const authStore = createNamespacedHelpers('AuthStore')
|
||||||
export default {
|
export default {
|
||||||
name: 'HeaderLayout'
|
name: 'HeaderLayout',
|
||||||
|
computed: {
|
||||||
|
...authStore.mapGetters(['isAuthenticated'])
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
redirect() {
|
||||||
|
if (!this.isAuthenticated && this.$router.name != 'home') {
|
||||||
|
this.$router
|
||||||
|
.push({
|
||||||
|
name: 'home'
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e + ':' + this.$router)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
logout() {
|
||||||
|
this.$store
|
||||||
|
.dispatch('AuthStore/logout', {})
|
||||||
|
.then(() => this.redirect())
|
||||||
|
.catch(({ message }) => alert(message))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,74 +1,58 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<validation-observer ref="observer" v-slot="{ handleSubmit }">
|
||||||
<b-modal
|
<b-form @submit.stop.prevent="handleSubmit(onSubmit)">
|
||||||
id="inputForm"
|
<validation-provider
|
||||||
centered
|
:name="field.title.label"
|
||||||
title="입력폼"
|
:rules="field.title.rules"
|
||||||
size="lg"
|
v-slot="validationContext"
|
||||||
:header-bg-variant="headerBgVariant"
|
>
|
||||||
:header-text-variant="headerTextVariant"
|
<b-input-group>
|
||||||
:body-bg-variant="bodyBgVariant"
|
<label class="input-group-text">{{ field.title.label }}</label>
|
||||||
:body-text-variant="bodyTextVariant"
|
<b-form-input
|
||||||
:footer-bg-variant="footerBgVariant"
|
:name="field.title.key"
|
||||||
:footer-text-variant="footerTextVariant"
|
:type="field.title.type"
|
||||||
>
|
v-model="form.title"
|
||||||
<validation-observer ref="observer" v-slot="{ handleSubmit }">
|
:state="getValidationState(validationContext)"
|
||||||
<b-form @submit.stop.prevent="handleSubmit(onSubmit)">
|
:style="field.title.style"
|
||||||
<validation-provider
|
></b-form-input>
|
||||||
:name="field.title.label"
|
<b-form-invalid-feedback>{{
|
||||||
:rules="field.title.rules"
|
validationContext.errors[0]
|
||||||
v-slot="validationContext"
|
}}</b-form-invalid-feedback>
|
||||||
>
|
</b-input-group>
|
||||||
<b-input-group>
|
</validation-provider>
|
||||||
<label class="input-group-text">{{ field.title.label }}</label>
|
<validation-provider
|
||||||
<b-form-input
|
:name="field.content.label"
|
||||||
:name="field.title.key"
|
:rules="field.content.rules"
|
||||||
:type="field.title.type"
|
v-slot="validationContext"
|
||||||
v-model="form.title"
|
>
|
||||||
:state="getValidationState(validationContext)"
|
<label class="input-group-text">{{ field.content.label }}</label>
|
||||||
:style="field.title.style"
|
<b-input-group>
|
||||||
></b-form-input>
|
<b-form-textarea
|
||||||
<b-form-invalid-feedback>{{
|
:config="field.content.config"
|
||||||
validationContext.errors[0]
|
:name="field.content.key"
|
||||||
}}</b-form-invalid-feedback>
|
v-model="form.content"
|
||||||
</b-input-group>
|
:state="getValidationState(validationContext)"
|
||||||
</validation-provider>
|
:style="field.content.style"
|
||||||
<validation-provider
|
rows="8"
|
||||||
:name="field.content.label"
|
></b-form-textarea>
|
||||||
:rules="field.content.rules"
|
<b-form-invalid-feedback>{{
|
||||||
v-slot="validationContext"
|
validationContext.errors[0]
|
||||||
>
|
}}</b-form-invalid-feedback>
|
||||||
<label class="input-group-text">{{ field.content.label }}</label>
|
</b-input-group>
|
||||||
<b-input-group>
|
</validation-provider>
|
||||||
<b-form-textarea
|
<div class="mt-300 border">
|
||||||
:config="field.content.config"
|
<b-button type="submit" variant="primary">입력</b-button>
|
||||||
:name="field.content.key"
|
<b-button type="reset" variant="danger" @click="onReset()"
|
||||||
v-model="form.content"
|
>Reset</b-button
|
||||||
:state="getValidationState(validationContext)"
|
>
|
||||||
:style="field.content.style"
|
</div>
|
||||||
rows="8"
|
</b-form>
|
||||||
></b-form-textarea>
|
</validation-observer>
|
||||||
<b-form-invalid-feedback>{{
|
|
||||||
validationContext.errors[0]
|
|
||||||
}}</b-form-invalid-feedback>
|
|
||||||
</b-input-group>
|
|
||||||
</validation-provider>
|
|
||||||
<div class="mt-300 border">
|
|
||||||
<b-button type="submit" variant="primary">입력</b-button>
|
|
||||||
<b-button type="reset" variant="danger" @click="onReset()"
|
|
||||||
>Reset</b-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</b-form>
|
|
||||||
</validation-observer>
|
|
||||||
</b-modal>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
//import { VueEditor } from 'vue2-editor/dist/vue2-editor.core.js'
|
//import { VueEditor } from 'vue2-editor/dist/vue2-editor.core.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {},
|
components: {},
|
||||||
data() {
|
data() {
|
||||||
@ -93,23 +77,7 @@ export default {
|
|||||||
rules: { required: true, min: 10 },
|
rules: { required: true, min: 10 },
|
||||||
style: 'height: 200px'
|
style: 'height: 200px'
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
variants: [
|
|
||||||
'primary',
|
|
||||||
'secondary',
|
|
||||||
'success',
|
|
||||||
'warning',
|
|
||||||
'danger',
|
|
||||||
'info',
|
|
||||||
'light',
|
|
||||||
'dark'
|
|
||||||
],
|
|
||||||
headerBgVariant: 'dark',
|
|
||||||
headerTextVariant: 'light',
|
|
||||||
bodyBgVariant: 'light',
|
|
||||||
bodyTextVariant: 'dark',
|
|
||||||
footerBgVariant: 'warning',
|
|
||||||
footerTextVariant: 'dark'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@ -60,10 +60,9 @@
|
|||||||
</b-row>
|
</b-row>
|
||||||
</b-container>
|
</b-container>
|
||||||
<b-table
|
<b-table
|
||||||
id="todoTable"
|
ref="BTable"
|
||||||
ref="todoTable"
|
|
||||||
:page="page"
|
:page="page"
|
||||||
:items="rows"
|
:items="getRows"
|
||||||
:fields="fields"
|
:fields="fields"
|
||||||
:per-page="perPage"
|
:per-page="perPage"
|
||||||
:sort-by.sync="sortBy"
|
:sort-by.sync="sortBy"
|
||||||
@ -75,7 +74,6 @@
|
|||||||
:select-mode="selectedRowMode"
|
:select-mode="selectedRowMode"
|
||||||
selectable
|
selectable
|
||||||
@row-selected="rowSelectedToggle"
|
@row-selected="rowSelectedToggle"
|
||||||
:busy="isBusy"
|
|
||||||
:sticky-header="commonTableAttributes.stickyHeader"
|
:sticky-header="commonTableAttributes.stickyHeader"
|
||||||
:no-border-collapse="commonTableAttributes.noBorderCollapse"
|
:no-border-collapse="commonTableAttributes.noBorderCollapse"
|
||||||
:striped="commonTableAttributes.striped"
|
:striped="commonTableAttributes.striped"
|
||||||
@ -95,13 +93,12 @@
|
|||||||
</template>
|
</template>
|
||||||
<!-- 비/선택 Row 토글용 -->
|
<!-- 비/선택 Row 토글용 -->
|
||||||
<template #cell(rowSelect)="{ rowSelected }">
|
<template #cell(rowSelect)="{ rowSelected }">
|
||||||
<template v-if="rowSelected"> ✓ </template>
|
<template v-if="rowSelected">✓</template>
|
||||||
<template v-else> </template>
|
|
||||||
</template>
|
</template>
|
||||||
<!-- Row의 Index 이용한 번호 출력용 -->
|
<!-- Row의 Index 이용한 번호 출력용 -->
|
||||||
<template #cell(id)="row">
|
<template #cell(id)="row">
|
||||||
<b-link ref="edit/{{row.item.id}}">
|
<b-link ref="edit/{{row.item.id}}">
|
||||||
{{ total - (page * perPage + row.index) + perPage }}
|
{{ getTotal - (page * perPage + row.index) + Number(perPage) }}
|
||||||
</b-link>
|
</b-link>
|
||||||
</template>
|
</template>
|
||||||
<!-- 제목부분 출력용 -->
|
<!-- 제목부분 출력용 -->
|
||||||
@ -126,13 +123,6 @@
|
|||||||
<template #cell(is_done)="row">
|
<template #cell(is_done)="row">
|
||||||
{{ row.value }}
|
{{ row.value }}
|
||||||
</template>
|
</template>
|
||||||
<!-- 데이터 로딩중 출력용 -->
|
|
||||||
<template #table-busy>
|
|
||||||
<div class="text-center text-danger my-2">
|
|
||||||
<b-spinner class="align-middle"></b-spinner>
|
|
||||||
<strong>Loading...</strong>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</b-table>
|
</b-table>
|
||||||
<b-container fluid>
|
<b-container fluid>
|
||||||
<b-row>
|
<b-row>
|
||||||
@ -142,32 +132,33 @@
|
|||||||
<b-col>
|
<b-col>
|
||||||
<b-pagination
|
<b-pagination
|
||||||
:page="page"
|
:page="page"
|
||||||
:total-rows="total"
|
:total-rows="getTotal"
|
||||||
:per-page="perPage"
|
:per-page="perPage"
|
||||||
@page-click="pageClick"
|
@page-click="pageClick"
|
||||||
last-number
|
last-number
|
||||||
aria-controls="todoTable"
|
|
||||||
class="justify-content-center"
|
class="justify-content-center"
|
||||||
>
|
>
|
||||||
</b-pagination>
|
</b-pagination>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col>
|
<b-col> </b-col>
|
||||||
<b-input-group class="justify-content-end">
|
|
||||||
<b-button @click="busyToggle">Toggle Busy State</b-button>
|
|
||||||
</b-input-group>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
</b-row>
|
||||||
</b-container>
|
</b-container>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { createNamespacedHelpers } from 'vuex'
|
||||||
|
const todoStore = createNamespacedHelpers('TodoStore')
|
||||||
// 참조 : https://vuejsexamples.com/vuejs-tables-and-select-all-checkbox/
|
// 참조 : https://vuejsexamples.com/vuejs-tables-and-select-all-checkbox/
|
||||||
import axios from 'axios'
|
|
||||||
export default {
|
export default {
|
||||||
components: {},
|
components: {},
|
||||||
|
computed: {
|
||||||
|
...todoStore.mapGetters(['getTotal', 'getRows'])
|
||||||
|
},
|
||||||
|
created: function () {
|
||||||
|
this.setDatas()
|
||||||
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
rows: [],
|
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
key: 'rowSelect',
|
key: 'rowSelect',
|
||||||
@ -221,8 +212,7 @@ export default {
|
|||||||
sortByFormatted: true //fomatter결과에따른 Sort가 필요시 true
|
sortByFormatted: true //fomatter결과에따른 Sort가 필요시 true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
total: 0,
|
page: 1, // 현재 페이지
|
||||||
page: process.env.VUE_APP_TABLE_DEFAULT_PAGE, // 현재 페이지
|
|
||||||
perPage: process.env.VUE_APP_TABLE_DEFAULT_PERPAGE, // 페이지당 보여줄 갯수
|
perPage: process.env.VUE_APP_TABLE_DEFAULT_PERPAGE, // 페이지당 보여줄 갯수
|
||||||
perPageOptions: [
|
perPageOptions: [
|
||||||
{ text: '5줄', value: 5 },
|
{ text: '5줄', value: 5 },
|
||||||
@ -242,7 +232,6 @@ export default {
|
|||||||
dark: false,
|
dark: false,
|
||||||
headVariant: 'light'
|
headVariant: 'light'
|
||||||
},
|
},
|
||||||
isBusy: false,
|
|
||||||
sortBy: 'id',
|
sortBy: 'id',
|
||||||
sortDesc: true,
|
sortDesc: true,
|
||||||
search: null,
|
search: null,
|
||||||
@ -267,17 +256,10 @@ export default {
|
|||||||
selectedRowModes: ['multi', 'single', 'range']
|
selectedRowModes: ['multi', 'single', 'range']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup: function () {},
|
|
||||||
created: function () {},
|
|
||||||
mounted: function () {
|
|
||||||
this.getDatas()
|
|
||||||
},
|
|
||||||
unmounted() {},
|
|
||||||
methods: {
|
methods: {
|
||||||
async getDatas(page = 1) {
|
setDatas(page = 1) {
|
||||||
this.isBusy = true
|
this.$store
|
||||||
const results = await this.callAPI('/todo', {
|
.dispatch('TodoStore/setData', {
|
||||||
params: {
|
|
||||||
page: page,
|
page: page,
|
||||||
perPage: this.perPage,
|
perPage: this.perPage,
|
||||||
sortBy: this.sortBy,
|
sortBy: this.sortBy,
|
||||||
@ -288,90 +270,60 @@ export default {
|
|||||||
filterField: this.filterField,
|
filterField: this.filterField,
|
||||||
filterDateStart: this.filterDateStart,
|
filterDateStart: this.filterDateStart,
|
||||||
filterDateEnd: this.filterDateEnd
|
filterDateEnd: this.filterDateEnd
|
||||||
}
|
})
|
||||||
})
|
.catch((error) => {
|
||||||
//console.log(JSON.stringify(results))
|
alert(error)
|
||||||
console.log(results)
|
})
|
||||||
this.total = results.total
|
|
||||||
this.perPage = results.perPage
|
|
||||||
this.page = results.page
|
|
||||||
this.sortBy = results.sortBy
|
|
||||||
this.sortDesc = results.sortDesc === 'true' ? true : false
|
|
||||||
this.rows = results.rows
|
|
||||||
this.isBusy = false
|
|
||||||
},
|
},
|
||||||
searchClick() {
|
searchClick() {
|
||||||
this.getDatas()
|
this.setDatas()
|
||||||
},
|
},
|
||||||
pageClick(bvEvent, page) {
|
pageClick(event, page) {
|
||||||
console.log(bvEvent)
|
//console.log(event)
|
||||||
this.getDatas(page)
|
this.setDatas(page)
|
||||||
// bvEvent.preventDefault()
|
// event.preventDefault()
|
||||||
},
|
},
|
||||||
perPageClick(perPage) {
|
perPageClick(perPage) {
|
||||||
this.perPage = perPage
|
this.perPage = perPage
|
||||||
console.log(this.perPage)
|
//console.log(this.perPage)
|
||||||
this.getDatas()
|
this.setDatas()
|
||||||
},
|
},
|
||||||
sortClick(event) {
|
sortClick(event) {
|
||||||
console.log(event)
|
//console.log(event)
|
||||||
this.sortBy = event.sortBy
|
this.sortBy = event.sortBy
|
||||||
this.sortDesc = event.sortDesc
|
this.sortDesc = event.sortDesc
|
||||||
this.getDatas()
|
this.setDatas()
|
||||||
},
|
},
|
||||||
dateClick() {
|
dateClick() {
|
||||||
this.filterField = this.filterDateField
|
this.filterField = this.filterDateField
|
||||||
this.getDatas()
|
this.setDatas()
|
||||||
},
|
},
|
||||||
isDoneClick() {
|
isDoneClick() {
|
||||||
this.filter = this.filterIsDone
|
this.filter = this.filterIsDone
|
||||||
this.filterField = 'is_done'
|
this.filterField = 'is_done'
|
||||||
this.getDatas()
|
this.setDatas()
|
||||||
},
|
|
||||||
busyToggle() {
|
|
||||||
this.isBusy = !this.isBusy
|
|
||||||
},
|
},
|
||||||
rowSelectedToggle(selectedRow) {
|
rowSelectedToggle(selectedRow) {
|
||||||
this.selectedRows = selectedRow
|
this.selectedRows = selectedRow
|
||||||
},
|
},
|
||||||
rowSelectAllToggle() {
|
rowSelectAllToggle() {
|
||||||
if (this.selectedRowAllToggle) {
|
if (this.selectedRowAllToggle) {
|
||||||
this.$refs.todoTable.clearSelected()
|
this.$refs.BTable.clearSelected()
|
||||||
this.selectedRowAllToggle = false
|
this.selectedRowAllToggle = false
|
||||||
} else {
|
} else {
|
||||||
this.$refs.todoTable.selectAllRows()
|
this.$refs.BTable.selectAllRows()
|
||||||
this.selectedRowAllToggle = true
|
this.selectedRowAllToggle = true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// rowSelectSet(id) {
|
// rowSelectSet(id) {
|
||||||
// // Rows are indexed from 0, so the third row is index 2
|
// // Rows are indexed from 0, so the third row is index 2
|
||||||
// this.$refs.todoTable.selectRow(id)
|
// this.$refs.BTable.selectRow(id)
|
||||||
// },
|
// },
|
||||||
// rowSelectUnSet(id) {
|
// rowSelectUnSet(id) {
|
||||||
// // Rows are indexed from 0, so the third row is index 2
|
// // Rows are indexed from 0, so the third row is index 2
|
||||||
// this.$refs.todoTable.unselectRow(id)
|
// this.$refs.BTable.unselectRow(id)
|
||||||
// }
|
// }
|
||||||
async callAPI(url, params) {
|
}
|
||||||
console.log('CallAPI..', [url, params])
|
|
||||||
axios.defaults.baseURL = process.env.VUE_APP_BACKEND_HOST
|
|
||||||
//전송 Header에 추가
|
|
||||||
const headers = {
|
|
||||||
Authorization:
|
|
||||||
'Bearer ' +
|
|
||||||
sessionStorage.getItem(process.env.VUE_APP_LOCALSTORAGE_NAME)
|
|
||||||
}
|
|
||||||
return await axios
|
|
||||||
.get(url, params, headers)
|
|
||||||
.then((response) => {
|
|
||||||
return response.status === 200 ? response.data : []
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log(err)
|
|
||||||
return []
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import VueRouter from 'vue-router'
|
import VueRouter from 'vue-router'
|
||||||
import HomeView from '../views/HomeView.vue'
|
import HomeView from '../views/HomeView.vue'
|
||||||
|
import store from '../store'
|
||||||
Vue.use(VueRouter)
|
Vue.use(VueRouter)
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
@ -33,8 +33,8 @@ const routes = [
|
|||||||
component: () =>
|
component: () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "api", webpackPrefetch:true */ '@/views/todoView.vue'
|
/* webpackChunkName: "api", webpackPrefetch:true */ '@/views/todoView.vue'
|
||||||
)
|
),
|
||||||
//meta: { requiredAuth: true }
|
meta: { requiredAuth: true }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -52,21 +52,6 @@ const router = new VueRouter({
|
|||||||
// 1. 전역가드 : router.beforeEach((to, from, next) 이런식으로 전체에 대해 특정조건 (meta: { requiredAuth: true })으로 사용
|
// 1. 전역가드 : router.beforeEach((to, from, next) 이런식으로 전체에 대해 특정조건 (meta: { requiredAuth: true })으로 사용
|
||||||
// 2. 라우터 가드: 특정 Route Path아래에 직접선언-> beforeEach: function(to, from, next){}
|
// 2. 라우터 가드: 특정 Route Path아래에 직접선언-> beforeEach: function(to, from, next){}
|
||||||
// 3. 컴포넌트 가드: 특정 vue 파일내에서 바로 선언해서 사용 :// view/Mypage.vue ( Mypage Components )
|
// 3. 컴포넌트 가드: 특정 vue 파일내에서 바로 선언해서 사용 :// view/Mypage.vue ( Mypage Components )
|
||||||
// export default {
|
|
||||||
// name: "Mypage",
|
|
||||||
// beforeRouteEnter(to, from, next) {
|
|
||||||
// // Login 컴포넌트가 화면에 표시되기 전에 수행될 로직
|
|
||||||
// // Login 컴포넌트는 아직 생성되지 않은 시점
|
|
||||||
// },
|
|
||||||
// beforeRouteUpdate(to, from, next) {
|
|
||||||
// // 화면에 표시된 컴포넌트가 변경될 때 수행될 로직
|
|
||||||
// // `this`로 Login 컴포넌트를 접근할 수 있음
|
|
||||||
// },
|
|
||||||
// beforeRouteLeave(to, from, next) {
|
|
||||||
// // Login 컴포넌트를 화면에 표시한 url 값이 변경되기 직전의 로직
|
|
||||||
// // `this`로 Login 컴포넌트를 접근할 수 있음
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// 로그인 처리 과정용
|
// 로그인 처리 과정용
|
||||||
// to : 이동할 url 정보가 담긴 라우터 객체
|
// to : 이동할 url 정보가 담긴 라우터 객체
|
||||||
// from : 현재 url 정보가 담긴 라우터 객체
|
// from : 현재 url 정보가 담긴 라우터 객체
|
||||||
@ -74,26 +59,21 @@ const router = new VueRouter({
|
|||||||
//여기에서 login은 path가 아니라 name에서 찾는다
|
//여기에서 login은 path가 아니라 name에서 찾는다
|
||||||
//router.beforeEach()를 호출하고 나면 모든 라우팅이 대기 상태가 된다
|
//router.beforeEach()를 호출하고 나면 모든 라우팅이 대기 상태가 된다
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
console.log('라우딩 대기')
|
|
||||||
//1. routes 설정에서 meta: { requiredAuth: true } 가 선언된 경우
|
//1. routes 설정에서 meta: { requiredAuth: true } 가 선언된 경우
|
||||||
if (to.matched.some((routeRecord) => routeRecord.meta.requiredAuth)) {
|
if (to.matched.some((route) => route.meta.requiredAuth)) {
|
||||||
//2. 로그인 인증 않된 경우
|
//2. 로그인 인증 않된 경우
|
||||||
//sessionStorage Access-Token이 없으면 Login페이지로 전송
|
//console.log(store.getters)
|
||||||
if (!sessionStorage.getItem(process.env.VUE_APP_SESSIONSTORAGE_JWT_NAME)) {
|
//console.log(store.getters['AuthStore/isAuthenticated'])
|
||||||
console.log(from.path + ' => 3. Login 페이지 이동 => 로그인 페이지')
|
if (!store.getters['AuthStore/isAuthenticated']) {
|
||||||
//로그인 성공 후 이동할 URL 저장
|
console.log(from.name + ' => 3. To[' + to.name + '] => 로그인')
|
||||||
sessionStorage.setItem(
|
next({ name: 'login', params: { return_url: to.name } })
|
||||||
process.env.VUE_APP_SESSIONSTORAGE_REDIRECT_NAME,
|
|
||||||
to.path
|
|
||||||
)
|
|
||||||
next({ name: 'login' })
|
|
||||||
} else {
|
} else {
|
||||||
//3. 로그인 인증완료
|
//3. 로그인 인증완료
|
||||||
console.log(from.path + ' => 2. 이미인증완료 => ' + to.path)
|
console.log(from.name + ' => 2. 이미인증완료 => ' + to.name)
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(from.path + ' => 1. 인증요구 없음 => ' + to.path)
|
console.log(from.name + ' => 1. 인증요구 없음 => ' + to.name)
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
|
import AuthStore from './modules/auth'
|
||||||
|
import TodoStore from './modules/todo'
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
state: {},
|
modules: {
|
||||||
getters: {},
|
AuthStore,
|
||||||
mutations: {},
|
TodoStore
|
||||||
actions: {},
|
}
|
||||||
modules: {}
|
|
||||||
})
|
})
|
||||||
|
|||||||
97
src/store/modules/auth.js
Normal file
97
src/store/modules/auth.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import jwt from './jwt'
|
||||||
|
|
||||||
|
//참조: https://yamoo9.github.io/axios/guide/interceptors.html
|
||||||
|
const authAxios = axios.create({
|
||||||
|
baseURL: process.env.VUE_APP_AUTH_HOST,
|
||||||
|
headers: { 'content-type': 'application/json' }
|
||||||
|
})
|
||||||
|
authAxios.interceptors.request.use(
|
||||||
|
(config) => jwt.getAuthorizationHeader(config),
|
||||||
|
(error) => {
|
||||||
|
// Do something with request error
|
||||||
|
Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
authAxios.defaults.headers.post['Content-Type'] =
|
||||||
|
'application/x-www-form-urlencoded'
|
||||||
|
|
||||||
|
// count state 속성 추가
|
||||||
|
const state = {
|
||||||
|
token: {
|
||||||
|
accessToken: jwt.getToken()
|
||||||
|
}, // 토큰정보
|
||||||
|
isAuthenticated: jwt.isAuthenticated()
|
||||||
|
}
|
||||||
|
const getters = {
|
||||||
|
getAccessToken: function (state) {
|
||||||
|
return state.token.accessToken
|
||||||
|
},
|
||||||
|
isAuthenticated: function (state) {
|
||||||
|
return state.isAuthenticated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const mutations = {
|
||||||
|
login: function (state, payload = {}) {
|
||||||
|
state.token.accessToken = payload.accessToken
|
||||||
|
state.isAuthenticated = true
|
||||||
|
jwt.saveToken(payload.accessToken)
|
||||||
|
},
|
||||||
|
logout: function (state) {
|
||||||
|
state.token.accessToken = ''
|
||||||
|
state.isAuthenticated = false
|
||||||
|
jwt.destroyToken()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const actions = {
|
||||||
|
login: function (context, payload) {
|
||||||
|
let params = {
|
||||||
|
email: payload.email,
|
||||||
|
password: payload.password
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
authAxios
|
||||||
|
.post('/auth/login', params)
|
||||||
|
.then((response) => {
|
||||||
|
const { data } = response
|
||||||
|
context.commit('login', {
|
||||||
|
accessToken: data.accessToken
|
||||||
|
})
|
||||||
|
resolve(response)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
logout: function (context, payload) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(function () {
|
||||||
|
context.commit('logout', payload)
|
||||||
|
resolve({})
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
register: function (context, payload) {
|
||||||
|
let params = {
|
||||||
|
email: payload.email,
|
||||||
|
password: payload.password,
|
||||||
|
name: payload.name
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
authAxios
|
||||||
|
.post('/user/register', params)
|
||||||
|
.then((response) => {
|
||||||
|
const { data } = response
|
||||||
|
context.commit('login', {
|
||||||
|
accessToken: data.accessToken
|
||||||
|
})
|
||||||
|
resolve(response)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default { namespaced: true, state, getters, mutations, actions }
|
||||||
28
src/store/modules/jwt.js
Normal file
28
src/store/modules/jwt.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
const Payload_KEY_NAME = 'access_token'
|
||||||
|
const getToken = () => {
|
||||||
|
return window.localStorage.getItem(Payload_KEY_NAME)
|
||||||
|
}
|
||||||
|
const saveToken = (token) => {
|
||||||
|
window.localStorage.setItem(Payload_KEY_NAME, token)
|
||||||
|
}
|
||||||
|
const destroyToken = () => {
|
||||||
|
window.localStorage.removeItem(Payload_KEY_NAME)
|
||||||
|
}
|
||||||
|
const isAuthenticated = () => {
|
||||||
|
return !!getToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAuthorizationHeader = (config) => {
|
||||||
|
//로그인이 되었는지 확인후 Request시 헤더에 로그인정보 추가하기 위함
|
||||||
|
if (isAuthenticated) {
|
||||||
|
config.headers.common['Authorization'] = getToken()
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
getToken,
|
||||||
|
saveToken,
|
||||||
|
destroyToken,
|
||||||
|
isAuthenticated,
|
||||||
|
getAuthorizationHeader
|
||||||
|
}
|
||||||
55
src/store/modules/todo.js
Normal file
55
src/store/modules/todo.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import jwt from './jwt'
|
||||||
|
|
||||||
|
//참조: https://yamoo9.github.io/axios/guide/interceptors.html
|
||||||
|
const todoAxios = axios.create({
|
||||||
|
baseURL: process.env.VUE_APP_BACKEND_HOST,
|
||||||
|
headers: { 'content-type': 'application/json' }
|
||||||
|
})
|
||||||
|
todoAxios.interceptors.request.use(
|
||||||
|
(config) => jwt.getAuthorizationHeader(config),
|
||||||
|
(error) => {
|
||||||
|
// Do something with request error
|
||||||
|
Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
todoAxios.defaults.headers.post['Content-Type'] =
|
||||||
|
'application/x-www-form-urlencoded'
|
||||||
|
|
||||||
|
// count state 속성 추가
|
||||||
|
const state = {
|
||||||
|
total: 0,
|
||||||
|
rows: []
|
||||||
|
}
|
||||||
|
const getters = {
|
||||||
|
getTotal: function (state) {
|
||||||
|
return state.total
|
||||||
|
},
|
||||||
|
getRows: function (state) {
|
||||||
|
return state.rows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const mutations = {
|
||||||
|
setData: function (state, data) {
|
||||||
|
state.total = data.total
|
||||||
|
state.rows = data.rows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const actions = {
|
||||||
|
setData: function (context, params) {
|
||||||
|
console.log(params)
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
todoAxios
|
||||||
|
.get('/todo', params)
|
||||||
|
.then((response) => {
|
||||||
|
const { data } = response
|
||||||
|
context.commit('setData', data)
|
||||||
|
resolve(response)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default { namespaced: true, state, getters, mutations, actions }
|
||||||
@ -1,15 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="wrapper">
|
||||||
<!-- 참조: https://codesandbox.io/s/3v0m1?file=/src/components/board/BoardList.vue -->
|
<!-- 참조: https://codesandbox.io/s/3v0m1?file=/src/components/board/BoardList.vue -->
|
||||||
<LoginComponent />
|
<LoginComponent />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import LoginComponent from '@/components/common/LoginComponent.vue'
|
import LoginComponent from '@/components/LoginComponent.vue'
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
LoginComponent
|
LoginComponent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -3,18 +3,48 @@
|
|||||||
<!-- 참조: https://codesandbox.io/s/3v0m1?file=/src/components/board/BoardList.vue -->
|
<!-- 참조: https://codesandbox.io/s/3v0m1?file=/src/components/board/BoardList.vue -->
|
||||||
<ListComponent />
|
<ListComponent />
|
||||||
<div class="justify-content-end border">
|
<div class="justify-content-end border">
|
||||||
<b-button v-b-modal.inputForm variant="outline-primary">입력</b-button>
|
<template v-if="isAuthenticated">
|
||||||
<InputComponent />
|
<b-button v-b-modal.inputForm variant="outline-primary">입력</b-button>
|
||||||
|
<b-modal
|
||||||
|
id="inputForm"
|
||||||
|
centered
|
||||||
|
title="입력폼"
|
||||||
|
size="lg"
|
||||||
|
:header-bg-variant="headerBgVariant"
|
||||||
|
:header-text-variant="headerTextVariant"
|
||||||
|
:body-bg-variant="bodyBgVariant"
|
||||||
|
:body-text-variant="bodyTextVariant"
|
||||||
|
:footer-bg-variant="footerBgVariant"
|
||||||
|
:footer-text-variant="footerTextVariant"
|
||||||
|
>
|
||||||
|
<InputComponent />
|
||||||
|
</b-modal>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import ListComponent from '@/components/todo/ListComponent.vue'
|
import ListComponent from '@/components/todo/ListComponent.vue'
|
||||||
import InputComponent from '@/components/todo/InputComponent.vue'
|
import InputComponent from '@/components/todo/InputComponent.vue'
|
||||||
|
import { createNamespacedHelpers } from 'vuex'
|
||||||
|
const authStore = createNamespacedHelpers('AuthStore')
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
InputComponent,
|
InputComponent,
|
||||||
ListComponent
|
ListComponent
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...authStore.mapGetters(['isAuthenticated'])
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
headerBgVariant: 'dark',
|
||||||
|
headerTextVariant: 'light',
|
||||||
|
bodyBgVariant: 'light',
|
||||||
|
bodyTextVariant: 'dark',
|
||||||
|
footerBgVariant: 'warning',
|
||||||
|
footerTextVariant: 'dark'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user