|
@@ -12,14 +12,6 @@
|
|
|
</option>
|
|
</option>
|
|
|
</select>
|
|
</select>
|
|
|
|
|
|
|
|
- <label class="admin--filter-label">지점</label>
|
|
|
|
|
- <select v-model="filters.branch" class="admin--form-select">
|
|
|
|
|
- <option value="">전체</option>
|
|
|
|
|
- <option v-for="branch in branches" :key="branch.id" :value="branch.id">
|
|
|
|
|
- {{ branch.name }}
|
|
|
|
|
- </option>
|
|
|
|
|
- </select>
|
|
|
|
|
-
|
|
|
|
|
<label class="admin--filter-label">팀</label>
|
|
<label class="admin--filter-label">팀</label>
|
|
|
<select v-model="filters.team" class="admin--form-select">
|
|
<select v-model="filters.team" class="admin--form-select">
|
|
|
<option value="">전체</option>
|
|
<option value="">전체</option>
|
|
@@ -37,7 +29,7 @@
|
|
|
class="admin--form-input"
|
|
class="admin--form-input"
|
|
|
placeholder="이름으로 검색"
|
|
placeholder="이름으로 검색"
|
|
|
@keyup.enter="handleSearch"
|
|
@keyup.enter="handleSearch"
|
|
|
- >
|
|
|
|
|
|
|
+ />
|
|
|
<button class="admin--btn admin--btn-primary" @click="handleSearch">
|
|
<button class="admin--btn admin--btn-primary" @click="handleSearch">
|
|
|
검색
|
|
검색
|
|
|
</button>
|
|
</button>
|
|
@@ -68,7 +60,6 @@
|
|
|
<th>NO</th>
|
|
<th>NO</th>
|
|
|
<th>사진</th>
|
|
<th>사진</th>
|
|
|
<th>전시장</th>
|
|
<th>전시장</th>
|
|
|
- <th>지점</th>
|
|
|
|
|
<th>팀</th>
|
|
<th>팀</th>
|
|
|
<th>이름</th>
|
|
<th>이름</th>
|
|
|
<th>직책</th>
|
|
<th>직책</th>
|
|
@@ -80,25 +71,20 @@
|
|
|
</thead>
|
|
</thead>
|
|
|
<tbody>
|
|
<tbody>
|
|
|
<tr v-if="isLoading">
|
|
<tr v-if="isLoading">
|
|
|
- <td colspan="11" class="admin--table-loading">
|
|
|
|
|
- 데이터를 불러오는 중...
|
|
|
|
|
- </td>
|
|
|
|
|
|
|
+ <td colspan="10" class="admin--table-loading">데이터를 불러오는 중...</td>
|
|
|
</tr>
|
|
</tr>
|
|
|
<tr v-else-if="!salesList || salesList.length === 0">
|
|
<tr v-else-if="!salesList || salesList.length === 0">
|
|
|
- <td colspan="11" class="admin--table-empty">
|
|
|
|
|
- 등록된 영업사원이 없습니다.
|
|
|
|
|
- </td>
|
|
|
|
|
|
|
+ <td colspan="10" class="admin--table-empty">등록된 영업사원이 없습니다.</td>
|
|
|
</tr>
|
|
</tr>
|
|
|
<tr v-else v-for="(sales, index) in salesList" :key="sales.id">
|
|
<tr v-else v-for="(sales, index) in salesList" :key="sales.id">
|
|
|
<td>{{ totalCount - ((currentPage - 1) * perPage + index) }}</td>
|
|
<td>{{ totalCount - ((currentPage - 1) * perPage + index) }}</td>
|
|
|
<td>
|
|
<td>
|
|
|
<div class="admin--table-photo">
|
|
<div class="admin--table-photo">
|
|
|
- <img v-if="sales.photo_url" :src="sales.photo_url" :alt="sales.name">
|
|
|
|
|
|
|
+ <img v-if="sales.photo_url" :src="getImageUrl(sales.photo_url)" :alt="sales.name" />
|
|
|
<div v-else class="admin--table-photo-empty">사진없음</div>
|
|
<div v-else class="admin--table-photo-empty">사진없음</div>
|
|
|
</div>
|
|
</div>
|
|
|
</td>
|
|
</td>
|
|
|
<td>{{ sales.showroom_name }}</td>
|
|
<td>{{ sales.showroom_name }}</td>
|
|
|
- <td>{{ sales.branch_name }}</td>
|
|
|
|
|
<td>{{ sales.team_name }}</td>
|
|
<td>{{ sales.team_name }}</td>
|
|
|
<td class="admin--table-title">{{ sales.name }}</td>
|
|
<td class="admin--table-title">{{ sales.name }}</td>
|
|
|
<td>{{ sales.position }}</td>
|
|
<td>{{ sales.position }}</td>
|
|
@@ -168,162 +154,174 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { ref, computed, onMounted } from 'vue'
|
|
|
|
|
-import { useRouter } from 'vue-router'
|
|
|
|
|
-
|
|
|
|
|
-definePageMeta({
|
|
|
|
|
- layout: 'admin',
|
|
|
|
|
- middleware: ['auth']
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-const router = useRouter()
|
|
|
|
|
-const { get, del } = useApi()
|
|
|
|
|
-
|
|
|
|
|
-const isLoading = ref(false)
|
|
|
|
|
-const salesList = ref([])
|
|
|
|
|
-const showrooms = ref([])
|
|
|
|
|
-const branches = ref([])
|
|
|
|
|
-const teams = ref([])
|
|
|
|
|
-
|
|
|
|
|
-const filters = ref({
|
|
|
|
|
- showroom: '',
|
|
|
|
|
- branch: '',
|
|
|
|
|
- team: '',
|
|
|
|
|
- keyword: ''
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-const currentPage = ref(1)
|
|
|
|
|
-const perPage = ref(10)
|
|
|
|
|
-const totalCount = ref(0)
|
|
|
|
|
-const totalPages = ref(0)
|
|
|
|
|
-
|
|
|
|
|
-// 보이는 페이지 번호 계산
|
|
|
|
|
-const visiblePages = computed(() => {
|
|
|
|
|
- const pages = []
|
|
|
|
|
- const maxVisible = 5
|
|
|
|
|
- let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2))
|
|
|
|
|
- let end = Math.min(totalPages.value, start + maxVisible - 1)
|
|
|
|
|
-
|
|
|
|
|
- if (end - start < maxVisible - 1) {
|
|
|
|
|
- start = Math.max(1, end - maxVisible + 1)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- for (let i = start; i <= end; i++) {
|
|
|
|
|
- pages.push(i)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return pages
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-// 필터 데이터 로드
|
|
|
|
|
-const loadFilters = async () => {
|
|
|
|
|
- // 전시장 리스트
|
|
|
|
|
- const { data: showroomData } = await get('/staff/showrooms')
|
|
|
|
|
- if (showroomData) showrooms.value = showroomData
|
|
|
|
|
-
|
|
|
|
|
- // 지점 리스트
|
|
|
|
|
- const { data: branchData } = await get('/branch/list', { per_page: 1000 })
|
|
|
|
|
- if (branchData) branches.value = branchData.items || []
|
|
|
|
|
-
|
|
|
|
|
- // 팀 리스트
|
|
|
|
|
- const { data: teamData } = await get('/staff/teams')
|
|
|
|
|
- if (teamData) teams.value = teamData
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 데이터 로드
|
|
|
|
|
-const loadSales = async () => {
|
|
|
|
|
- isLoading.value = true
|
|
|
|
|
-
|
|
|
|
|
- const params = {
|
|
|
|
|
- page: currentPage.value,
|
|
|
|
|
- per_page: perPage.value,
|
|
|
|
|
- ...filters.value
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const { data, error } = await get('/staff/sales', params)
|
|
|
|
|
-
|
|
|
|
|
- if (data) {
|
|
|
|
|
- salesList.value = data.items || []
|
|
|
|
|
- totalCount.value = data.total || 0
|
|
|
|
|
- totalPages.value = Math.ceil(totalCount.value / perPage.value)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- isLoading.value = false
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 검색
|
|
|
|
|
-const handleSearch = () => {
|
|
|
|
|
- currentPage.value = 1
|
|
|
|
|
- loadSales()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 초기화
|
|
|
|
|
-const handleReset = () => {
|
|
|
|
|
- filters.value = {
|
|
|
|
|
- showroom: '',
|
|
|
|
|
- branch: '',
|
|
|
|
|
- team: '',
|
|
|
|
|
- keyword: ''
|
|
|
|
|
- }
|
|
|
|
|
- currentPage.value = 1
|
|
|
|
|
- loadSales()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 페이지 변경
|
|
|
|
|
-const changePage = (page) => {
|
|
|
|
|
- if (page < 1 || page > totalPages.value) return
|
|
|
|
|
- currentPage.value = page
|
|
|
|
|
- loadSales()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 엑셀 다운로드 (전체)
|
|
|
|
|
-const handleExcelDownload = async () => {
|
|
|
|
|
- const params = { ...filters.value }
|
|
|
|
|
- window.open(`/api/staff/sales/excel?${new URLSearchParams(params)}`, '_blank')
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// A2 출력
|
|
|
|
|
-const handleA2Print = () => {
|
|
|
|
|
- const params = { ...filters.value }
|
|
|
|
|
- window.open(`/api/staff/sales/print-a2?${new URLSearchParams(params)}`, '_blank')
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 개별 프린트
|
|
|
|
|
-const handlePrint = (id) => {
|
|
|
|
|
- window.open(`/api/staff/sales/${id}/print`, '_blank')
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 개별 엑셀 출력
|
|
|
|
|
-const handleExcelExport = (id) => {
|
|
|
|
|
- window.open(`/api/staff/sales/${id}/excel`, '_blank')
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 등록 페이지로 이동
|
|
|
|
|
-const goToCreate = () => {
|
|
|
|
|
- router.push('/admin/staff/sales/create')
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 수정 페이지로 이동
|
|
|
|
|
-const goToEdit = (id) => {
|
|
|
|
|
- router.push(`/admin/staff/sales/edit/${id}`)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 삭제
|
|
|
|
|
-const handleDelete = async (id) => {
|
|
|
|
|
- if (!confirm('정말 삭제하시겠습니까?')) return
|
|
|
|
|
-
|
|
|
|
|
- const { error } = await del(`/staff/sales/${id}`)
|
|
|
|
|
-
|
|
|
|
|
- if (error) {
|
|
|
|
|
- alert('삭제에 실패했습니다.')
|
|
|
|
|
- } else {
|
|
|
|
|
- alert('삭제되었습니다.')
|
|
|
|
|
- loadSales()
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-onMounted(() => {
|
|
|
|
|
- loadFilters()
|
|
|
|
|
- loadSales()
|
|
|
|
|
-})
|
|
|
|
|
|
|
+ import { ref, computed, onMounted } from "vue";
|
|
|
|
|
+ import { useRouter } from "vue-router";
|
|
|
|
|
+
|
|
|
|
|
+ definePageMeta({
|
|
|
|
|
+ layout: "admin",
|
|
|
|
|
+ middleware: ["auth"],
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const router = useRouter();
|
|
|
|
|
+ const { get, del } = useApi();
|
|
|
|
|
+ const { getImageUrl } = useImage();
|
|
|
|
|
+
|
|
|
|
|
+ const isLoading = ref(false);
|
|
|
|
|
+ const salesList = ref([]);
|
|
|
|
|
+ const showrooms = ref([]);
|
|
|
|
|
+
|
|
|
|
|
+ // 영업팀 수동 리스트 (0번째는 마스터팀)
|
|
|
|
|
+ const teams = ref([
|
|
|
|
|
+ { id: 0, name: '마스터팀' },
|
|
|
|
|
+ { id: 1, name: '1팀' },
|
|
|
|
|
+ { id: 2, name: '2팀' },
|
|
|
|
|
+ { id: 3, name: '3팀' },
|
|
|
|
|
+ { id: 4, name: '4팀' },
|
|
|
|
|
+ { id: 5, name: '5팀' },
|
|
|
|
|
+ { id: 6, name: '6팀' },
|
|
|
|
|
+ { id: 7, name: '7팀' },
|
|
|
|
|
+ { id: 8, name: '8팀' },
|
|
|
|
|
+ { id: 9, name: '9팀' },
|
|
|
|
|
+ { id: 10, name: '10팀' }
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ const filters = ref({
|
|
|
|
|
+ showroom: "",
|
|
|
|
|
+ team: "",
|
|
|
|
|
+ keyword: "",
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const currentPage = ref(1);
|
|
|
|
|
+ const perPage = ref(10);
|
|
|
|
|
+ const totalCount = ref(0);
|
|
|
|
|
+ const totalPages = ref(0);
|
|
|
|
|
+
|
|
|
|
|
+ // 보이는 페이지 번호 계산
|
|
|
|
|
+ const visiblePages = computed(() => {
|
|
|
|
|
+ const pages = [];
|
|
|
|
|
+ const maxVisible = 5;
|
|
|
|
|
+ let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2));
|
|
|
|
|
+ let end = Math.min(totalPages.value, start + maxVisible - 1);
|
|
|
|
|
+
|
|
|
|
|
+ if (end - start < maxVisible - 1) {
|
|
|
|
|
+ start = Math.max(1, end - maxVisible + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (let i = start; i <= end; i++) {
|
|
|
|
|
+ pages.push(i);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return pages;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 필터 데이터 로드
|
|
|
|
|
+ const loadFilters = async () => {
|
|
|
|
|
+ // 전시장 리스트 (지점 목록)
|
|
|
|
|
+ const { data: branchData, error: branchError } = await get("/branch/list", {
|
|
|
|
|
+ per_page: 1000,
|
|
|
|
|
+ });
|
|
|
|
|
+ console.log("[SalesList] 전시장(지점) API 응답:", { data: branchData, error: branchError });
|
|
|
|
|
+ if (branchData?.success && branchData?.data) {
|
|
|
|
|
+ showrooms.value = branchData.data.items || [];
|
|
|
|
|
+ console.log("[SalesList] 전시장(지점) 로드 성공");
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 데이터 로드
|
|
|
|
|
+ const loadSales = async () => {
|
|
|
|
|
+ isLoading.value = true;
|
|
|
|
|
+
|
|
|
|
|
+ const params = {
|
|
|
|
|
+ page: currentPage.value,
|
|
|
|
|
+ per_page: perPage.value,
|
|
|
|
|
+ ...filters.value,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const { data, error } = await get("/staff/sales", params);
|
|
|
|
|
+ console.log("[SalesList] 영업사원 목록 API 응답:", { data, error });
|
|
|
|
|
+
|
|
|
|
|
+ if (data?.success && data?.data) {
|
|
|
|
|
+ salesList.value = data.data.items || [];
|
|
|
|
|
+ totalCount.value = data.data.total || 0;
|
|
|
|
|
+ totalPages.value = Math.ceil(totalCount.value / perPage.value);
|
|
|
|
|
+ console.log("[SalesList] 영업사원 목록 로드 성공");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ isLoading.value = false;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 검색
|
|
|
|
|
+ const handleSearch = () => {
|
|
|
|
|
+ currentPage.value = 1;
|
|
|
|
|
+ loadSales();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 초기화
|
|
|
|
|
+ const handleReset = () => {
|
|
|
|
|
+ filters.value = {
|
|
|
|
|
+ showroom: "",
|
|
|
|
|
+ team: "",
|
|
|
|
|
+ keyword: "",
|
|
|
|
|
+ };
|
|
|
|
|
+ currentPage.value = 1;
|
|
|
|
|
+ loadSales();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 페이지 변경
|
|
|
|
|
+ const changePage = (page) => {
|
|
|
|
|
+ if (page < 1 || page > totalPages.value) return;
|
|
|
|
|
+ currentPage.value = page;
|
|
|
|
|
+ loadSales();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 엑셀 다운로드 (전체)
|
|
|
|
|
+ const handleExcelDownload = async () => {
|
|
|
|
|
+ const params = { ...filters.value };
|
|
|
|
|
+ window.open(`/api/staff/sales/excel?${new URLSearchParams(params)}`, "_blank");
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // A2 출력
|
|
|
|
|
+ const handleA2Print = () => {
|
|
|
|
|
+ const params = { ...filters.value };
|
|
|
|
|
+ window.open(`/api/staff/sales/print-a2?${new URLSearchParams(params)}`, "_blank");
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 개별 프린트
|
|
|
|
|
+ const handlePrint = (id) => {
|
|
|
|
|
+ window.open(`/api/staff/sales/${id}/print`, "_blank");
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 개별 엑셀 출력
|
|
|
|
|
+ const handleExcelExport = (id) => {
|
|
|
|
|
+ window.open(`/api/staff/sales/${id}/excel`, "_blank");
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 등록 페이지로 이동
|
|
|
|
|
+ const goToCreate = () => {
|
|
|
|
|
+ router.push("/admin/staff/sales/create");
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 수정 페이지로 이동
|
|
|
|
|
+ const goToEdit = (id) => {
|
|
|
|
|
+ router.push(`/admin/staff/sales/edit/${id}`);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 삭제
|
|
|
|
|
+ const handleDelete = async (id) => {
|
|
|
|
|
+ if (!confirm("정말 삭제하시겠습니까?")) return;
|
|
|
|
|
+
|
|
|
|
|
+ const { error } = await del(`/staff/sales/${id}`);
|
|
|
|
|
+
|
|
|
|
|
+ if (error) {
|
|
|
|
|
+ alert("삭제에 실패했습니다.");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ alert("삭제되었습니다.");
|
|
|
|
|
+ loadSales();
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ onMounted(() => {
|
|
|
|
|
+ loadFilters();
|
|
|
|
|
+ loadSales();
|
|
|
|
|
+ });
|
|
|
</script>
|
|
</script>
|