Skip to content

Commit

Permalink
Merge pull request #235 from beyond-sw-camp/feat/quotation/list
Browse files Browse the repository at this point in the history
feat: Frontend - 견적서 목록 조회 기능 추가
  • Loading branch information
AYeong-Jeon authored Dec 18, 2024
2 parents f925f7f + f5550d7 commit 39e2899
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 46 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
env.properties
.idea
.env
.idea
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
package error.pirate.backend.quotation.command.domain.aggregate.entity;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.Arrays;
import java.util.List;

@Getter
@RequiredArgsConstructor
public enum QuotationStatus {
BEFORE, // 결재전
AFTER, // 결재후
REFUSAL, // 반려
DELETE // 삭제
BEFORE("결재 전"),
AFTER("결재 후"),
REFUSAL("반려"),
DELETE("삭제");

private final String value;

@Getter
public static class QuotationStatusResponse {
private final String key;
private final String value;

public QuotationStatusResponse(String key, QuotationStatus quotationStatus) {
this.key = key;
this.value = quotationStatus.getValue();
}
}

public static List<QuotationStatusResponse> readQuotationStatusList() {
return Arrays.stream(QuotationStatus.class.getEnumConstants()).map(key ->
new QuotationStatusResponse(key.toString(), QuotationStatus.valueOf(key.toString()))).toList();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package error.pirate.backend.quotation.query.controller;

import error.pirate.backend.quotation.command.domain.aggregate.entity.QuotationStatus;
import error.pirate.backend.quotation.query.dto.QuotationListResponse;
import error.pirate.backend.quotation.query.dto.QuotationResponse;
import error.pirate.backend.quotation.query.dto.QuotationSituationResponse;
Expand All @@ -8,6 +9,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

Expand All @@ -30,7 +32,7 @@ public ResponseEntity<QuotationListResponse> readQuotationList(
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
@RequestParam(required = false) String clientName,
@RequestParam(required = false) String quotationStatus) {
@RequestParam(required = false) List<String> quotationStatus) {

return ResponseEntity.ok(quotationQueryService.readQuotationList(
page, size, startDate, endDate, clientName, quotationStatus));
Expand All @@ -54,4 +56,10 @@ public ResponseEntity<QuotationSituationResponse> readQuotationSituation(
return ResponseEntity.ok(quotationQueryService.readQuotationSituation(
startDate, endDate, clientName));
}

@GetMapping("/status")
@Operation(summary = "견적서 상태 분류 조회")
public ResponseEntity<List<QuotationStatus.QuotationStatusResponse>> readQuotationStatus() {
return ResponseEntity.ok(QuotationStatus.readQuotationStatusList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ List<QuotationListItemDTO> selectQuotationList(
@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate,
@Param("clientName") String clientName,
@Param("quotationStatus") String quotationStatus);
@Param("quotationStatus") List<String> quotationStatus);

// 견적서 개수 목록 조회
int countQuotationList(
@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate,
@Param("clientName") String clientName,
@Param("quotationStatus") String quotationStatus);
@Param("quotationStatus") List<String> quotationStatus);

// 견적서 상세 조회
QuotationDTO selectQuotation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class QuotationQueryService {
public QuotationListResponse readQuotationList(
Integer page, Integer size,
LocalDate startDate, LocalDate endDate,
String clientName, String quotationStatus) {
String clientName, List<String> quotationStatus) {

// 견적서 목록 조회
List<QuotationListItemDTO> quotationList = quotationMapper.selectQuotationList(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
AND tb_client.client_name LIKE CONCAT('%', #{clientName}, '%')
</if>
<if test="quotationStatus != null">
AND tb_quotation.quotation_status LIKE #{quotationStatus}
AND tb_quotation.quotation_status IN
<foreach collection="quotationStatus" item="item" open="(" separator="," close=")">#{item}</foreach>
</if>
LIMIT #{limit} OFFSET #{offset}
</select>
Expand All @@ -52,7 +53,8 @@
AND tb_client.client_name LIKE CONCAT('%', #{clientName}, '%')
</if>
<if test="quotationStatus != null">
AND tb_quotation.quotation_status LIKE #{quotationStatus}
AND tb_quotation.quotation_status IN
<foreach collection="quotationStatus" item="item" open="(" separator="," close=")">#{item}</foreach>
</if>
</select>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDate;
import java.util.List;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.*;
Expand Down Expand Up @@ -37,7 +38,7 @@ private static Stream<Arguments> readQuotationListParam() {
@MethodSource("readQuotationListParam")
void readQuotationList(Integer page, Integer size,
LocalDate startDate, LocalDate endDate,
String clientName, String quotationStatus) {
String clientName, List<String> quotationStatus) {

assertDoesNotThrow(() -> quotationQueryService.readQuotationList(
page, size, startDate, endDate, clientName, quotationStatus));
Expand Down
15 changes: 15 additions & 0 deletions SCM/frontend/src/axios.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import axios from 'axios';

const instance = axios.create({
baseURL: import.meta.env.VITE_API_URL,
// headers: {
// common: {
// 'Authorization': 'AUTH_TOKEN'
// },
// post: {
// 'Content-Type': 'application/x-www-form-urlencoded'
// }
// }
});

export default instance;
4 changes: 3 additions & 1 deletion SCM/frontend/src/components/common/Header.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import statisticsIcon from '@/assets/statisticsIcon.svg'
</b-nav-item-dropdown>
<b-nav-item-dropdown>
<template #button-content><salesIcon class="icon" />영업관리</template>
<b-dropdown-item href="/quotation">견적 관리</b-dropdown-item>
<RouterLink to="/quotation" style="text-decoration-line: none;">
<b-dropdown-item>견적 관리</b-dropdown-item>
</RouterLink>
<b-dropdown-item href="#">주문서 관리</b-dropdown-item>
<b-dropdown-item href="#">판매 관리</b-dropdown-item>
<b-dropdown-item href="#"><RouterLink to="/shipping-instruction" active-class="active" replace>출하지시서 관리</RouterLink></b-dropdown-item>
Expand Down
137 changes: 104 additions & 33 deletions SCM/frontend/src/components/quotation/QuotationList.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,53 @@
<script setup>
import { ref } from 'vue';
import { ref, onMounted, watch } from 'vue';
import searchIcon from "@/assets/searchIcon.svg"
import axios from "@/axios"
// 임시 데이터 셋
const rows = ref(123);
const perPage = ref(10);
const quotationList = ref([]);
const quotationStatusList = ref([]);
const currentPage = ref(1);
const items = ref([
{ id: '2024/12/03 - 2', item: '김치볶음밥, 치즈볶음밥, 그냥볶음밥', client: '대한항공', quotation_date: '2024/1/1', status: '주문 전' },
{ id: '2024/12/03 - 1', item: '김치볶음밥, 치즈볶음밥, 그냥볶음밥', client: '아시아나항공', quotation_date: '2024/1/1', status: '주문 후' },
{ id: '2024/12/03 - 1', item: null, client: '아시아나항공', quotation_date: '2024/1/1', status: '주문 후' },
{ id: '2024/12/03 - 1', item: null, client: '아시아나항공', quotation_date: '2024/1/1', status: '주문 후' },
{ id: '위에', client: '거래처', quotation_date: '2024/1/1', status: '반려' },
{ id: '위에', client: '거래처', quotation_date: '2024/1/1', status: '반려' },
{ id: '위에', client: '거래처', quotation_date: '2024/1/1', status: '반려' },
{ id: '위에', client: '거래처', quotation_date: '2024/1/1', status: '반려' },
{ id: '위에', client: '거래처', quotation_date: '2024/1/1', status: '반려' },
{ id: '위에', client: '거래처', quotation_date: '2024/1/1', status: '반려' }
]);
const totalPage = ref(1);
const totalQuotation = ref(0);
const searchPage = ref(1);
const searchSize = ref(10);
const searchStartDate = ref(null);
const searchEndDate = ref(null);
const searchClient = ref(null);
const searchStatus = ref(new Set());
// 견적서 목록 요청
const fetchQuotationList = async () => {
try {
const response = await axios.get(`quotation`, {
params: {
page: searchPage.value,
size: searchSize.value,
startDate: searchStartDate.value,
endDate: searchEndDate.value,
clientName: searchClient.value,
quotationStatus: searchStatus.value.size === 0 ? null : Array.from(searchStatus.value).join(",")
}
});
quotationList.value = response.data.quotation;
currentPage.value = response.data.currentPage;
totalPage.value = response.data.totalPages;
totalQuotation.value = response.data.totalQuotation;
} catch (error) {
console.log(`견적서 목록 요청 실패`, error);
}
}
// 견적서 상태 분류 목록 요청
const fetchQuotationStatus = async () => {
try {
const response = await axios.get(`quotation/status`);
quotationStatusList.value = response.data;
} catch (error) {
console.log(`견적서 상태 분류 목록 요청 실패`, error);
}
}
// 선택한 Item 확장 | 축소
function itemExtend(event) {
Expand All @@ -41,6 +72,31 @@ function addItemCard() {
}
onMounted(() => {
fetchQuotationList();
fetchQuotationStatus();
});
watch([searchStartDate, searchEndDate, searchStatus.value], () => {
search();
});
watch(searchPage, () => {
fetchQuotationList();
});
function statusCheck(status) {
searchStatus.value.has(status) ? searchStatus.value.delete(status)
: searchStatus.value.add(status);
}
function search() {
searchPage.value = 1;
fetchQuotationList();
}
</script>

<template>
Expand All @@ -49,31 +105,31 @@ function addItemCard() {
<div class="side-box card">
<div class="card-body">
<p class="card-title">견적일</p>
<input type="date" style="max-width: 40%;"/> ~ <input type="date" style="max-width: 40%;"/>
<input type="date" v-model="searchStartDate" style="max-width: 40%;"/> ~ <input type="date" v-model="searchEndDate" style="max-width: 40%;"/>
</div>
</div>
<div class="side-box card">
<div class="card-body">
<p class="card-title">거래처명</p>
<b-input-group class="mt-3">
<b-form-input></b-form-input>
<b-input-group-text><svg width="1em" id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M344.5,298c15-23.6,23.8-51.6,23.8-81.7c0-84.1-68.1-152.3-152.1-152.3C132.1,64,64,132.2,64,216.3 c0,84.1,68.1,152.3,152.1,152.3c30.5,0,58.9-9,82.7-24.4l6.9-4.8L414.3,448l33.7-34.3L339.5,305.1L344.5,298z M301.4,131.2 c22.7,22.7,35.2,52.9,35.2,85c0,32.1-12.5,62.3-35.2,85c-22.7,22.7-52.9,35.2-85,35.2c-32.1,0-62.3-12.5-85-35.2 c-22.7-22.7-35.2-52.9-35.2-85c0-32.1,12.5-62.3,35.2-85c22.7-22.7,52.9-35.2,85-35.2C248.5,96,278.7,108.5,301.4,131.2z"/></svg></b-input-group-text>
<b-form-input v-model="searchClient"></b-form-input>
<b-button variant="light" class="button" @click="search()"><searchIcon class="icon"/></b-button>
</b-input-group>
</div>
</div>
<div class="side-box card">
<div class="card-body">
<p class="card-title">견적서 상태</p>
<b-form-checkbox>주문 전</b-form-checkbox>
<b-form-checkbox>주문 후</b-form-checkbox>
<b-form-checkbox>반려</b-form-checkbox>
<template v-for="quotationStatus in quotationStatusList">
<b-form-checkbox @click="statusCheck(quotationStatus.key)">{{ quotationStatus.value }}</b-form-checkbox>
</template>
</div>
</div>
</div>
<div class="col-md-9">
<div style="width: 90%;">
<div class="d-flex justify-content-between">
<div>검색결과: {{ rows }}개</div>
<div>검색결과: {{ totalQuotation }}개</div>
<b-button variant="light" size="sm" class="button">견적서 등록</b-button>
</div>
<div class="list-headline row">
Expand All @@ -82,22 +138,27 @@ function addItemCard() {
<div class="list-headvalue col-2">견적일</div>
<div class="list-headvalue col-2">상태</div>
</div>
<template v-if="quotationList.length > 0">
<div style="max-height: 600px; overflow-y: auto;">
<div v-for="item in items" :key="item.id" class="list-line row" @click="itemExtend">
<div class="col-6">{{ item.id }}<br>
<div v-if="!item.item"><br></div>
<div v-else>{{ item.item }}</div></div>
<div class="list-value col-2">{{ item.client }}</div>
<div class="list-value col-2">{{ item.quotation_date }}</div>
<div class="list-value col-2">{{ item.status }}</div>
<div v-for="quotation in quotationList" :key="quotation.quotationSeq" class="list-line row" @click="itemExtend">
<div class="col-6">{{ quotation.quotationName }}<br>
<div v-if="!quotation.itemName"><br></div>
<div v-else>{{ quotation.itemName }}</div></div>
<div class="list-value col-2">{{ quotation.clientName }}</div>
<div class="list-value col-2">{{ quotation.quotationQuotationDate }}</div>
<div class="list-value col-2">{{ quotation.quotationStatus }}</div>
</div>
</div>
</template>
<template v-else>
<b-card-text class="no-list-text">해당 검색 조건에 부합하는 견적서가 존재하지 않습니다.</b-card-text>
</template>
</div>
<div class="pagenation">
<b-pagination
v-model="currentPage"
:total-rows="rows"
:per-page="perPage">
v-model="searchPage"
:total-rows="totalQuotation"
:per-page="searchSize">
</b-pagination>
</div>
</div>
Expand Down Expand Up @@ -154,4 +215,14 @@ div {
margin-top: 20px;
}
.no-list-text {
text-align: center;
margin-top: 100px;
}
.icon {
width: 20px;
height: 20px;
}
</style>
1 change: 1 addition & 0 deletions SCM/frontend/src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const routes = [
},
{
path: "/quotation",
name: "Quotation",
component: () => import("@/views/quotation/QuotationListView.vue")
},
{
Expand Down

0 comments on commit 39e2899

Please sign in to comment.