|
|
@@ -1,798 +0,0 @@
|
|
|
-<template>
|
|
|
- <div class="admin--sales-list">
|
|
|
- <!-- 검색 영역 -->
|
|
|
- <div class="admin--search-box admin--search-box-large">
|
|
|
- <div class="admin--search-filters">
|
|
|
- <div class="admin--filter-row">
|
|
|
- <label class="admin--filter-label">전시장</label>
|
|
|
- <select v-model="filters.showroom" class="admin--form-select">
|
|
|
- <option value="">전체</option>
|
|
|
- <option v-for="showroom in showrooms" :key="showroom.id" :value="showroom.id">
|
|
|
- {{ showroom.name }}
|
|
|
- </option>
|
|
|
- </select>
|
|
|
-
|
|
|
- <label class="admin--filter-label">팀</label>
|
|
|
- <select v-model="filters.team" class="admin--form-select">
|
|
|
- <option value="">전체</option>
|
|
|
- <option v-for="team in teams" :key="team.id" :value="team.id">
|
|
|
- {{ team.name }}
|
|
|
- </option>
|
|
|
- </select>
|
|
|
-
|
|
|
- <label class="admin--filter-label">직책</label>
|
|
|
- <select v-model="filters.position" class="admin--form-select">
|
|
|
- <option value="">전체</option>
|
|
|
- <option
|
|
|
- v-for="position in positions"
|
|
|
- :key="position.value"
|
|
|
- :value="position.value"
|
|
|
- >
|
|
|
- {{ position.label }}
|
|
|
- </option>
|
|
|
- </select>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="admin--filter-row">
|
|
|
- <label class="admin--filter-label">검색어</label>
|
|
|
- <input
|
|
|
- v-model="filters.keyword"
|
|
|
- type="text"
|
|
|
- class="admin--form-input"
|
|
|
- placeholder="이름으로 검색"
|
|
|
- @keyup.enter="handleSearch"
|
|
|
- />
|
|
|
- <button class="admin--btn-small admin--btn-small-primary" @click="handleSearch">
|
|
|
- 검색
|
|
|
- </button>
|
|
|
- <button
|
|
|
- class="admin--btn-small admin--btn-small-secondary"
|
|
|
- @click="handleReset"
|
|
|
- >
|
|
|
- 초기화
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="admin--search-actions">
|
|
|
- <button
|
|
|
- class="admin--btn-small admin--btn-small-excel"
|
|
|
- @click="handleExcelDownload"
|
|
|
- >
|
|
|
- 엑셀 다운로드
|
|
|
- </button>
|
|
|
- <button
|
|
|
- class="admin--btn-small admin--btn-small-secondary"
|
|
|
- @click="handleA2Print"
|
|
|
- >
|
|
|
- A2 출력하기
|
|
|
- </button>
|
|
|
- <button class="admin--btn-small admin--btn-small-primary" @click="goToCreate">
|
|
|
- + 사원 등록
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 테이블 -->
|
|
|
- <div class="admin--table-wrapper">
|
|
|
- <table class="admin--table admin--table-sales">
|
|
|
- <thead>
|
|
|
- <tr>
|
|
|
- <th>NO</th>
|
|
|
- <th>사진</th>
|
|
|
- <th>전시장</th>
|
|
|
- <th>팀</th>
|
|
|
- <th>이름</th>
|
|
|
- <th>직책</th>
|
|
|
- <th>대표번호</th>
|
|
|
- <th>핸드폰</th>
|
|
|
- <th>이메일</th>
|
|
|
- <th>상태</th>
|
|
|
- <th>관리</th>
|
|
|
- </tr>
|
|
|
- </thead>
|
|
|
- <tbody>
|
|
|
- <tr v-if="!salesList || salesList.length === 0">
|
|
|
- <td colspan="11" class="admin--table-empty">등록된 영업사원이 없습니다.</td>
|
|
|
- </tr>
|
|
|
- <tr v-else v-for="(sales, index) in salesList" :key="sales.id">
|
|
|
- <td>{{ totalCount - ((currentPage - 1) * perPage + index) }}</td>
|
|
|
- <td>
|
|
|
- <div class="admin--table-photo">
|
|
|
- <img
|
|
|
- v-if="sales.photo_url"
|
|
|
- :src="getImageUrl(sales.photo_url)"
|
|
|
- :alt="sales.name"
|
|
|
- />
|
|
|
- <div v-else class="admin--table-photo-empty">사진없음</div>
|
|
|
- </div>
|
|
|
- </td>
|
|
|
- <td>{{ getShowroomName(sales.showroom) }}</td>
|
|
|
- <td>{{ getTeamName(sales.team) }}</td>
|
|
|
- <td class="admin--table-title">{{ sales.name }}</td>
|
|
|
- <td>{{ getPositionName(sales.position) }}</td>
|
|
|
- <td>{{ sales.direct_phone || "-" }}</td>
|
|
|
- <td>{{ sales.main_phone }}</td>
|
|
|
- <td>{{ sales.email }}</td>
|
|
|
- <td>
|
|
|
- <button
|
|
|
- class="admin--toggle-btn"
|
|
|
- :class="{ 'is-active': sales.is_act == 1 }"
|
|
|
- @click="toggleActive(sales.id, sales.is_act)"
|
|
|
- >
|
|
|
- {{ sales.is_act == 1 ? '노출' : '비노출' }}
|
|
|
- </button>
|
|
|
- </td>
|
|
|
- <td>
|
|
|
- <div class="admin--table-actions admin--table-actions-col">
|
|
|
- <button
|
|
|
- class="admin--btn-small admin--btn-small-primary"
|
|
|
- @click="goToEdit(sales.id)"
|
|
|
- >
|
|
|
- 수정
|
|
|
- </button>
|
|
|
- <button
|
|
|
- class="admin--btn-small admin--btn-small-danger"
|
|
|
- @click="handleDelete(sales.id)"
|
|
|
- >
|
|
|
- 삭제
|
|
|
- </button>
|
|
|
- <button
|
|
|
- class="admin--btn-small admin--btn-small-secondary"
|
|
|
- @click="handlePrint(sales.id)"
|
|
|
- >
|
|
|
- 프린트
|
|
|
- </button>
|
|
|
- <button
|
|
|
- class="admin--btn-small admin--btn-small-excel"
|
|
|
- @click="handleExcelExport(sales.id)"
|
|
|
- >
|
|
|
- 엑셀출력
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </td>
|
|
|
- </tr>
|
|
|
- </tbody>
|
|
|
- </table>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 페이지네이션 -->
|
|
|
- <div v-if="totalPages > 1" class="admin--pagination">
|
|
|
- <button
|
|
|
- class="admin--pagination-btn"
|
|
|
- :disabled="currentPage === 1"
|
|
|
- @click="changePage(1)"
|
|
|
- title="처음"
|
|
|
- >
|
|
|
- ⏮
|
|
|
- </button>
|
|
|
- <button
|
|
|
- class="admin--pagination-btn"
|
|
|
- :disabled="currentPage === 1"
|
|
|
- @click="changePage(currentPage - 1)"
|
|
|
- title="이전"
|
|
|
- >
|
|
|
- ◀
|
|
|
- </button>
|
|
|
- <button
|
|
|
- v-for="page in visiblePages"
|
|
|
- :key="page"
|
|
|
- class="admin--pagination-btn"
|
|
|
- :class="{ 'is-active': page === currentPage }"
|
|
|
- @click="changePage(page)"
|
|
|
- >
|
|
|
- {{ page }}
|
|
|
- </button>
|
|
|
- <button
|
|
|
- class="admin--pagination-btn"
|
|
|
- :disabled="currentPage === totalPages"
|
|
|
- @click="changePage(currentPage + 1)"
|
|
|
- title="다음"
|
|
|
- >
|
|
|
- ▶
|
|
|
- </button>
|
|
|
- <button
|
|
|
- class="admin--pagination-btn"
|
|
|
- :disabled="currentPage === totalPages"
|
|
|
- @click="changePage(totalPages)"
|
|
|
- title="끝"
|
|
|
- >
|
|
|
- ⏭
|
|
|
- </button>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 알림 모달 -->
|
|
|
- <AdminAlertModal
|
|
|
- v-if="alertModal.show"
|
|
|
- :title="alertModal.title"
|
|
|
- :message="alertModal.message"
|
|
|
- :type="alertModal.type"
|
|
|
- @confirm="handleAlertConfirm"
|
|
|
- @cancel="handleAlertCancel"
|
|
|
- @close="closeAlertModal"
|
|
|
- />
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script setup>
|
|
|
- import { ref, computed, onMounted } from "vue";
|
|
|
- import { useRouter } from "vue-router";
|
|
|
- import AdminAlertModal from '~/components/admin/AdminAlertModal.vue';
|
|
|
-
|
|
|
- definePageMeta({
|
|
|
- layout: "admin",
|
|
|
- middleware: ["auth"],
|
|
|
- });
|
|
|
-
|
|
|
- const router = useRouter();
|
|
|
- const { get, del, post } = useApi();
|
|
|
- const { getImageUrl } = useImage();
|
|
|
- const { teams, positions, getTeamName, getPositionName } = useSalesData();
|
|
|
-
|
|
|
- const salesList = ref([]);
|
|
|
- const showrooms = ref([]);
|
|
|
-
|
|
|
- const filters = ref({
|
|
|
- showroom: "",
|
|
|
- team: "",
|
|
|
- position: "",
|
|
|
- keyword: "",
|
|
|
- });
|
|
|
-
|
|
|
- const currentPage = ref(1);
|
|
|
- const perPage = ref(10);
|
|
|
- const totalCount = ref(0);
|
|
|
- const totalPages = ref(0);
|
|
|
-
|
|
|
- // 알림 모달
|
|
|
- const alertModal = ref({
|
|
|
- show: false,
|
|
|
- title: '알림',
|
|
|
- message: '',
|
|
|
- type: 'alert',
|
|
|
- onConfirm: null
|
|
|
- });
|
|
|
-
|
|
|
- // 알림 모달 표시
|
|
|
- const showAlert = (message, title = '알림') => {
|
|
|
- alertModal.value = {
|
|
|
- show: true,
|
|
|
- title,
|
|
|
- message,
|
|
|
- type: 'alert',
|
|
|
- onConfirm: null
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- // 확인 모달 표시
|
|
|
- const showConfirm = (message, onConfirm, title = '확인') => {
|
|
|
- alertModal.value = {
|
|
|
- show: true,
|
|
|
- title,
|
|
|
- message,
|
|
|
- type: 'confirm',
|
|
|
- onConfirm
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- // 알림 모달 닫기
|
|
|
- const closeAlertModal = () => {
|
|
|
- alertModal.value.show = false
|
|
|
- };
|
|
|
-
|
|
|
- // 알림 모달 확인
|
|
|
- const handleAlertConfirm = () => {
|
|
|
- if (alertModal.value.onConfirm) {
|
|
|
- alertModal.value.onConfirm()
|
|
|
- }
|
|
|
- closeAlertModal()
|
|
|
- };
|
|
|
-
|
|
|
- // 알림 모달 취소
|
|
|
- const handleAlertCancel = () => {
|
|
|
- closeAlertModal()
|
|
|
- };
|
|
|
-
|
|
|
- // 보이는 페이지 번호 계산
|
|
|
- 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", {
|
|
|
- params: {
|
|
|
- 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 () => {
|
|
|
- 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] 영업사원 목록 로드 성공");
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- // 검색
|
|
|
- const handleSearch = () => {
|
|
|
- currentPage.value = 1;
|
|
|
- loadSales();
|
|
|
- };
|
|
|
-
|
|
|
- // 초기화
|
|
|
- const handleReset = () => {
|
|
|
- filters.value = {
|
|
|
- showroom: "",
|
|
|
- team: "",
|
|
|
- position: "",
|
|
|
- keyword: "",
|
|
|
- };
|
|
|
- currentPage.value = 1;
|
|
|
- loadSales();
|
|
|
- };
|
|
|
-
|
|
|
- // 전시장 이름 가져오기
|
|
|
- const getShowroomName = (showroomId) => {
|
|
|
- const showroom = showrooms.value.find((s) => s.id === showroomId);
|
|
|
- return showroom ? showroom.name : "-";
|
|
|
- };
|
|
|
-
|
|
|
- // 페이지 변경
|
|
|
- const changePage = (page) => {
|
|
|
- if (page < 1 || page > totalPages.value) return;
|
|
|
- currentPage.value = page;
|
|
|
- loadSales();
|
|
|
- window.scrollTo({ top: 0, behavior: "smooth" });
|
|
|
- };
|
|
|
-
|
|
|
- // 엑셀 다운로드 (전체)
|
|
|
- const handleExcelDownload = async () => {
|
|
|
- // 전체 데이터 가져오기 (페이지네이션 없이)
|
|
|
- const params = {
|
|
|
- ...filters.value,
|
|
|
- per_page: 10000, // 충분히 큰 값으로 전체 가져오기
|
|
|
- };
|
|
|
-
|
|
|
- const { data } = await get("/staff/sales", { params });
|
|
|
- if (!data?.success || !data?.data) {
|
|
|
- alert("데이터를 가져올 수 없습니다.");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const allStaff = data.data.items || [];
|
|
|
-
|
|
|
- if (allStaff.length === 0) {
|
|
|
- alert("다운로드할 데이터가 없습니다.");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // HTML 테이블 생성
|
|
|
- let tableRows = "";
|
|
|
- allStaff.forEach((staff, index) => {
|
|
|
- const showroomName = getShowroomName(staff.showroom);
|
|
|
- const teamName = getTeamName(staff.team);
|
|
|
- const positionName = getPositionName(staff.position);
|
|
|
- const photoUrl = staff.photo_url ? getImageUrl(staff.photo_url) : "";
|
|
|
-
|
|
|
- tableRows += `
|
|
|
- <tr style="height: 110px;">
|
|
|
- <td style="text-align: center; vertical-align: middle;">${index + 1}</td>
|
|
|
- <td style="text-align: center; padding: 5px; vertical-align: middle;">
|
|
|
- ${
|
|
|
- photoUrl
|
|
|
- ? `<img src="${photoUrl}" height="100" style="display: block; margin: 0 auto; max-width: 100%;" />`
|
|
|
- : "사진없음"
|
|
|
- }
|
|
|
- </td>
|
|
|
- <td style="vertical-align: middle;">${showroomName}</td>
|
|
|
- <td style="vertical-align: middle;">${teamName}</td>
|
|
|
- <td style="vertical-align: middle;">${staff.name}</td>
|
|
|
- <td style="vertical-align: middle;">${positionName}</td>
|
|
|
- <td style="vertical-align: middle;">${staff.direct_phone || "-"}</td>
|
|
|
- <td style="vertical-align: middle;">${staff.main_phone || "-"}</td>
|
|
|
- <td style="vertical-align: middle;">${staff.email || ""}</td>
|
|
|
- </tr>
|
|
|
- `;
|
|
|
- });
|
|
|
-
|
|
|
- const html = `
|
|
|
-<html xmlns:x="urn:schemas-microsoft-com:office:excel">
|
|
|
-<head>
|
|
|
- <meta charset="UTF-8">
|
|
|
- <style>
|
|
|
- body {
|
|
|
- font-family: "Malgun Gothic", "맑은 고딕", Arial, sans-serif;
|
|
|
- }
|
|
|
- table {
|
|
|
- border-collapse: collapse;
|
|
|
- width: 100%;
|
|
|
- }
|
|
|
- th, td {
|
|
|
- border: 1px solid #ddd;
|
|
|
- padding: 10px;
|
|
|
- text-align: left;
|
|
|
- }
|
|
|
- th {
|
|
|
- background-color: #f5f5f5;
|
|
|
- font-weight: bold;
|
|
|
- text-align: center;
|
|
|
- }
|
|
|
- .title {
|
|
|
- font-size: 18pt;
|
|
|
- font-weight: bold;
|
|
|
- margin-bottom: 20px;
|
|
|
- text-align: center;
|
|
|
- }
|
|
|
- </style>
|
|
|
-</head>
|
|
|
-<body>
|
|
|
- <div class="title">영업사원 목록</div>
|
|
|
- <table>
|
|
|
- <thead>
|
|
|
- <tr>
|
|
|
- <th>NO</th>
|
|
|
- <th>사진</th>
|
|
|
- <th>전시장</th>
|
|
|
- <th>팀</th>
|
|
|
- <th>이름</th>
|
|
|
- <th>직책</th>
|
|
|
- <th>대표번호</th>
|
|
|
- <th>핸드폰</th>
|
|
|
- <th>이메일</th>
|
|
|
- </tr>
|
|
|
- </thead>
|
|
|
- <tbody>
|
|
|
- ${tableRows}
|
|
|
- </tbody>
|
|
|
- </table>
|
|
|
-</body>
|
|
|
-</html>`;
|
|
|
-
|
|
|
- // Blob 생성 및 다운로드
|
|
|
- const blob = new Blob(["\ufeff" + html], {
|
|
|
- type: "application/vnd.ms-excel;charset=utf-8;",
|
|
|
- });
|
|
|
- const link = document.createElement("a");
|
|
|
- const url = URL.createObjectURL(blob);
|
|
|
- link.setAttribute("href", url);
|
|
|
- link.setAttribute(
|
|
|
- "download",
|
|
|
- `sales_staff_list_${new Date().toISOString().split("T")[0]}.xls`
|
|
|
- );
|
|
|
- link.style.visibility = "hidden";
|
|
|
- document.body.appendChild(link);
|
|
|
- link.click();
|
|
|
- document.body.removeChild(link);
|
|
|
- };
|
|
|
-
|
|
|
- // A2 출력
|
|
|
- const handleA2Print = () => {
|
|
|
- window.open("/site-manager/staff/sales/print-a2", "_blank");
|
|
|
- };
|
|
|
-
|
|
|
- // 개별 프린트
|
|
|
- const handlePrint = (id) => {
|
|
|
- window.open(`/site-manager/staff/sales/print/${id}`, "_blank");
|
|
|
- };
|
|
|
-
|
|
|
- // 개별 엑셀 출력
|
|
|
- const handleExcelExport = async (id) => {
|
|
|
- // 해당 사원 데이터 가져오기
|
|
|
- const { data } = await get(`/staff/sales/${id}`);
|
|
|
- if (!data?.success || !data?.data) {
|
|
|
- alert("데이터를 가져올 수 없습니다.");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const staff = data.data;
|
|
|
-
|
|
|
- // 전시장 이름
|
|
|
- const showroomName = getShowroomName(staff.showroom);
|
|
|
- // 팀 이름
|
|
|
- const teamName = getTeamName(staff.team);
|
|
|
- // 직책 이름
|
|
|
- const positionName = getPositionName(staff.position);
|
|
|
-
|
|
|
- // 사진 URL
|
|
|
- const photoUrl = staff.photo_url ? getImageUrl(staff.photo_url) : "";
|
|
|
-
|
|
|
- // HTML 생성 (엑셀 호환 테이블 레이아웃)
|
|
|
- const html = `
|
|
|
-<html xmlns:x="urn:schemas-microsoft-com:office:excel">
|
|
|
-<head>
|
|
|
- <meta charset="UTF-8">
|
|
|
- <style>
|
|
|
- body {
|
|
|
- font-family: "Malgun Gothic", "맑은 고딕", Arial, sans-serif;
|
|
|
- }
|
|
|
- table {
|
|
|
- border-collapse: collapse;
|
|
|
- width: 100%;
|
|
|
- }
|
|
|
- .header-table {
|
|
|
- width: 100%;
|
|
|
- margin-bottom: 40px;
|
|
|
- border-bottom: 2px solid #000;
|
|
|
- padding-bottom: 10px;
|
|
|
- }
|
|
|
- .header-table td {
|
|
|
- border: none;
|
|
|
- padding: 10px;
|
|
|
- }
|
|
|
- .header-title {
|
|
|
- font-size: 20pt;
|
|
|
- font-weight: bold;
|
|
|
- text-align: left;
|
|
|
- }
|
|
|
- .header-logo {
|
|
|
- text-align: right;
|
|
|
- font-size: 10pt;
|
|
|
- color: #666;
|
|
|
- }
|
|
|
- .content-table {
|
|
|
- width: 100%;
|
|
|
- border: 1px solid #ddd;
|
|
|
- margin-top: 20px;
|
|
|
- }
|
|
|
- .content-table td {
|
|
|
- border: 1px solid #ddd;
|
|
|
- vertical-align: top;
|
|
|
- padding: 10px;
|
|
|
- }
|
|
|
- .info-wrapper {
|
|
|
- padding-top: 0px;
|
|
|
- }
|
|
|
- .photo-cell {
|
|
|
- width: 200px;
|
|
|
- text-align: center;
|
|
|
- vertical-align: top;
|
|
|
- }
|
|
|
- .photo-cell img {
|
|
|
- width: 200px;
|
|
|
- height: auto;
|
|
|
- display: block;
|
|
|
- }
|
|
|
- .photo-placeholder {
|
|
|
- width: 200px;
|
|
|
- height: 300px;
|
|
|
- border: 1px solid #ddd;
|
|
|
- background-color: #f9f9f9;
|
|
|
- display: inline-block;
|
|
|
- line-height: 300px;
|
|
|
- text-align: center;
|
|
|
- color: #999;
|
|
|
- }
|
|
|
- .info-table {
|
|
|
- width: 100%;
|
|
|
- border-collapse: collapse;
|
|
|
- border: none;
|
|
|
- }
|
|
|
- .info-table tr {
|
|
|
- border: none;
|
|
|
- }
|
|
|
- .info-table td {
|
|
|
- padding: 10px;
|
|
|
- border: 1px solid #ddd;
|
|
|
- }
|
|
|
- .info-label {
|
|
|
- width: 100px;
|
|
|
- font-weight: bold;
|
|
|
- color: #333;
|
|
|
- font-size: 13pt;
|
|
|
- background-color: #f5f5f5;
|
|
|
- }
|
|
|
- .info-value {
|
|
|
- color: #555;
|
|
|
- font-size: 13pt;
|
|
|
- }
|
|
|
- .info-value-name {
|
|
|
- color: #000;
|
|
|
- font-size: 16pt;
|
|
|
- font-weight: bold;
|
|
|
- }
|
|
|
- </style>
|
|
|
-</head>
|
|
|
-<body>
|
|
|
- <table class="content-table" style="table-layout: fixed;">
|
|
|
- <colgroup>
|
|
|
- <col style="width: 200px;">
|
|
|
- <col style="width: 100px;">
|
|
|
- <col style="width: auto;">
|
|
|
- </colgroup>
|
|
|
- <tr>
|
|
|
- <td class="photo-cell" rowspan="6" style="padding: 0;">
|
|
|
- ${
|
|
|
- photoUrl
|
|
|
- ? `<img src="${photoUrl}" width="200" style="display: block;" />`
|
|
|
- : '<div class="photo-placeholder">사진없음</div>'
|
|
|
- }
|
|
|
- </td>
|
|
|
- <td class="info-label" style="height: 44.5px;">전시장</td>
|
|
|
- <td class="info-value">${showroomName}</td>
|
|
|
- </tr>
|
|
|
- <tr>
|
|
|
- <td class="info-label" style="height: 44.5px;">팀</td>
|
|
|
- <td class="info-value">${teamName}</td>
|
|
|
- </tr>
|
|
|
- <tr>
|
|
|
- <td class="info-label" style="height: 44.5px;">이름</td>
|
|
|
- <td class="info-value-name">${staff.name}</td>
|
|
|
- </tr>
|
|
|
- <tr>
|
|
|
- <td class="info-label" style="height: 44.5px;">직책</td>
|
|
|
- <td class="info-value">${positionName}</td>
|
|
|
- </tr>
|
|
|
- <tr>
|
|
|
- <td class="info-label" style="height: 44.5px;">핸드폰</td>
|
|
|
- <td class="info-value">${staff.main_phone || "-"}</td>
|
|
|
- </tr>
|
|
|
- <tr>
|
|
|
- <td class="info-label" style="height: 44.5px;">이메일</td>
|
|
|
- <td class="info-value">${staff.email || ""}</td>
|
|
|
- </tr>
|
|
|
- </table>
|
|
|
-</body>
|
|
|
-</html>`;
|
|
|
-
|
|
|
- // Blob 생성 및 다운로드
|
|
|
- const blob = new Blob(["\ufeff" + html], {
|
|
|
- type: "application/vnd.ms-excel;charset=utf-8;",
|
|
|
- });
|
|
|
- const link = document.createElement("a");
|
|
|
- const url = URL.createObjectURL(blob);
|
|
|
- link.setAttribute("href", url);
|
|
|
- link.setAttribute(
|
|
|
- "download",
|
|
|
- `sales_staff_${staff.name}_${new Date().toISOString().split("T")[0]}.xls`
|
|
|
- );
|
|
|
- link.style.visibility = "hidden";
|
|
|
- document.body.appendChild(link);
|
|
|
- link.click();
|
|
|
- document.body.removeChild(link);
|
|
|
- };
|
|
|
-
|
|
|
- // 등록 페이지로 이동
|
|
|
- const goToCreate = () => {
|
|
|
- router.push("/site-manager/staff/sales/create");
|
|
|
- };
|
|
|
-
|
|
|
- // 수정 페이지로 이동
|
|
|
- const goToEdit = (id) => {
|
|
|
- router.push(`/site-manager/staff/sales/edit/${id}`);
|
|
|
- };
|
|
|
-
|
|
|
- // 삭제
|
|
|
- const handleDelete = async (id) => {
|
|
|
- showConfirm(
|
|
|
- '정말 삭제하시겠습니까?',
|
|
|
- async () => {
|
|
|
- const { data, error } = await del(`/staff/sales/${id}`);
|
|
|
-
|
|
|
- if (error || !data?.success) {
|
|
|
- showAlert(error?.message || data?.message || '삭제에 실패했습니다.', '오류');
|
|
|
- } else {
|
|
|
- showAlert(data.message || '삭제되었습니다.', '성공');
|
|
|
- loadSales();
|
|
|
- }
|
|
|
- },
|
|
|
- '영업사원 삭제'
|
|
|
- );
|
|
|
- };
|
|
|
-
|
|
|
- // 노출/비노출 토글
|
|
|
- const toggleActive = (id, currentStatus) => {
|
|
|
- const statusText = currentStatus == 1 ? '비노출' : '노출';
|
|
|
-
|
|
|
- showConfirm(
|
|
|
- `영업사원을 ${statusText} 상태로 변경하시겠습니까?`,
|
|
|
- async () => {
|
|
|
- const { data, error } = await post(`/staff/sales/${id}/toggle-active`);
|
|
|
-
|
|
|
- if (error || !data?.success) {
|
|
|
- showAlert(error?.message || data?.message || '상태 변경에 실패했습니다.', '오류');
|
|
|
- } else {
|
|
|
- showAlert(data.message || '영업사원 상태가 변경되었습니다.', '성공');
|
|
|
- loadSales();
|
|
|
- }
|
|
|
- },
|
|
|
- '상태 변경'
|
|
|
- );
|
|
|
- };
|
|
|
-
|
|
|
- onMounted(() => {
|
|
|
- loadFilters();
|
|
|
- loadSales();
|
|
|
- });
|
|
|
-</script>
|
|
|
-
|
|
|
-<style scoped>
|
|
|
- /* 필터 영역 input/select 테두리 스타일 */
|
|
|
- .admin--filter-row .admin--form-input,
|
|
|
- .admin--filter-row .admin--form-select {
|
|
|
- border: 1px solid var(--admin-border-color);
|
|
|
- border-radius: 4px;
|
|
|
- height: 33px;
|
|
|
- padding: 6px 14px;
|
|
|
- font-size: 13px;
|
|
|
- }
|
|
|
-
|
|
|
- /* 버튼 영역 간격 조정 */
|
|
|
- .admin--search-actions {
|
|
|
- display: flex;
|
|
|
- gap: 12px;
|
|
|
- flex-shrink: 0;
|
|
|
- white-space: nowrap;
|
|
|
- }
|
|
|
-
|
|
|
- /* 상단 버튼들 세로 크기 */
|
|
|
- .admin--search-actions .admin--btn-small {
|
|
|
- height: 78px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- }
|
|
|
-
|
|
|
- /* 상태 토글 버튼 */
|
|
|
- .admin--toggle-btn {
|
|
|
- padding: 6px 16px;
|
|
|
- font-size: 12px;
|
|
|
- border-radius: 20px;
|
|
|
- border: 1px solid #ddd;
|
|
|
- background: #f5f5f5;
|
|
|
- color: #666;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.3s ease;
|
|
|
- font-weight: 500;
|
|
|
- }
|
|
|
-
|
|
|
- .admin--toggle-btn:hover {
|
|
|
- border-color: #bbb;
|
|
|
- background: #e8e8e8;
|
|
|
- }
|
|
|
-
|
|
|
- .admin--toggle-btn.is-active {
|
|
|
- background: var(--admin-accent-primary);
|
|
|
- color: white;
|
|
|
- border-color: var(--admin-accent-primary);
|
|
|
- }
|
|
|
-
|
|
|
- .admin--toggle-btn.is-active:hover {
|
|
|
- background: var(--admin-accent-hover);
|
|
|
- border-color: var(--admin-accent-hover);
|
|
|
- }
|
|
|
-</style>
|