feat : page upload
This commit is contained in:
parent
392e846342
commit
361287b098
18
package-lock.json
generated
18
package-lock.json
generated
@ -9,7 +9,9 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.8",
|
"axios": "^1.6.8",
|
||||||
|
"jwt-decode": "^4.0.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
"vue": "^3.4.21",
|
"vue": "^3.4.21",
|
||||||
"vue-router": "^4.3.0"
|
"vue-router": "^4.3.0"
|
||||||
},
|
},
|
||||||
@ -2847,6 +2849,14 @@
|
|||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jwt-decode": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/keyv": {
|
"node_modules/keyv": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||||
@ -3239,6 +3249,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pinia-plugin-persistedstate": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-MK++8LRUsGF7r45PjBFES82ISnPzyO6IZx3CH5vyPseFLZCk1g2kgx6l/nW8pEBKxxd4do0P6bJw+mUSZIEZUQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"pinia": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pinia/node_modules/vue-demi": {
|
"node_modules/pinia/node_modules/vue-demi": {
|
||||||
"version": "0.14.7",
|
"version": "0.14.7",
|
||||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz",
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz",
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.8",
|
"axios": "^1.6.8",
|
||||||
|
"jwt-decode": "^4.0.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
"vue": "^3.4.21",
|
"vue": "^3.4.21",
|
||||||
"vue-router": "^4.3.0"
|
"vue-router": "^4.3.0"
|
||||||
},
|
},
|
||||||
|
@ -2,9 +2,9 @@ import { localAxios } from '@/utils/http-commons';
|
|||||||
|
|
||||||
const local = localAxios;
|
const local = localAxios;
|
||||||
|
|
||||||
function getArticles(success, fail) {
|
function getArticles(params,success, fail) {
|
||||||
//list
|
//list
|
||||||
local.get('/article/list').then(success).catch(fail);
|
local.get('/article/list',{ params }).then(success).catch(fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchArticle(keyword, success, fail) {
|
function searchArticle(keyword, success, fail) {
|
||||||
|
27
src/api/member.js
Normal file
27
src/api/member.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { localAxios } from "@/utils/http-commons";
|
||||||
|
|
||||||
|
const local = localAxios;
|
||||||
|
|
||||||
|
async function userConfirm(param, success, fail) {
|
||||||
|
await local.post(`/member/login`, param).then(success).catch(fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findById(userid, success, fail) {
|
||||||
|
local.defaults.headers["Authorization"] = sessionStorage.getItem("accessToken");
|
||||||
|
await local.get(`/member/info/${userid}`).then(success).catch(fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function tokenRegeneration(user, success, fail) {
|
||||||
|
local.defaults.headers["refreshToken"] = sessionStorage.getItem("refreshToken"); //axios header에 refresh-token 셋팅
|
||||||
|
await local.post(`/member/refresh`, user).then(success).catch(fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logout(userid, success, fail) {
|
||||||
|
await local.get(`/member/logout/${userid}`).then(success).catch(fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
function registMember(member, success, fail) {
|
||||||
|
local.post('/member/join',JSON.stringify(member)).then(success).catch(fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { userConfirm, findById, tokenRegeneration, logout, registMember };
|
@ -1,5 +1,21 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { RouterLink } from 'vue-router';
|
import { RouterLink } from 'vue-router';
|
||||||
|
import { useMenuStore } from "@/stores/menu";
|
||||||
|
import { useMemberStore } from "@/stores/member";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
|
||||||
|
const menuStore = useMenuStore();
|
||||||
|
const memberStore = useMemberStore();
|
||||||
|
|
||||||
|
const { menuList } = storeToRefs(menuStore);
|
||||||
|
const { changeMenuState } = menuStore;
|
||||||
|
|
||||||
|
const { userLogout } = memberStore;
|
||||||
|
|
||||||
|
const logout = () => {
|
||||||
|
userLogout();
|
||||||
|
changeMenuState();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -11,11 +27,31 @@ import { RouterLink } from 'vue-router';
|
|||||||
<div class="menuWrapper">
|
<div class="menuWrapper">
|
||||||
<div class="item"><RouterLink :to="{ name: 'home' }">검색</RouterLink></div>
|
<div class="item"><RouterLink :to="{ name: 'home' }">검색</RouterLink></div>
|
||||||
<div class="item"><RouterLink :to="{ name: 'board' }">게시판</RouterLink></div>
|
<div class="item"><RouterLink :to="{ name: 'board' }">게시판</RouterLink></div>
|
||||||
<div class="item" v-if="true"><RouterLink :to="{ name: 'home' }">로그인</RouterLink></div>
|
|
||||||
|
<template v-for="menu in menuList" :key="menu.routeName">
|
||||||
|
<template v-if="menu.show">
|
||||||
|
<template v-if="menu.routeName === 'user-logout'">
|
||||||
|
<div class="item">
|
||||||
|
<router-link to="/" @click.prevent="logout" class="nav-link">{{
|
||||||
|
menu.name
|
||||||
|
}}</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="item">
|
||||||
|
<router-link :to="{ name: menu.routeName }" class="nav-link">{{
|
||||||
|
menu.name
|
||||||
|
}}</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- <div class="item" v-if="true"><RouterLink :to="{ name: 'user-login' }">로그인</RouterLink></div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="item"><RouterLink :to="{ name: 'home' }">마이페이지</RouterLink></div>
|
<div class="item"><RouterLink :to="{ name: 'home' }">마이페이지</RouterLink></div>
|
||||||
<div class="item"><RouterLink :to="{ name: 'home' }">로그아웃</RouterLink></div>
|
<div class="item"><RouterLink :to="{ name: 'home' }">로그아웃</RouterLink></div>
|
||||||
</template>
|
</template> -->
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
@ -1,12 +1,43 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { getArticles } from '@/api/article';
|
import { getArticles } from '@/api/article';
|
||||||
import { ref } from 'vue';
|
import { ref , reactive } from 'vue';
|
||||||
import { RouterLink } from 'vue-router';
|
import { RouterLink } from 'vue-router';
|
||||||
import FilledButton from '../common/FilledButton.vue';
|
import FilledButton from '../common/FilledButton.vue';
|
||||||
|
import PageNavigation from "../common/PageNavigation.vue";
|
||||||
|
|
||||||
const articles = ref([]);
|
const articles = ref([]);
|
||||||
|
|
||||||
getArticles(({ data }) => (articles.value = data));
|
const params = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
key: 'all',
|
||||||
|
word: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentPage = ref(1)
|
||||||
|
const totalpage = ref(1)
|
||||||
|
|
||||||
|
function searchList() {
|
||||||
|
getArticles(
|
||||||
|
params,
|
||||||
|
({ data }) => {
|
||||||
|
articles.value = data.articles
|
||||||
|
currentPage.value = data.page.pageNo
|
||||||
|
totalpage.value = data.page.total
|
||||||
|
console.log(articles.value)
|
||||||
|
console.log(currentPage.value)
|
||||||
|
console.log(totalpage.value)
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
searchList()
|
||||||
|
|
||||||
|
function pageChange(value) {
|
||||||
|
params.pageNo = value
|
||||||
|
searchList()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -30,6 +61,7 @@ getArticles(({ data }) => (articles.value = data));
|
|||||||
</RouterLink>
|
</RouterLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<PageNavigation :currentPage="currentPage" :totalPage="totalpage" @page-change="pageChange" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
82
src/components/users/UserLogin.vue
Normal file
82
src/components/users/UserLogin.vue
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from "vue"
|
||||||
|
import { storeToRefs } from "pinia"
|
||||||
|
import { useRouter } from "vue-router"
|
||||||
|
import { useMemberStore } from "@/stores/member"
|
||||||
|
import { useMenuStore } from "@/stores/menu"
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const memberStore = useMemberStore()
|
||||||
|
|
||||||
|
const { isLogin, isLoginError } = storeToRefs(memberStore)
|
||||||
|
const { userLogin, getUserInfo } = memberStore
|
||||||
|
const { changeMenuState } = useMenuStore()
|
||||||
|
|
||||||
|
const loginUser = ref({
|
||||||
|
id: "",
|
||||||
|
password: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
const login = async () => {
|
||||||
|
await userLogin(loginUser.value)
|
||||||
|
let token = sessionStorage.getItem("accessToken")
|
||||||
|
console.log(token)
|
||||||
|
console.log("isLogin: " + isLogin.value)
|
||||||
|
if (isLogin.value) {
|
||||||
|
getUserInfo(token)
|
||||||
|
changeMenuState()
|
||||||
|
router.replace("/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-10">
|
||||||
|
<h2 class="my-3 py-3 shadow-sm bg-light text-center">
|
||||||
|
<mark class="orange">로그인</mark>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-10">
|
||||||
|
<form>
|
||||||
|
<div class="form-check mb-3 float-end">
|
||||||
|
<input class="form-check-input" type="checkbox" />
|
||||||
|
<label class="form-check-label" for="saveid"> 아이디저장 </label>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 text-start">
|
||||||
|
<label for="userid" class="form-label">아이디 : </label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
v-model="loginUser.id"
|
||||||
|
placeholder="아이디..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 text-start">
|
||||||
|
<label for="userpwd" class="form-label">비밀번호 : </label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
class="form-control"
|
||||||
|
v-model="loginUser.password"
|
||||||
|
@keyup.enter="login"
|
||||||
|
placeholder="비밀번호..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 text-start" v-if="isLoginError === true">
|
||||||
|
<div class="alert alert-danger" role="alert">아이디 또는 비밀번호 확인해 주세요</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto text-center">
|
||||||
|
<button type="button" class="btn btn-outline-primary mb-3" @click="login">
|
||||||
|
로그인
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-success ms-1 mb-3">회원가입</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
40
src/components/users/UserMyPage.vue
Normal file
40
src/components/users/UserMyPage.vue
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<script setup></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-10">
|
||||||
|
<h2 class="my-3 py-3 shadow-sm bg-light text-center">
|
||||||
|
<mark class="orange">내정보</mark>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-10">
|
||||||
|
<div class="card mt-3 m-auto" style="max-width: 700px">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<!-- <img
|
||||||
|
src="https://source.unsplash.com/random/250x250/?food"
|
||||||
|
class="img-fluid rounded-start"
|
||||||
|
alt="..."
|
||||||
|
/> -->
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card-body text-start">
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
<!-- <li class="list-group-item">SSAFY</li>
|
||||||
|
<li class="list-group-item">김싸피</li>
|
||||||
|
<li class="list-group-item">ssafy@ssafy.com</li> -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button type="button" class="btn btn-outline-secondary mt-2">수정</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
82
src/components/users/UserRegister.vue
Normal file
82
src/components/users/UserRegister.vue
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { registMember } from "@/api/member";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const email_id = ref("")
|
||||||
|
const email_domain = ref("")
|
||||||
|
|
||||||
|
const member = ref({
|
||||||
|
id: "",
|
||||||
|
name: "",
|
||||||
|
password: "",
|
||||||
|
email: email_id.value + "@" + email_domain.value,
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit() {
|
||||||
|
member.value.email = email_id.value + "@" + email_domain.value
|
||||||
|
console.log(member.value)
|
||||||
|
registMember(
|
||||||
|
member.value,
|
||||||
|
(response) => {
|
||||||
|
if (response.status == 200) console.log("회원가입 성공!")
|
||||||
|
router.push({ name : "user-login"})
|
||||||
|
},
|
||||||
|
(error) => console.error(error)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-10">
|
||||||
|
<h2 class="my-3 py-3 shadow-sm bg-light text-center">
|
||||||
|
<mark class="orange">회원가입</mark>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-10 text-start">
|
||||||
|
<form>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">이름 : </label>
|
||||||
|
<input type="text" class="form-control" placeholder="이름..." v-model="member.name"/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="userid" class="form-label">아이디 : </label>
|
||||||
|
<input type="text" class="form-control" placeholder="아이디..." v-model="member.id"/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="userpwd" class="form-label">비밀번호 : </label>
|
||||||
|
<input type="text" class="form-control" placeholder="비밀번호..." v-model="member.password"/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="pwdcheck" class="form-label">비밀번호확인 : </label>
|
||||||
|
<input type="text" class="form-control" id="pwdcheck" placeholder="비밀번호확인..." />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="emailid" class="form-label">이메일 : </label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" placeholder="이메일아이디" v-model="email_id"/>
|
||||||
|
<span class="input-group-text">@</span>
|
||||||
|
<select class="form-select" aria-label="이메일 도메인 선택" v-model="email_domain">
|
||||||
|
<option selected>선택</option>
|
||||||
|
<option value="ssafy.com">싸피</option>
|
||||||
|
<option value="google.com">구글</option>
|
||||||
|
<option value="naver.com">네이버</option>
|
||||||
|
<option value="kakao.com">카카오</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto text-center">
|
||||||
|
<button type="button" class="btn btn-outline-primary mb-3" @click="onSubmit">회원가입</button>
|
||||||
|
<button type="button" class="btn btn-outline-success ms-1 mb-3">초기화</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
@ -2,13 +2,20 @@ import './assets/main.css';
|
|||||||
|
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import { createPinia } from 'pinia';
|
import { createPinia } from 'pinia';
|
||||||
|
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
|
||||||
|
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
const pinia = createPinia();
|
||||||
|
|
||||||
|
pinia.use(piniaPluginPersistedstate);
|
||||||
|
|
||||||
app.use(createPinia());
|
app.use(createPinia());
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
|
||||||
app.mount('#app');
|
// app.mount('#app');
|
||||||
|
router.isReady().then(() => {
|
||||||
|
app.mount("#app");
|
||||||
|
});
|
||||||
|
@ -1,6 +1,27 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router';
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
import HomeView from '@/views/HomeView.vue';
|
import HomeView from '@/views/HomeView.vue';
|
||||||
|
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
|
||||||
|
import { useMemberStore } from "@/stores/member";
|
||||||
|
|
||||||
|
const onlyAuthUser = async (to, from, next) => {
|
||||||
|
const memberStore = useMemberStore();
|
||||||
|
const { userInfo, isValidToken } = storeToRefs(memberStore);
|
||||||
|
const { getUserInfo } = memberStore;
|
||||||
|
|
||||||
|
let token = sessionStorage.getItem("accessToken");
|
||||||
|
|
||||||
|
if (userInfo.value != null && token) {
|
||||||
|
await getUserInfo(token);
|
||||||
|
}
|
||||||
|
if (!isValidToken.value || userInfo.value === null) {
|
||||||
|
next({ name: "user-login" });
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
@ -9,6 +30,34 @@ const router = createRouter({
|
|||||||
name: 'home',
|
name: 'home',
|
||||||
component: HomeView,
|
component: HomeView,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/user",
|
||||||
|
name: "user",
|
||||||
|
component: () => import("@/views/TheUserView.vue"),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "login",
|
||||||
|
name: "user-login",
|
||||||
|
component: () => import("@/components/users/UserLogin.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "join",
|
||||||
|
name: "user-join",
|
||||||
|
component: () => import("@/components/users/UserRegister.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "mypage",
|
||||||
|
name: "user-mypage",
|
||||||
|
beforeEnter: onlyAuthUser,
|
||||||
|
component: () => import("@/components/users/UserMyPage.vue"),
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// path: "modify/:userid",
|
||||||
|
// name: "user-modify",
|
||||||
|
// component: () => import("@/components/users/UserModify.vue"),
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/board',
|
path: '/board',
|
||||||
component: () => import('@/views/BoardView.vue'),
|
component: () => import('@/views/BoardView.vue'),
|
||||||
|
139
src/stores/member.js
Normal file
139
src/stores/member.js
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import { ref } from "vue"
|
||||||
|
import { useRouter } from "vue-router"
|
||||||
|
import { defineStore } from "pinia"
|
||||||
|
import { jwtDecode } from "jwt-decode"
|
||||||
|
|
||||||
|
import { userConfirm, findById, tokenRegeneration, logout } from "@/api/member"
|
||||||
|
import { httpStatusCode } from "@/utils/http-status"
|
||||||
|
|
||||||
|
export const useMemberStore = defineStore("memberStore", () => {
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const isLogin = ref(false)
|
||||||
|
const isLoginError = ref(false)
|
||||||
|
const userInfo = ref(null)
|
||||||
|
const isValidToken = ref(false)
|
||||||
|
|
||||||
|
const userLogin = async (loginUser) => {
|
||||||
|
await userConfirm(
|
||||||
|
loginUser,
|
||||||
|
(response) => {
|
||||||
|
if (response.status === httpStatusCode.CREATE) {
|
||||||
|
console.log("로그인 성공!!!!")
|
||||||
|
let { data } = response
|
||||||
|
let accessToken = data["access-token"]
|
||||||
|
let refreshToken = data["refresh-token"]
|
||||||
|
isLogin.value = true
|
||||||
|
isLoginError.value = false
|
||||||
|
isValidToken.value = true
|
||||||
|
sessionStorage.setItem("accessToken", accessToken)
|
||||||
|
sessionStorage.setItem("refreshToken", refreshToken)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.log("로그인 실패!!!!")
|
||||||
|
isLogin.value = false
|
||||||
|
isLoginError.value = true
|
||||||
|
isValidToken.value = false
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getUserInfo = async (token) => {
|
||||||
|
let decodeToken = jwtDecode(token)
|
||||||
|
console.log(decodeToken)
|
||||||
|
await findById(
|
||||||
|
decodeToken.userId,
|
||||||
|
(response) => {
|
||||||
|
if (response.status === httpStatusCode.OK) {
|
||||||
|
userInfo.value = response.data.userInfo
|
||||||
|
} else {
|
||||||
|
console.log("유저 정보 없음!!!!")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async (error) => {
|
||||||
|
console.error(
|
||||||
|
"g[토큰 만료되어 사용 불가능.] : ",
|
||||||
|
error.response.status,
|
||||||
|
error.response.statusText
|
||||||
|
)
|
||||||
|
isValidToken.value = false
|
||||||
|
|
||||||
|
await tokenRegenerate()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenRegenerate = async () => {
|
||||||
|
await tokenRegeneration(
|
||||||
|
JSON.stringify(userInfo.value),
|
||||||
|
(response) => {
|
||||||
|
if (response.status === httpStatusCode.CREATE) {
|
||||||
|
let accessToken = response.data["access-token"]
|
||||||
|
sessionStorage.setItem("accessToken", accessToken)
|
||||||
|
isValidToken.value = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async (error) => {
|
||||||
|
// HttpStatus.UNAUTHORIZE(401) : RefreshToken 기간 만료 >> 다시 로그인!!!!
|
||||||
|
if (error.response.status === httpStatusCode.UNAUTHORIZED) {
|
||||||
|
// 다시 로그인 전 DB에 저장된 RefreshToken 제거.
|
||||||
|
await logout(
|
||||||
|
userInfo.value.userid,
|
||||||
|
(response) => {
|
||||||
|
if (response.status === httpStatusCode.OK) {
|
||||||
|
console.log("리프레시 토큰 제거 성공")
|
||||||
|
} else {
|
||||||
|
console.log("리프레시 토큰 제거 실패")
|
||||||
|
}
|
||||||
|
alert("RefreshToken 기간 만료!!! 다시 로그인해 주세요.")
|
||||||
|
isLogin.value = false
|
||||||
|
userInfo.value = null
|
||||||
|
isValidToken.value = false
|
||||||
|
router.push({ name: "user-login" })
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.error(error)
|
||||||
|
isLogin.value = false
|
||||||
|
userInfo.value = null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const userLogout = async () => {
|
||||||
|
console.log("로그아웃 아이디 : " + userInfo.value.userId)
|
||||||
|
await logout(
|
||||||
|
userInfo.value.userId,
|
||||||
|
(response) => {
|
||||||
|
if (response.status === httpStatusCode.OK) {
|
||||||
|
isLogin.value = false
|
||||||
|
userInfo.value = null
|
||||||
|
isValidToken.value = false
|
||||||
|
|
||||||
|
sessionStorage.removeItem("accessToken")
|
||||||
|
sessionStorage.removeItem("refreshToken")
|
||||||
|
} else {
|
||||||
|
console.error("유저 정보 없음!!!!")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isLogin,
|
||||||
|
isLoginError,
|
||||||
|
userInfo,
|
||||||
|
isValidToken,
|
||||||
|
userLogin,
|
||||||
|
getUserInfo,
|
||||||
|
tokenRegenerate,
|
||||||
|
userLogout,
|
||||||
|
}
|
||||||
|
})
|
19
src/stores/menu.js
Normal file
19
src/stores/menu.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { ref } from "vue";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
|
||||||
|
export const useMenuStore = defineStore("menuStore", () => {
|
||||||
|
const menuList = ref([
|
||||||
|
{ name: "로그인", show: true, routeName: "user-login" },
|
||||||
|
{ name: "회원가입", show: true, routeName: "user-join" },
|
||||||
|
{ name: "내정보", show: false, routeName: "user-mypage" },
|
||||||
|
{ name: "로그아웃", show: false, routeName: "user-logout" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const changeMenuState = () => {
|
||||||
|
menuList.value = menuList.value.map((item) => ({ ...item, show: !item.show }));
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
menuList,
|
||||||
|
changeMenuState,
|
||||||
|
};
|
||||||
|
});
|
11
src/utils/http-status.js
Normal file
11
src/utils/http-status.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// HTTP Status Code
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
|
||||||
|
export const httpStatusCode = {
|
||||||
|
OK: 200,
|
||||||
|
CREATE: 201,
|
||||||
|
NOCONTENT: 204,
|
||||||
|
UNAUTHORIZED: 401,
|
||||||
|
FORBIDDEN: 403,
|
||||||
|
NOTFOUND: 404,
|
||||||
|
};
|
||||||
|
|
14
src/views/TheUserView.vue
Normal file
14
src/views/TheUserView.vue
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<script setup></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="container text-center mt-3">
|
||||||
|
<div class="alert alert-primary" role="alert">Member Service</div>
|
||||||
|
<router-view></router-view>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
mark.orange {
|
||||||
|
background: linear-gradient(to top, rgb(243, 164, 18) 20%, transparent 30%);
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user