feat: Update search bar component

This commit is contained in:
jhynsoo 2024-05-22 11:51:49 +09:00
parent 9e3574cf36
commit 0c5ad07f0e
10 changed files with 123 additions and 129 deletions

View File

@ -35,6 +35,10 @@ button {
transform 0.25s;
}
button:active {
transform: scale(0.95);
}
button:disabled {
cursor: initial;
}

View File

@ -28,13 +28,12 @@ onMounted(() => {
oldMarkers.forEach((marker) => marker.setMap(null));
newMarkers.forEach((marker) => marker.setMap(map));
target;
setTarget(newMarkers[0].getPosition());
if (!oldMarkers[0] || !newMarkers[0].getPosition().equals(oldMarkers[0].getPosition())) {
setTarget(newMarkers[0].getPosition());
}
});
watch(target, (newTarget, oldTarget) => {
if (oldTarget && newTarget.La === oldTarget.La && newTarget.Ma === oldTarget.Ma) {
return;
}
watch(target, (newTarget) => {
map.panTo(newTarget);
});
});

View File

@ -0,0 +1,51 @@
<script setup>
import TextButton from './TextButton.vue';
const props = defineProps({
name: String,
onSubmit: Function,
});
const { name, onSubmit } = props;
const model = defineModel();
</script>
<template>
<form @submit.prevent="onSubmit" class="search-box">
<input type="text" :name="name" v-model.lazy="model" placeholder="검색어를 입력하세요" />
<TextButton @click="onSubmit">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="bi bi-search"
viewBox="0 0 16 16"
part="svg"
>
<path
d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"
></path>
</svg>
</TextButton>
</form>
</template>
<style scoped>
.search-box {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
box-shadow: var(--shadow);
border-radius: 12px;
border: 1px solid var(--color-border);
padding: 12px;
}
input {
padding: 0 8px;
border: none;
width: 100%;
outline: none;
}
</style>

View File

@ -1,9 +1,9 @@
<!-- eslint-disable vue/multi-word-component-names -->
<script setup>
import { computed, onMounted, ref } from 'vue';
import { computed, onMounted, ref, toRefs } from 'vue';
import FilledButton from './FilledButton.vue';
const { placeholder, options, optionName, disabled } = defineProps({
const props = defineProps({
placeholder: {
type: String,
default: '----',
@ -17,20 +17,27 @@ const { placeholder, options, optionName, disabled } = defineProps({
default: 'value',
},
modelValue: {
sidoCode: Number,
type: Object,
default: null,
},
defaultIndex: {
type: Number || null,
default: null,
},
disabled: {
type: Boolean,
default: false,
},
});
const { placeholder, optionName } = props;
const { modelValue, options, disabled } = toRefs(props);
const updateModel = defineEmits(['update:modelValue']);
const body = document.querySelector('body');
const buttonRef = ref(null);
const isOpen = ref(false);
const menuStyle = ref('');
const selected = ref(null);
const selected = modelValue;
onMounted(() => {
const rect = buttonRef.value.$el.getBoundingClientRect();
@ -38,7 +45,7 @@ onMounted(() => {
menuStyle.value = `transform: translate(${rect.left}px, ${rect.bottom + 4}px)`;
});
const selectedName = computed(() => selected.value?.[optionName] ?? selected.value);
const selectedName = computed(() => modelValue.value?.[optionName] ?? modelValue.value);
function handleOpen() {
isOpen.value = true;
@ -60,7 +67,6 @@ function handleClose({ target }) {
}
function handleSelect(option) {
selected.value = option;
updateModel('update:modelValue', option);
closeMenu();
}
@ -73,7 +79,7 @@ function handleSelect(option) {
ref="buttonRef"
@mousedown="handleOpen"
>
<span>{{ selectedName ?? placeholder }}</span>
<div class="select-label">{{ selectedName ?? placeholder }}</div>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
@ -118,6 +124,12 @@ function handleSelect(option) {
justify-content: space-between;
align-items: center;
border: 1px solid var(--color-border);
overflow: hidden;
}
.select-label {
overflow: hidden;
white-space: nowrap;
}
.active {

View File

@ -1,20 +0,0 @@
<script setup>
import { ref} from 'vue'
defineProps({ selectOptions: Array })
const emit = defineEmits(["onKeySelect"])
const key = ref('all')
const onSelect = () => {
console.log('vselect................... key:', key.value)
emit('onKeySelect', key.value)
}
</script>
<template>
<select class="form-select" v-model="key" @change="onSelect">
<option v-for="(k, index) in selectOptions" :key="index" :value="k.value">
{{k.text}}
</option>
</select>
</template>
<style scoped>
</style>

View File

@ -60,6 +60,11 @@ function handleClick() {
border: 1px solid var(--color-border);
background-color: var(--color-background);
cursor: pointer;
transition: transform 0.25s;
}
.attraction:active {
transform: scale(0.98);
}
.info {

View File

@ -1,11 +1,12 @@
<script setup>
import { ref, watch } from 'vue';
import TextButton from '../common/TextButton.vue';
import { reactive, ref, watch } from 'vue';
import axios from 'axios';
import Select from '../common/Select.vue';
import { useAttractionStore } from '@/stores/attractionStore';
import SearchBox from '../common/SearchBox.vue';
const contentTypeList = [
const contentTypeList = reactive([
{ value: 0, name: '전체' },
{ value: 12, name: '관광지' },
{ value: 14, name: '문화시설' },
{ value: 15, name: '축제공연행사' },
@ -14,31 +15,37 @@ const contentTypeList = [
{ value: 32, name: '숙박' },
{ value: 38, name: '쇼핑' },
{ value: 39, name: '음식점' },
];
]);
const { search, setQueryString } = useAttractionStore();
const sidoList = ref([]);
const gugunList = ref([]);
const sido = ref({ sidoCode: 0 });
const gugun = ref({ gugunCode: 0 });
const contentType = ref(0);
const sido = ref(null);
const gugun = ref(null);
const contentType = ref(null);
const keyword = ref('');
watch(sido, ({ sidoCode }) => {
gugun.value = null;
if (sidoCode === 0) {
return;
}
axios.get(`//localhost:8000/area/gugun?sidoCode=${sidoCode}`).then(({ data }) => {
gugunList.value = data;
gugunList.value = [{ gugunCode: 0, gugunName: '전체' }, ...data];
});
});
if (sidoList.value.length === 0) {
axios.get('//localhost:8000/area/sido').then(({ data }) => (sidoList.value = data));
axios
.get('//localhost:8000/area/sido')
.then(({ data }) => (sidoList.value = [{ sidoCode: 0, sidoName: '전체' }, ...data]));
}
function handleSubmit() {
const { sidoCode } = sido.value;
const { gugunCode } = gugun.value;
const areaQuery = `sidoCode=${sidoCode}` + (sidoCode ? `&gugunCode=${gugunCode}` : '');
const { sidoCode } = sido.value ?? {};
const { gugunCode } = gugun.value ?? {};
const areaQuery = `sidoCode=${sidoCode ?? 0}` + (sidoCode ? `&gugunCode=${gugunCode ?? 0}` : '');
const keywordQuery = `title=${keyword.value}`;
const contentTypeQuery = `contentTypeId=${contentType.value.value}`;
const contentTypeQuery = `contentTypeId=${contentType.value?.value ?? 0}`;
const queryString = [areaQuery, keywordQuery, contentTypeQuery].join('&');
setQueryString(queryString);
@ -54,34 +61,17 @@ function handleSubmit() {
placeholder="시/군/구"
v-model="gugun"
:options="gugunList"
:disabled="gugunList.length === 0"
:disabled="gugunList.length <= 1"
optionName="gugunName"
/>
<Select
placeholder="류"
placeholder="류"
v-model="contentType"
:options="contentTypeList"
optionName="name"
/>
</nav>
<form @submit.prevent="handleSubmit" class="search-box">
<input type="text" name="keyword" v-model.lazy="keyword" placeholder="검색어를 입력하세요" />
<TextButton @click="handleSubmit">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="bi bi-search"
viewBox="0 0 16 16"
part="svg"
>
<path
d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"
></path>
</svg>
</TextButton>
</form>
<SearchBox name="keyword" v-model.lazy="keyword" :onSubmit="handleSubmit" />
</div>
</template>
@ -101,27 +91,4 @@ function handleSubmit() {
.select-wrapper > * {
width: 100%;
}
.search-box {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
box-shadow: var(--shadow);
border-radius: 12px;
border: 1px solid var(--color-border);
padding: 12px;
}
input {
padding: 0 8px;
border: none;
width: 100%;
outline: none;
}
button {
display: flex;
align-items: center;
}
</style>

View File

@ -1,22 +1,22 @@
import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '@/views/HomeView.vue';
import { storeToRefs } from "pinia";
import { storeToRefs } from 'pinia';
import { useMemberStore } from "@/stores/memberStore";
import { useMemberStore } from '@/stores/memberStore';
const onlyAuthUser = async (to, from, next) => {
const memberStore = useMemberStore();
const { userInfo, isValidToken } = storeToRefs(memberStore);
const { getUserInfo } = memberStore;
let token = sessionStorage.getItem("accessToken");
let token = sessionStorage.getItem('accessToken');
if (userInfo.value != null && token) {
await getUserInfo(token);
}
if (!isValidToken.value || userInfo.value === null) {
next({ name: "user-login" });
next({ name: 'user-login' });
} else {
next();
}
@ -31,25 +31,25 @@ const router = createRouter({
component: HomeView,
},
{
path: "/user",
name: "user",
component: () => import("@/views/TheUserView.vue"),
path: '/user',
name: 'user',
component: () => import('@/views/TheUserView.vue'),
children: [
{
path: "login",
name: "user-login",
component: () => import("@/components/users/UserLogin.vue"),
path: 'login',
name: 'user-login',
component: () => import('@/components/users/UserLogin.vue'),
},
{
path: "join",
name: "user-join",
component: () => import("@/components/users/UserRegister.vue"),
path: 'join',
name: 'user-join',
component: () => import('@/components/users/UserRegister.vue'),
},
{
path: "mypage",
name: "user-mypage",
path: 'mypage',
name: 'user-mypage',
beforeEnter: onlyAuthUser,
component: () => import("@/components/users/UserMyPage.vue"),
component: () => import('@/components/users/UserMyPage.vue'),
},
// {
// path: "modify/:userid",

View File

@ -11,6 +11,7 @@ export const useAttractionStore = defineStore('attraction', () => {
function setQueryString(value) {
queryString.value = value;
page.value = 0;
}
function search(preventClear = false) {

View File

@ -1,25 +0,0 @@
<template>
<div id="app">
<TravelChatBot />
</div>
</template>
<script>
import TravelChatBot from '@/components/chatbot/TravelChatBot.vue';
export default {
name: 'App',
components: {
TravelChatBot,
},
};
</script>
<style>
body {
font-family: Avenir, Helvetica, Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f5f5f5;
}
</style>