Vue2_Frontend 수정1...

This commit is contained in:
최준흠 2022-08-30 15:54:22 +09:00
parent 5ac1eb6722
commit 409ad85356
9 changed files with 1300 additions and 713 deletions

1566
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@
"vee-validate": "^3.4.14",
"vue": "^2.6.14",
"vue-router": "^3.5.1",
"vue2-editor": "^2.10.3",
"vuex": "^3.6.2"
},
"devDependencies": {

View File

@ -11,7 +11,7 @@
<b-input-group>
<label class="input-group-text">{{ field.email.label }}</label>
<b-form-input
:name="field.email.label"
:name="field.email.key"
:type="field.email.type"
v-model="form.email"
:state="getValidationState(validationContext)"
@ -30,7 +30,7 @@
<b-input-group>
<label class="input-group-text">{{ field.password.label }}</label>
<b-form-input
:name="field.password.label"
:name="field.password.key"
:type="field.password.type"
v-model="form.password"
:state="getValidationState(validationContext)"
@ -89,13 +89,12 @@ export default {
password: this.form.password
})
)
if (result) {
if (result.access_token) {
// localStorage , key-value Storage
localStorage.setItem(process.env.VUE_APP_LOCALSTORAGE_NAME, result)
alert('로그인 성공...')
console.log('로그인 성공...')
console.log(this.$router)
this.$router.push(this.$reouter.path)
//this.$router.push(this.$router.path)
} else {
console.log(result)
localStorage.removeItem(process.env.VUE_APP_LOCALSTORAGE_NAME)
@ -115,15 +114,16 @@ export default {
async callAPI(url, params) {
console.log('CallAPI..', [url, params])
axios.defaults.baseURL = process.env.VUE_APP_BACKEND_HOST
axios.defaults.headers.post['Content-Type'] =
process.env.VUE_APP_BACKEND_HEADERS_Content_Type
axios.defaults.headers.post['Access-Control-Allow-Origin'] =
process.env.VUE_APP_BACKEND_HEADERS_Access_Control_Allow_Origin
// Header CORP
// axios.defaults.headers.post['Content-Type'] =
// process.env.VUE_APP_BACKEND_HEADERS_Content_Type
// axios.defaults.headers.post['Access-Control-Allow-Origin'] =
// process.env.VUE_APP_BACKEND_HEADERS_Access_Control_Allow_Origin
return await axios
.post(url, params)
.then((response) => {
// console.log(response)
return response.status === 201 ? response.data.access_token : null
return response.status === 201 ? response.data : null
})
.catch((err) => {
console.log(err)

View File

@ -0,0 +1,180 @@
<template>
<div>
<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"
>
<validation-observer ref="observer" v-slot="{ handleSubmit }">
<b-form @submit.stop.prevent="handleSubmit(onSubmit)">
<validation-provider
:name="field.title.label"
:rules="field.title.rules"
v-slot="validationContext"
>
<b-input-group>
<label class="input-group-text">{{ field.title.label }}</label>
<b-form-input
:name="field.title.key"
:type="field.title.type"
v-model="form.title"
:state="getValidationState(validationContext)"
:style="field.title.style"
></b-form-input>
<b-form-invalid-feedback>{{
validationContext.errors[0]
}}</b-form-invalid-feedback>
</b-input-group>
</validation-provider>
<validation-provider
:name="field.content.label"
:rules="field.content.rules"
v-slot="validationContext"
>
<label class="input-group-text">{{ field.content.label }}</label>
<b-input-group>
<b-form-textarea
:config="field.content.config"
:name="field.content.key"
v-model="form.content"
:state="getValidationState(validationContext)"
:style="field.content.style"
rows="8"
></b-form-textarea>
<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>
<script>
import axios from 'axios'
//import { VueEditor } from 'vue2-editor/dist/vue2-editor.core.js'
export default {
components: {},
data() {
return {
form: {
title: null,
content: null
},
field: {
title: {
key: 'title',
label: '제목',
type: 'text',
placeholder: 'Enter your name',
rules: { required: true, min: 10 },
style: 'sm-2'
},
content: {
key: 'content',
label: '내용',
type: 'textarea',
rules: { required: true, min: 10 },
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: {
getValidationState({ dirty, validated, valid = null }) {
return dirty || validated ? valid : null
},
async onSubmit() {
const result = await this.callAPI(
'/todo',
JSON.stringify({
title: this.form.title,
content: this.form.content
})
)
if (result !== null) {
alert('todo 추가 완료...')
} else {
console.log(result)
alert('todo 추가 오류발생...')
}
},
onReset() {
// Reset our form values
this.form.email = ''
this.form.pasword = ''
// 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_BACKEND_HOST
// Header CORP
// axios.defaults.headers.post['Content-Type'] =
// process.env.VUE_APP_BACKEND_HEADERS_Content_Type
// axios.defaults.headers.post['Access-Control-Allow-Origin'] =
// process.env.VUE_APP_BACKEND_HEADERS_Access_Control_Allow_Origin
// Header
axios.defaults.headers.common = {
Authorization: `Bearer ${localStorage.getItem(
process.env.VUE_APP_LOCALSTORAGE_NAME
)}`
}
const headers = {
Authorization: 'Bearer ' + process.env.VUE_APP_LOCALSTORAGE_NAME
}
return await axios
.post(url, params, headers)
.then((response) => {
console.log(response)
return response.status === 201 ? response.data : null
})
.catch((err) => {
console.log(err)
return null
})
}
}
}
</script>
<style scoped>
/* @import '~vue2-editor/dist/vue2-editor.css';
/* Import the Quill styles you want */
/* @import '~quill/dist/quill.core.css';
@import '~quill/dist/quill.bubble.css';
@import '~quill/dist/quill.snow.css'; */
</style>

View File

@ -21,16 +21,16 @@
<label class="input-group-text">
<b-form-checkbox-group
class="d-flex flex-nowrap"
v-model="filterOn"
:options="filterOptions"
v-model="searchFields"
:options="searchOptions"
></b-form-checkbox-group>
</label>
<b-form-input
v-model="filter"
v-model="search"
type="text"
placeholder="Type to Filter"
placeholder="검색어"
></b-form-input>
<b-button :disabled="!filter" @click="filter = ''">Clear</b-button>
<b-button @click="searchClick">검색</b-button>
</b-input-group>
</b-col>
<b-col class="border">
@ -39,20 +39,19 @@
<b-form-select
v-model="perPage"
:options="perPageOptions"
@change="perPageClick"
></b-form-select>
</b-input-group>
</b-col>
</b-row>
<b-row class="overflow-auto">
<b-table
ref="selectableTable"
:items="items"
id="todoTable"
ref="todoTable"
:items="rows"
:fields="fields"
:per-page="perPage"
:current-page="currentPage"
:filter="filter"
:filter-included-fields="filterOn"
@filtered="onFiltered"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
:sort-direction="sortDirection"
@ -71,14 +70,6 @@
:dark="commonTableAttributes.dark"
:head-variant="commonTableAttributes.headVariant"
>
<!-- Cell/Field별 속성 추가용 -->
<template>
<col
v-for="field in fields"
:key="field.key"
:style="field.style"
/>
</template>
<!-- /선택 Row를 토글용 -->
<template #cell(rowSelect)="{ rowSelected }">
<template v-if="rowSelected">
@ -89,25 +80,27 @@
</template>
</template>
<!-- Row의 Index를 이용한 번호 출력 -->
<template #cell(index)="row">
{{ total - (currentPage * perPage + row.index) + perPage }}
<template #cell(id)="row">
<b-link ref="edit/{{row.item.id}}">
{{ total - (currentPage * perPage + row.index) + perPage }}
</b-link>
</template>
<!-- 제목부분 출력용 -->
<template #cell(title)="row">
{{ row.item.title }}
<b-button size="sm" @click="row.toggleDetails" class="mr-2">
{{ row.detailsShowing ? 'Hide' : 'Show' }} Details
</b-button>
</template>
<!-- 상세 내용 출력용 -->
<template #cell(content)="row">
<b-button size="sm" @click="row.toggleDetails" class="mr-2">
{{ row.detailsShowing ? '숨기기' : '보기' }}
</b-button>
</template>
<template #row-details="row">
<b-card>
<b-row class="mb-2">
<b-row>
<b-col v-html="row.item.content"></b-col>
</b-row>
<b-button size="sm" @click="row.toggleDetails"
>Hide Details</b-button
>
<b-button size="sm" @click="row.toggleDetails">숨기기</b-button>
</b-card>
</template>
<!-- 데이터 로딩중 출력용 -->
@ -121,18 +114,20 @@
</b-row>
<b-row>
<b-col>
<b-pagination
v-model="currentPage"
:total-rows="total"
:per-page="perPage"
></b-pagination>
<p>{{ selectedRows }}</p>
</b-col>
<b-col>
<b-input-group>
<label class="input-group-text">DB</label>
<input type="text" class="form-control" v-model="search" />
<span class="input-group-text" @click="searchForDB">검색</span>
</b-input-group>
<template>
<b-pagination
v-model="currentPage"
:total-rows="total"
:per-page="perPage"
@page-click="pageClick"
aria-controls="todoTable"
size="lg"
class="justify-content-center"
></b-pagination>
</template>
</b-col>
<b-col>
<b-input-group class="justify-content-end">
@ -141,10 +136,6 @@
</b-col>
</b-row>
</b-container>
<p>
Selected Rows:<br />
{{ selectedRows }}
</p>
</div>
</template>
<script>
@ -154,45 +145,41 @@ export default {
components: {},
data: function () {
return {
items: [],
rows: [],
fields: [
{ key: 'rowSelect', label: '✓', width: '55px' },
{ key: 'rowSelect', label: '✓', thStyle: { width: '55px' } },
{
key: 'index',
key: 'id',
label: '번호',
style: { width: '55px' },
variant: 'primary'
thStyle: { width: '65px' },
variant: 'primary',
sortable: true
},
{
key: 'title',
label: '제목',
sortable: true,
sortByFormatted: true,
filterByFormatted: true
sortable: true
},
{
key: 'content',
label: '내용',
sortable: true
},
// {
// key: 'content',
// label: '',
// sortable: true,
// sortByFormatted: true,
// filterByFormatted: true
// },
{
key: 'is_done',
label: '사용여부',
style: { width: '105px' },
// formatter: (value, key, item) => {
thStyle: { width: '105px' },
formatter: (value) => {
return value ? 'Yes' : 'No'
},
sortable: true,
filterByFormatted: true
sortByFormatted: true //fomatter Sort true
},
// { key: 'updateAt', label: '' },
{
key: 'createdAt',
label: '등록일',
style: { width: '105px' },
thStyle: { width: '105px' },
formatter: (value) => {
return value.replace(
/([0-9]{4})-([0-9]{2})-([0-9]{2}).*/gi,
@ -200,25 +187,23 @@ export default {
)
},
sortable: true,
sortByFormatted: true
// filterByFormatted: true
sortByFormatted: true //fomatter Sort true
}
],
total: 0,
currentPage: 1, //
perPage: 5, //
perPageOptions: [
{ value: 5, text: '5줄' },
{ value: 10, text: '10줄' },
{ value: 30, text: '30줄' },
{ value: 60, text: '60줄' },
{ value: 100, text: '100줄' }
{ text: '5줄', value: 5 },
{ text: '10줄', value: 10 },
{ text: '30줄', value: 30 },
{ text: '60줄', value: 60 },
{ text: '100줄', value: 100 }
],
currentPage: 1, //
search: null, //DB
//
commonTableAttributes: {
stickyHeader: '400px',
noBorderCollapse: false,
stickyHeader: '50%',
//noBorderCollapse: false,
striped: true,
hover: true,
bordered: true,
@ -227,15 +212,15 @@ export default {
},
isBusy: false,
sortBy: 'id',
sortDesc: false,
sortDirection: 'asc',
filter: null,
filterOn: ['title', 'content'],
filterOptions: [
sortDesc: true,
sortDirection: 'desc',
search: null, //DB
searchFields: ['title', 'content'],
searchOptions: [
{ text: '제목', value: 'title' },
{ text: '내용', value: 'content' },
{ text: '수정일', value: 'updateAt', disabled: true },
{ text: '등록일', value: 'createdAt' }
{ text: '수정일', value: 'updateAt' },
{ text: '등록일', value: 'createAt' }
],
selectModes: ['multi', 'single', 'range'],
selectMode: 'multi',
@ -252,51 +237,70 @@ export default {
methods: {
async getDatas() {
this.isBusy = true
this.items = await this.callAPI('/todo', {
search: this.search
const results = await this.callAPI('/todo', {
params: {
sortBy: this.sortBy,
sortDirection: this.sortDirection,
searchFields: this.searchFields,
search: this.search,
page: this.currentPage,
perPage: this.perPage
}
})
//console.log(JSON.stringify(this.items))
this.total = this.items.length
//console.log(JSON.stringify(results))
console.log(results)
this.total = results.total
this.perPage = results.perPage
this.page = results.page
this.rows = results.rows
this.isBusy = false
},
searchForDB() {
searchClick() {
this.currentPage = 1
this.getDatas()
},
pageClick(bvEvent, page) {
console.log(bvEvent)
this.currentPage = page
this.getDatas()
bvEvent.preventDefault()
},
perPageClick(perPage) {
this.currentPage = 1
this.perPage = perPage
console.log(this.perPage)
this.getDatas()
},
busyToggle() {
this.isBusy = !this.isBusy
},
onFiltered(filteredItems) {
this.total = filteredItems.length
this.currentPage = 1
},
rowSelectedToggle(selectedRow) {
this.selectedRows = selectedRow
},
rowSelectAllToggle() {
if (this.selectedAllToggle) {
this.$refs.selectableTable.clearSelected()
this.$refs.todoTable.clearSelected()
this.selectedAllToggle = false
} else {
this.$refs.selectableTable.selectAllRows()
this.$refs.todoTable.selectAllRows()
this.selectedAllToggle = true
}
},
// rowSelectSet(id) {
// // Rows are indexed from 0, so the third row is index 2
// this.$refs.selectableTable.selectRow(id)
// this.$refs.todoTable.selectRow(id)
// },
// rowSelectUnSet(id) {
// // Rows are indexed from 0, so the third row is index 2
// this.$refs.selectableTable.unselectRow(id)
// this.$refs.todoTable.unselectRow(id)
// }
async callAPI(url, params) {
console.log('CallAPI..', [url, params])
axios.defaults.baseURL = process.env.VUE_APP_BACKEND_HOST
axios.defaults.headers.post['Content-Type'] =
process.env.VUE_APP_BACKEND_HEADERS_Content_Type
axios.defaults.headers.post['Access-Control-Allow-Origin'] =
process.env.VUE_APP_BACKEND_HEADERS_Access_Control_Allow_Origin
// axios.defaults.headers.post['Content-Type'] =
// process.env.VUE_APP_BACKEND_HEADERS_Content_Type
// axios.defaults.headers.post['Access-Control-Allow-Origin'] =
// process.env.VUE_APP_BACKEND_HEADERS_Access_Control_Allow_Origin
return await axios
.get(url, params)
.then((response) => {
@ -308,16 +312,7 @@ export default {
})
}
},
computed: {
sortOptions() {
// Create an options list from our fields
return this.fields
.filter((f) => f.sortable)
.map((f) => {
return { text: f.label, value: f.key }
})
}
}
computed: {}
}
</script>
<style scoped></style>

View File

@ -26,7 +26,6 @@ localize('ko', ko)
// Install VeeValidate components globally
Vue.component('ValidationObserver', ValidationObserver)
Vue.component('ValidationProvider', ValidationProvider)
Vue.config.productionTip = false
Vue.use(BootstrapVue)
new Vue({

View File

@ -32,9 +32,9 @@ const routes = [
name: 'todo',
component: () =>
import(
/* webpackChunkName: "api", webpackPrefetch:true */ '@/views/todo/listView.vue'
/* webpackChunkName: "api", webpackPrefetch:true */ '@/views/todoView.vue'
),
meta: { requiresAuth: true }
meta: { requiredAuth: true }
}
]
@ -53,16 +53,17 @@ const router = new VueRouter({
// next : to에서 지정한 url로 이동하기 위해 꼭 호출해야 하는 함수
//여기에서 login은 path가 아니라 name에서 찾는다
router.beforeEach((to, from, next) => {
//routes 설정에서 meta: { requiresAuth: true } 가 선언된 경우
if (to.matched.some((record) => record.meta.requiresAuth)) {
//routes 설정에서 meta: { requiredAuth: true } 가 선언된 경우
if (to.matched.some((record) => record.meta.requiredAuth)) {
//localStorage에 Access-Token이 없으면 Login페이지로 전송
if (!localStorage.getItem(process.env.VUE_APP_LOCALSTORAGE_NAME)) {
console.log(from.path + ' => 인증않됨 => Login 페이지 이동')
next({ name: 'login' })
} else {
console.log(from.path + " => 인증됨 => '" + to.path + "'")
next()
}
console.log(from.path + ' => 인증 통과 => ' + to.path)
next()
} else {
console.log(from.path + " => 인증않됨 => '" + to.path + "'")
console.log(from.path + ' => 인증요구없으므로 통과 => ' + to.path)
next()
}
})

View File

@ -1,6 +1,6 @@
<template>
<!-- 참조: https://codesandbox.io/s/3v0m1?file=/src/components/board/BoardList.vue -->
<div>
<!-- 참조: https://codesandbox.io/s/3v0m1?file=/src/components/board/BoardList.vue -->
<LoginComponent />
</div>
</template>

21
src/views/todoView.vue Normal file
View File

@ -0,0 +1,21 @@
<template>
<div>
<!-- 참조: https://codesandbox.io/s/3v0m1?file=/src/components/board/BoardList.vue -->
<ListComponent />
<div class="justify-content-end border">
<b-button v-b-modal.inputForm variant="outline-primary">입력</b-button>
<InputComponent />
</div>
</div>
</template>
<script>
import ListComponent from '@/components/todo/ListComponent.vue'
import InputComponent from '@/components/todo/InputComponent.vue'
export default {
components: {
InputComponent,
ListComponent
}
}
</script>
<style scoped></style>