Merge branch 'master' into 상품상세_주문하기
This commit is contained in:
19
customer-vue/src/api/notification.js
Normal file
19
customer-vue/src/api/notification.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import axios from "axios";
|
||||
|
||||
const url = process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL + "/notification-service";
|
||||
|
||||
export default {
|
||||
requestNotification() {
|
||||
return axios.get(url + "/notifications");
|
||||
},
|
||||
patchNotification(id, isRead) {
|
||||
const body = {
|
||||
read: isRead
|
||||
}
|
||||
|
||||
return axios.patch(url + "/notification/" + id, body)
|
||||
},
|
||||
countsNotification() {
|
||||
return axios.get(url + "/api/notification/counts");
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,45 @@
|
||||
import axios from "axios";
|
||||
|
||||
export default {
|
||||
requestNearbyStore(latitude, longitude, storeName, page, size) {
|
||||
const options = {
|
||||
params: {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
storeName: storeName,
|
||||
page: page,
|
||||
size: size
|
||||
}
|
||||
requestNearbyStore(latitude, longitude, storeName, page, size) {
|
||||
const options = {
|
||||
params: {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
storeName: storeName,
|
||||
page: page,
|
||||
size: size
|
||||
}
|
||||
return axios.get(process.env.VUE_APP_STORE_API_URL + '/store/search', options);
|
||||
},
|
||||
getCategoryList(){
|
||||
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/category/');
|
||||
},
|
||||
fetchItem(itemId){
|
||||
return axios.get(process.env.VUE_APP_STORE_API_URL+'/item/'+itemId)
|
||||
},
|
||||
getFavoriteStore(latitude, longitude,){
|
||||
const options = {
|
||||
params: {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
}
|
||||
}
|
||||
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/api/customer/store/favorite',options)
|
||||
},
|
||||
|
||||
}
|
||||
return axios.get(process.env.VUE_APP_STORE_API_URL + '/store/search', options);
|
||||
},
|
||||
getItemById(itemId){
|
||||
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/item/'+itemId)
|
||||
},
|
||||
getFavoriteStore(latitude, longitude,){
|
||||
const options = {
|
||||
params: {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
}
|
||||
}
|
||||
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/api/customer/store/favorite',options)
|
||||
},
|
||||
getCategoryList(){
|
||||
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/category/');
|
||||
},
|
||||
fetchItem(itemId){
|
||||
return axios.get(process.env.VUE_APP_STORE_API_URL+'/item/'+itemId)
|
||||
},
|
||||
requestCategoriesWithItem(storeId) {
|
||||
const options = {
|
||||
params: {
|
||||
"storeId": storeId
|
||||
}
|
||||
}
|
||||
return axios.get(process.env.VUE_APP_STORE_API_URL + "/categories", options);
|
||||
},
|
||||
requestStore(storeId) {
|
||||
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL + "/store-service/store/" + storeId);
|
||||
}
|
||||
}
|
||||
@@ -6,29 +6,43 @@
|
||||
elevation="1"
|
||||
|
||||
>
|
||||
<v-app-bar-nav-icon>
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-app-bar-nav-icon>
|
||||
<v-spacer></v-spacer>
|
||||
<v-toolbar-title>
|
||||
<v-img :src="require('@/assets/just-logo.png')"></v-img>
|
||||
</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<v-row class="d-flex" style="max-width: 768px;display: table; margin-left: auto; margin-right: auto; width: 100%" align="center">
|
||||
<v-app-bar-nav-icon>
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-app-bar-nav-icon>
|
||||
<v-spacer></v-spacer>
|
||||
<v-toolbar-title>
|
||||
<v-img :src="require('@/assets/just-logo.png')"></v-img>
|
||||
</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon>
|
||||
<v-icon>mdi-magnify</v-icon>
|
||||
<v-btn
|
||||
color="white"
|
||||
elevation="0"
|
||||
@click="goNotification"
|
||||
>
|
||||
<v-badge
|
||||
:content="notificationCounts"
|
||||
:value="notificationCounts"
|
||||
color="orange"
|
||||
overlap
|
||||
>
|
||||
<v-icon>mdi-bell-outline</v-icon>
|
||||
</v-badge>
|
||||
</v-btn>
|
||||
</v-row>
|
||||
</v-app-bar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AppNavigation"
|
||||
name: "AppNavigation",
|
||||
props: ["notificationCounts"],
|
||||
methods: {
|
||||
goNotification: function() {
|
||||
this.$router.push('/notification');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
32
customer-vue/src/components/StoreNavigation.vue
Normal file
32
customer-vue/src/components/StoreNavigation.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<v-app-bar
|
||||
app
|
||||
dense
|
||||
color="white"
|
||||
elevation="1"
|
||||
hide-on-scroll
|
||||
absolute
|
||||
>
|
||||
<v-app-bar-nav-icon>
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-app-bar-nav-icon>
|
||||
<v-spacer></v-spacer>
|
||||
<v-toolbar-title>
|
||||
<b>{{store.name}}</b>
|
||||
</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon>
|
||||
<v-icon>mdi-magnify</v-icon>
|
||||
</v-btn>
|
||||
</v-app-bar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "StoreNavigation",
|
||||
props: ["store"],
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -4,6 +4,7 @@ import jwt from "@/common/jwt";
|
||||
import auth from "@/api/auth";
|
||||
|
||||
import HomeLayout from '../views/Layout/HomeLayout.vue';
|
||||
import StoreLayout from "@/views/Layout/StoreLayout";
|
||||
const ACCESS_TOKEN_NAME = "accessToken";
|
||||
const EXPIRED_TIME_NAME = "expiredTime";
|
||||
|
||||
@@ -54,6 +55,11 @@ const routes = [
|
||||
name: 'favorite-store',
|
||||
component: () => import('../views/FavoriteStore')
|
||||
},
|
||||
{
|
||||
path: "/notification",
|
||||
name: 'notification',
|
||||
component: () => import('../views/NotificationView')
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
@@ -71,8 +77,20 @@ const routes = [
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
path: '/store',
|
||||
redirect: 'store',
|
||||
beforeEnter: authCheck,
|
||||
component: StoreLayout,
|
||||
children: [
|
||||
{
|
||||
path: "/store/:storeId",
|
||||
name: "store",
|
||||
component: () => import('../views/StoreView'),
|
||||
props: true
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/auth',
|
||||
name: 'auth',
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<app-navigation></app-navigation>
|
||||
<app-navigation
|
||||
v-bind:notificationCounts="notificationCounts"></app-navigation>
|
||||
<v-main>
|
||||
<v-container class="px-8 py-8">
|
||||
<router-view></router-view>
|
||||
<router-view
|
||||
v-on:plusCount="notificationCounts++"
|
||||
v-on:minusCount="notificationCounts--"></router-view>
|
||||
</v-container>
|
||||
</v-main>
|
||||
<bottom-navigation></bottom-navigation>
|
||||
@@ -13,12 +16,27 @@
|
||||
<script>
|
||||
import AppNavigation from "../../components/AppNavigation.vue";
|
||||
import BottomNavigation from "../../components/BottomNavigation.vue";
|
||||
import notificationApi from "../../api/notification";
|
||||
|
||||
export default {
|
||||
name: "HomeLayout",
|
||||
components: {
|
||||
'app-navigation': AppNavigation,
|
||||
"bottom-navigation": BottomNavigation
|
||||
},
|
||||
mounted() {
|
||||
this.searchNotificationCounts();
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
notificationCounts: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
searchNotificationCounts: async function() {
|
||||
const response = await notificationApi.countsNotification();
|
||||
this.notificationCounts = response.data.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
62
customer-vue/src/views/Layout/StoreLayout.vue
Normal file
62
customer-vue/src/views/Layout/StoreLayout.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<store-navigation
|
||||
v-bind:store="store">
|
||||
</store-navigation>
|
||||
<v-main>
|
||||
<v-container class="px-8">
|
||||
<router-view
|
||||
v-on:getStoreId="renderNavigation">
|
||||
</router-view>
|
||||
</v-container>
|
||||
</v-main>
|
||||
<bottom-navigation></bottom-navigation>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StoreNavigation from "../../components/StoreNavigation.vue";
|
||||
import BottomNavigation from "../../components/BottomNavigation.vue";
|
||||
|
||||
import storeApi from "../../api/store";
|
||||
|
||||
export default {
|
||||
name: "StoreLayout",
|
||||
components: {
|
||||
'store-navigation': StoreNavigation,
|
||||
"bottom-navigation": BottomNavigation
|
||||
},
|
||||
data: () => ({
|
||||
store: {
|
||||
id: '',
|
||||
name: ''
|
||||
}
|
||||
}),
|
||||
methods: {
|
||||
renderNavigation: function(storeId) {
|
||||
this.store.id = storeId;
|
||||
this.getStore();
|
||||
},
|
||||
getStore: async function() {
|
||||
if (!this.store.id) {
|
||||
alert("매장 고유번호가 없습니다. 잠시후에 시도해주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await storeApi.requestStore(this.store.id);
|
||||
this.store = response.data.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
max-width: 768px;
|
||||
background-color: white;
|
||||
height: 100%;
|
||||
}
|
||||
main {
|
||||
background-color: #f2f2f2!important;
|
||||
}
|
||||
</style>
|
||||
84
customer-vue/src/views/NotificationView.vue
Normal file
84
customer-vue/src/views/NotificationView.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div>
|
||||
<template
|
||||
v-for="(item, index) in notifications">
|
||||
<v-list-item three-line :key="item.id">
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{item.title}}</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{item.message}}
|
||||
</v-list-item-subtitle>
|
||||
<v-list-item-subtitle>
|
||||
2022-03-09 14:00
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
|
||||
<v-list-item-action>
|
||||
<v-checkbox
|
||||
disabled
|
||||
v-if="item.prevRead"
|
||||
v-model="item.read"
|
||||
hide-details></v-checkbox>
|
||||
<v-checkbox
|
||||
v-else
|
||||
v-model="item.read"
|
||||
@click="clickRead(item.id, item.read)"
|
||||
hide-details></v-checkbox>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
<v-divider
|
||||
v-if="index < notifications.length - 1"
|
||||
:key="index"
|
||||
></v-divider>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import notificationApi from "@/api/notification";
|
||||
|
||||
export default {
|
||||
name: "NotificationView",
|
||||
mounted() {
|
||||
this.search();
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
notifications: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
search: async function() {
|
||||
const response = await notificationApi.requestNotification();
|
||||
this.render(response.data);
|
||||
},
|
||||
render: function(json) {
|
||||
const notifications = json.data.notifications;
|
||||
notifications.forEach(notification => {
|
||||
this.notifications.push({
|
||||
id: notification.id,
|
||||
message: notification.message,
|
||||
title: notification.title,
|
||||
prevRead: notification.read,
|
||||
read: notification.read
|
||||
});
|
||||
});
|
||||
},
|
||||
clickRead: async function(id, isRead) {
|
||||
await notificationApi.patchNotification(id, isRead);
|
||||
|
||||
if (isRead) {
|
||||
alert("해당 알림은 읽음 처리되었습니다.");
|
||||
this.$emit("minusCount");
|
||||
} else {
|
||||
alert("해당 알림은 읽음 해제 처리되었습니다.");
|
||||
this.$emit("plusCount");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -26,7 +26,10 @@
|
||||
|
||||
<v-row>
|
||||
<v-col v-for="card in cards" v-bind:key="card.storeId" sm="6">
|
||||
<v-card v-bind:data-id="card.storeId">
|
||||
<v-card
|
||||
v-bind:data-id="card.storeId"
|
||||
v-on:click="clickCard(card.storeId)"
|
||||
>
|
||||
<v-img
|
||||
height="180"
|
||||
:src="require('@/assets/store.jpeg')"
|
||||
@@ -147,6 +150,12 @@ export default {
|
||||
favoriteCounts: store.favoriteCounts
|
||||
})
|
||||
});
|
||||
},
|
||||
clickCard(storeId) {
|
||||
this.$router.push({
|
||||
name: "store",
|
||||
params: {storeId: storeId}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
111
customer-vue/src/views/StoreView.vue
Normal file
111
customer-vue/src/views/StoreView.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div>
|
||||
<div id="nav">
|
||||
<v-chip-group
|
||||
mandatory
|
||||
center-active
|
||||
show-arrows
|
||||
active-class="primary--text"
|
||||
v-model="tagIndex"
|
||||
>
|
||||
<v-chip
|
||||
v-for="tag in tags"
|
||||
:key="tag"
|
||||
>
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<div id="content">
|
||||
<div v-for="category in categories" :key="category.id">
|
||||
<h3 ref="focusTag">
|
||||
{{ category.name }}
|
||||
</h3>
|
||||
<br>
|
||||
<v-card
|
||||
class="mx-auto mb-5"
|
||||
outlined
|
||||
v-for="item in category.items"
|
||||
:key="item.id"
|
||||
>
|
||||
<v-list-item three-line>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title class="text-h5 mb-3">
|
||||
{{ item.name }}
|
||||
</v-list-item-title>
|
||||
<div class="text--primary mb-5">
|
||||
{{ item.price }}원
|
||||
</div>
|
||||
<v-list-item-subtitle>
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
|
||||
<v-list-item-avatar
|
||||
tile
|
||||
size="100"
|
||||
>
|
||||
<v-img :src="require('@/assets/store.jpeg')"></v-img>
|
||||
</v-list-item-avatar>
|
||||
</v-list-item>
|
||||
</v-card>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import storeApi from "../api/store";
|
||||
|
||||
export default {
|
||||
name: "StoreView",
|
||||
props: ["storeId"],
|
||||
mounted() {
|
||||
if (!this.storeId) {
|
||||
alert("잘못된 요청입니다.");
|
||||
return;
|
||||
}
|
||||
this.$emit('getStoreId', this.storeId)
|
||||
this.search(this.storeId);
|
||||
},
|
||||
data: () => ({
|
||||
tags: [],
|
||||
categories: [],
|
||||
tagIndex: 0
|
||||
}),
|
||||
watch: {
|
||||
tagIndex: function(newValue) {
|
||||
this.$refs.focusTag[newValue].scrollIntoView({behavior: 'smooth', block: 'center'});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
search: async function(storeId) {
|
||||
const response = await storeApi.requestCategoriesWithItem(storeId);
|
||||
this.render(response.data);
|
||||
},
|
||||
render: function(json) {
|
||||
this.categories = json.data.categories;
|
||||
this.categories.forEach(category => {
|
||||
this.tags.push(category.name);
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#nav {
|
||||
color: white;
|
||||
background-color: white;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
#content {
|
||||
display: block !important;
|
||||
z-index: -1;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user