From c00abb103fbadc14d396f83b75c88271454988a5 Mon Sep 17 00:00:00 2001
From: Yu Sung <hwchon1234@gmail.com>
Date: Tue, 12 Mar 2024 14:46:19 +0900
Subject: [PATCH] =?UTF-8?q?=EC=8B=9C=EA=B7=B8=EB=8B=88=EC=B2=98=20?=
 =?UTF-8?q?=EC=A1=B0=ED=9A=8C/=EB=93=B1=EB=A1=9D/=EC=88=98=EC=A0=95/?=
 =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B6=94?=
 =?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/api/signature.js                        |  23 +
 src/components/SideMenu.vue                 |   7 +-
 src/router/index.js                         |   5 +
 src/views/Signature/SignatureManagement.vue | 530 ++++++++++++++++++++
 4 files changed, 561 insertions(+), 4 deletions(-)
 create mode 100644 src/api/signature.js
 create mode 100644 src/views/Signature/SignatureManagement.vue

diff --git a/src/api/signature.js b/src/api/signature.js
new file mode 100644
index 0000000..10a61da
--- /dev/null
+++ b/src/api/signature.js
@@ -0,0 +1,23 @@
+import Vue from 'vue'
+
+async function getSignatureList(page) {
+    return Vue.axios.get('/creator-admin/signature?page=' + (page - 1) + "&size=20");
+}
+
+async function createSignature(formData) {
+    return Vue.axios.post('/creator-admin/signature', formData, {
+        headers: {
+            "Content-Type": "multipart/form-data",
+        },
+    });
+}
+
+async function modifySignature(formData) {
+    return Vue.axios.put('/creator-admin/signature', formData, {
+        headers: {
+            "Content-Type": "multipart/form-data",
+        },
+    });
+}
+
+export { getSignatureList, createSignature, modifySignature }
diff --git a/src/components/SideMenu.vue b/src/components/SideMenu.vue
index 1cc3222..ba7ea0a 100644
--- a/src/components/SideMenu.vue
+++ b/src/components/SideMenu.vue
@@ -20,11 +20,10 @@
             :to="item.route"
             active-class="blue white--text"
           >
-            <v-list-item-icon>
-              <v-icon>{{ item.icon }}</v-icon>
-            </v-list-item-icon>
-
             <v-list-item-title>{{ item.title }}</v-list-item-title>
+            <v-list-item-icon>
+              <v-icon>mdi-chevron-right</v-icon>
+            </v-list-item-icon>
           </v-list-item>
         </div>
 
diff --git a/src/router/index.js b/src/router/index.js
index 5ef7e7e..9e3d825 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -45,6 +45,11 @@ const routes = [
                 name: 'CalculateContentDonation',
                 component: () => import(/* webpackChunkName: "calculate" */ '../views/Calculate/CalculateContentDonation.vue')
             },
+            {
+                path: '/signature',
+                name: 'SignatureManagement',
+                component: () => import(/* webpackChunkName: "signature" */ '../views/Signature/SignatureManagement.vue')
+            }
         ]
     },
     {
diff --git a/src/views/Signature/SignatureManagement.vue b/src/views/Signature/SignatureManagement.vue
new file mode 100644
index 0000000..db3d7aa
--- /dev/null
+++ b/src/views/Signature/SignatureManagement.vue
@@ -0,0 +1,530 @@
+<template>
+  <div>
+    <v-toolbar dark>
+      <v-spacer />
+      <v-toolbar-title>시그니처 관리</v-toolbar-title>
+      <v-spacer />
+    </v-toolbar>
+    <v-container>
+      <v-row>
+        <v-col cols="10" />
+        <v-col>
+          <v-btn
+            block
+            color="#3bb9f1"
+            dark
+            depressed
+            @click="showWriteDialog"
+          >
+            시그니처 등록
+          </v-btn>
+        </v-col>
+      </v-row>
+      <v-row>
+        <v-col>
+          <v-data-table
+            :headers="headers"
+            :items="signature_list"
+            :loading="is_loading"
+            :items-per-page="-1"
+            item-key="id"
+            class="elevation-1"
+            hide-default-footer
+          >
+            <template v-slot:item.can="{ item }">
+              {{ item.can }}
+            </template>
+
+            <template v-slot:item.image="{ item }">
+              <v-img
+                :src="item.image"
+                max-width="200"
+                max-height="200"
+                align="center"
+                class="center-image"
+              />
+            </template>
+            <template v-slot:item.management="{ item }">
+              <v-row>
+                <v-col />
+                <v-col>
+                  <v-btn
+                    :disabled="is_loading"
+                    @click="showModifyDialog(item)"
+                  >
+                    수정
+                  </v-btn>
+                </v-col>
+                <v-col>
+                  <v-btn
+                    :disabled="is_loading"
+                    @click="showDeleteConfirm(item)"
+                  >
+                    삭제
+                  </v-btn>
+                </v-col>
+                <v-col />
+              </v-row>
+            </template>
+          </v-data-table>
+        </v-col>
+      </v-row>
+      <v-row class="text-center">
+        <v-col>
+          <v-pagination
+            v-model="page"
+            :length="total_page"
+            circle
+            @input="next"
+          />
+        </v-col>
+      </v-row>
+    </v-container>
+    <v-row>
+      <v-dialog
+        v-model="show_write_dialog"
+        max-width="1000px"
+        persistent
+      >
+        <v-card>
+          <v-card-title>
+            시그니처 캔 등록
+          </v-card-title>
+          <v-card-text>
+            <v-row align="center">
+              <v-col cols="4">
+                캔
+              </v-col>
+              <v-col cols="8">
+                <v-text-field
+                  v-model="can"
+                  label="캔"
+                />
+              </v-col>
+            </v-row>
+          </v-card-text>
+
+          <v-card-text>
+            <v-row align="center">
+              <v-col cols="4">
+                이미지
+              </v-col>
+              <v-col cols="8">
+                <div class="image-select">
+                  <label for="image">
+                    이미지 불러오기
+                  </label>
+                  <v-file-input
+                    id="image"
+                    v-model="image"
+                    @change="imageAdd"
+                  />
+                </div>
+                <img
+                  v-if="image_url"
+                  :src="image_url"
+                  alt=""
+                  class="image-preview"
+                >
+              </v-col>
+            </v-row>
+          </v-card-text>
+
+          <v-card-actions v-show="!is_loading">
+            <v-spacer />
+            <v-btn
+              color="blue darken-1"
+              text
+              @click="cancel"
+            >
+              취소
+            </v-btn>
+            <v-btn
+              color="blue darken-1"
+              text
+              @click="validate"
+            >
+              등록
+            </v-btn>
+          </v-card-actions>
+        </v-card>
+      </v-dialog>
+    </v-row>
+    <v-row>
+      <v-dialog
+        v-model="show_modify_dialog"
+        max-width="1000px"
+        persistent
+      >
+        <v-card>
+          <v-card-title>
+            시그니처 캔 수정
+          </v-card-title>
+          <v-card-text>
+            <v-row align="center">
+              <v-col cols="4">
+                캔
+              </v-col>
+              <v-col cols="8">
+                {{ selected_signature_can.can }} 캔
+              </v-col>
+            </v-row>
+          </v-card-text>
+
+          <v-card-text>
+            <v-row align="center">
+              <v-col cols="4">
+                이미지
+              </v-col>
+              <v-col cols="8">
+                <div class="image-select">
+                  <label for="image">
+                    이미지 불러오기
+                  </label>
+                  <v-file-input
+                    id="image"
+                    v-model="image"
+                    @change="imageAdd"
+                  />
+                </div>
+                <img
+                  v-if="image_url"
+                  :src="image_url"
+                  alt=""
+                  class="image-preview"
+                >
+              </v-col>
+            </v-row>
+          </v-card-text>
+
+          <v-card-actions v-show="!is_loading">
+            <v-spacer />
+            <v-btn
+              color="blue darken-1"
+              text
+              @click="cancel"
+            >
+              취소
+            </v-btn>
+            <v-btn
+              color="blue darken-1"
+              text
+              @click="modifySignatureCan"
+            >
+              수정
+            </v-btn>
+          </v-card-actions>
+        </v-card>
+      </v-dialog>
+    </v-row>
+    <v-row>
+      <v-dialog
+        v-model="show_delete_confirm_dialog"
+        max-width="400px"
+        persistent
+      >
+        <v-card>
+          <v-card-text />
+          <v-card-text>
+            삭제하시겠습니까?
+          </v-card-text>
+          <v-card-actions v-show="!is_loading">
+            <v-spacer />
+            <v-btn
+              color="blue darken-1"
+              text
+              @click="cancel"
+            >
+              취소
+            </v-btn>
+            <v-btn
+              color="blue darken-1"
+              text
+              @click="deleteSignature"
+            >
+              확인
+            </v-btn>
+          </v-card-actions>
+        </v-card>
+      </v-dialog>
+    </v-row>
+  </div>
+</template>
+
+<script>
+import * as api from "@/api/signature";
+
+export default {
+  name: "SignatureManagement",
+
+  data() {
+    return {
+      is_loading: false,
+      is_modify: false,
+      show_write_dialog: false,
+      show_modify_dialog: false,
+      show_delete_confirm_dialog: false,
+
+      signature_list: [],
+
+      page: 1,
+      total_page: 0,
+
+      can: 0,
+      image: null,
+      image_url: null,
+      is_active: null,
+      selected_signature_can: {},
+
+      headers: [
+        {
+          text: '캔',
+          align: 'center',
+          sortable: false,
+          value: 'can',
+        },
+        {
+          text: '이미지',
+          align: 'center',
+          sortable: false,
+          value: 'image',
+        },
+        {
+          text: '관리',
+          align: 'center',
+          sortable: false,
+          value: 'management',
+        },
+      ],
+    }
+  },
+
+  async created() {
+    await this.getSignatureList();
+  },
+
+  methods: {
+    notifyError(message) {
+      this.$dialog.notify.error(message)
+    },
+
+    notifySuccess(message) {
+      this.$dialog.notify.success(message)
+    },
+
+    imageAdd(payload) {
+      const file = payload;
+      if (file) {
+        this.image_url = URL.createObjectURL(file)
+        URL.revokeObjectURL(file)
+      } else {
+        this.image_url = null
+      }
+    },
+
+    showWriteDialog() {
+      this.show_write_dialog = true
+    },
+
+    validate() {
+      if (
+        this.can === 0 ||
+        this.image === null
+      ) {
+        this.notifyError('내용을 입력하세요')
+      } else {
+        this.submit()
+      }
+    },
+
+    cancel() {
+      this.show_write_dialog = false
+      this.show_modify_dialog = false
+      this.show_delete_confirm_dialog = false
+      this.image = null
+      this.image_url = null
+      this.can = 0
+      this.is_active = null
+      this.selected_signature_can = {}
+    },
+
+    showDeleteConfirm(item) {
+      this.selected_signature_can = item
+      this.show_delete_confirm_dialog = true
+    },
+
+    showModifyDialog(item) {
+      this.image_url = item.image
+      this.selected_signature_can = item
+      this.show_modify_dialog = true
+    },
+
+    async getSignatureList() {
+      this.isLoading = true
+
+      try {
+        let res = await api.getSignatureList(this.page);
+
+        if (res.status === 200 && res.data.success === true) {
+          const data = res.data.data
+
+          const total_page = Math.ceil(data.totalCount / 20)
+          this.signature_list = data.items
+
+          if (total_page <= 0)
+            this.total_page = 1
+          else
+            this.total_page = total_page
+        }
+      } catch (e) {
+        this.notifyError("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
+      } finally {
+        this.isLoading = false
+      }
+    },
+
+    async submit() {
+      if (this.is_loading) return;
+
+      this.is_loading = true
+
+      try {
+        const formData = new FormData()
+        formData.append("can", this.can)
+        formData.append("image", this.image)
+
+        const res = await api.createSignature(formData)
+        if (res.status === 200 && res.data.success === true) {
+          this.cancel()
+          this.notifySuccess(res.data.message || '등록되었습니다.')
+
+          this.page = 1
+          await this.getSignatureList()
+        } else {
+          this.is_loading = false
+          this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
+        }
+      } catch (e) {
+        this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
+        this.is_loading = false
+      } finally {
+        this.is_loading = false
+      }
+    },
+
+    async modifySignatureCan() {
+      if (this.image === null) {
+        this.notifyError('수정사항이 없습니다.')
+        return;
+      }
+
+      if (this.is_loading) return;
+
+      this.is_loading = true
+
+      try {
+        const formData = new FormData()
+        formData.append("id", this.selected_signature_can.id)
+        formData.append("image", this.image)
+
+        const res = await api.modifySignature(formData)
+        if (res.status === 200 && res.data.success === true) {
+          this.cancel()
+          this.notifySuccess(res.data.message || '수정되었습니다.')
+
+          this.page = 1
+          await this.getSignatureList()
+        } else {
+          this.is_loading = false
+          this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
+        }
+      } catch (e) {
+        this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
+        this.is_loading = false
+      } finally {
+        this.is_loading = false
+      }
+    },
+
+    async deleteSignature() {
+      if (this.is_loading) return;
+
+      this.is_loading = true
+
+      try {
+        const formData = new FormData()
+        formData.append("id", this.selected_signature_can.id)
+        formData.append("isActive", false)
+
+        const res = await api.modifySignature(formData)
+        if (res.status === 200 && res.data.success === true) {
+          this.cancel()
+          this.notifySuccess(res.data.message || '등록되었습니다.')
+
+          this.page = 1
+          await this.getSignatureList()
+        } else {
+          this.is_loading = false
+          this.notifyError(res.data.message || '알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
+        }
+      } catch (e) {
+        this.notifyError('알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.')
+        this.is_loading = false
+      } finally {
+        this.is_loading = false
+      }
+    },
+
+    async next() {
+      await this.getSignatureList();
+    }
+  }
+}
+</script>
+
+<style scoped>
+.image-select label {
+  display: inline-block;
+  padding: 10px 20px;
+  background-color: #232d4a;
+  color: #fff;
+  vertical-align: middle;
+  font-size: 15px;
+  cursor: pointer;
+  border-radius: 5px;
+}
+
+.v-file-input {
+  position: absolute;
+  width: 0;
+  height: 0;
+  padding: 0;
+  overflow: hidden;
+  border: 0;
+}
+
+.image-preview {
+  max-width: 100%;
+  width: 250px;
+  object-fit: cover;
+  margin-top: 10px;
+}
+
+.v-card__text {
+  margin-top: 20px;
+}
+
+.v-card__actions {
+  margin-top: 100px;
+}
+
+.v-card__actions > .v-btn {
+  font-size: 20px;
+
+}
+
+.center-image {
+  display: block;
+  margin: 0 auto;
+}
+</style>