jichoi / calendar star
박정하 박정하 07-23
250723 박정하 사용자권한관리, 접근제어관리 작업 완료
@a6ddfbf6a6c583dadab4bc0de7acd15dd98ca943
 
client/resources/api/author.js (added)
+++ client/resources/api/author.js
@@ -0,0 +1,26 @@
+import apiClient from "./index";
+
+// 목록 조회
+export const findAuthorsProc = () => {
+  return apiClient.get('/author/authors.json');
+}
+
+// 등록
+export const saveAuthorProc = data => {
+  return apiClient.post('/author/saveAuthor.json', data);
+}
+
+// 상세 조회
+export const findAuthorProc = data => {
+  return apiClient.get(`/author/${data}/authors.json`);
+}
+
+// 수정
+export const updateAuthorProc = (data) => {
+  return apiClient.put(`/author/${data.authorCode}/updateAuthor.json`, data);
+}
+
+// 삭제
+export const deleteAuthorProc = (data) => {
+  return apiClient.delete(`/author/${data}/deleteAuthor.json`);
+}(파일 끝에 줄바꿈 문자 없음)
 
client/resources/api/menuAuthor.js (added)
+++ client/resources/api/menuAuthor.js
@@ -0,0 +1,11 @@
+import apiClient from "./index";
+
+// 목록 조회
+export const findMenuAuthorsProc = data => {
+  return apiClient.get(`/menuAuthor/${data}/findMenuAuthors.json`);
+}
+
+// 수정
+export const updateMenuAuthorsProc = data => {
+  return apiClient.put('/menuAuthor/updateMenuAuthors.json', data);
+}(파일 끝에 줄바꿈 문자 없음)
client/views/layout/LeftNavBar.vue
--- client/views/layout/LeftNavBar.vue
+++ client/views/layout/LeftNavBar.vue
@@ -172,8 +172,8 @@
   system: {
     path: '/system-management',
     items: [
-      { name: 'userManagement', title: '사용자권한관리' },
-      { name: 'accessControlManagement', title: '접근제어관리' },
+      { name: 'AuthorManagementPage', title: '사용자권한관리' },
+      { name: 'MenuAuthorManagementPage', title: '접근제어관리' },
       { name: 'commonCodeManagement', title: '공통코드관리' }
     ]
   }
client/views/pages/AppRouter.js
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
@@ -61,8 +61,8 @@
 import buseoManagement from '../pages/Manager/hr/buseoManagement.vue';
 
 //시스템관리
-import userManagement from '../pages/Manager/system/userManagement.vue';
-import accessControlManagement from '../pages/Manager/system/accessControlManagement.vue';
+import AuthorManagementComp from '../pages/Manager/system/AuthorManagement.vue';
+import MenuAuthorManagementComp from './Manager/system/MenuAuthorManagement.vue';
 import commonCodeManagement from '../pages/Manager/system/commonCodeManagement.vue';
 import commonCodeInsert from '../pages/Manager/system/commonCodeInsert.vue';
 import commonCodeDetail from '../pages/Manager/system/commonCodeDetail.vue';
@@ -149,10 +149,10 @@
   },
   // 시스템관리
   {
-    path: '/system-management', name: 'system', redirect: { name: 'userManagement' },
+    path: '/system-management', name: 'system', redirect: { name: 'AuthorManagementPage' },
     children: [
-      { path: 'userManagement.page', name: 'userManagement', component: userManagement },
-      { path: 'accessControlManagement.page', name: 'accessControlManagement', component: accessControlManagement },
+      { path: 'AuthorManagement.page', name: 'AuthorManagementPage', component: AuthorManagementComp},
+      { path: 'MenuAuthorManagement.page', name: 'MenuAuthorManagementPage', component: MenuAuthorManagementComp },
       { path: 'commonCodeManagement.page', name: 'commonCodeManagement', component: commonCodeManagement },
       { path: 'commonCodeInsert.page', name: 'commonCodeInsert', component: commonCodeInsert },
       { path: 'commonCodeDetail.page', name: 'commonCodeDetail', component: commonCodeDetail },
 
client/views/pages/Manager/system/AuthorManagement.vue (added)
+++ client/views/pages/Manager/system/AuthorManagement.vue
@@ -0,0 +1,256 @@
+<template>
+  <div class="card ">
+    <div class="card-body ">
+      <h2 class="card-title">사용자 권한 관리</h2>
+      <div class="flex align-top">
+        <div class="sch-form-wrap search">
+          <div class="tbl-wrap table-scroll">
+            <table id="myTable" class="tbl data">
+              <thead>
+                <tr>
+                  <th>권한 목록</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr v-for="(item, idx) of lists" :key="idx" @click="findData(item.authorCode)">
+                  <td :class="{ 'selected': selectedAuthor == item.authorCode, 'muted': item.useAt === 'N' }">{{ item.authorNm }}</td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
+        <div>
+          <div class="sch-form-wrap title-wrap">
+            <h3><img :src="h3icon" alt="">권한 정보</h3>
+            <div class="buttons">
+              <button type="button" class="btn sm tertiary" @click="fnResetForm">신규</button>
+              <button type="button" class="btn sm secondary" v-if="!isEditMode" @click="fnSave">등록</button>
+              <button type="button" class="btn sm secondary" v-else @click="fnUpdate">수정</button>
+              <button type="button" class="btn sm btn-red" v-if="isEditMode" @click="fnDelete">삭제</button>
+            </div>
+          </div>
+          <div class="tbl-wrap row g-3 pt-3 needs-validation detail">
+            <div class="col-12">
+              <label for="authorCode" class="form-label">
+                <p>권한코드 <span class="require"><img :src="require" alt=""></span></p>
+              </label>
+              <input type="text" class="form-control" id="authorCode" v-model="data.authorCode" />
+            </div>
+            <div class="col-12">
+              <label for="authorNm" class="form-label">
+                <p>권한명 <span class="require"><img :src="require" alt=""></span></p>
+              </label>
+              <input type="text" class="form-control" id="authorNm" v-model="data.authorNm" />
+            </div>
+            <div class="col-12 chuljang ">
+              <label for="prvonsh" class="form-label">권한설명</label>
+              <textarea name="dc" id="dc" class="form-control" v-model="data.dc"></textarea>
+            </div>
+            <div class="col-12 border-x input-radio">
+              <label for="prvonsh" class="form-label">
+                <p>사용여부 <span class="require"><img :src="require" alt=""></span></p>
+              </label>
+              <div class="chk-area">
+                <div class="form-check">
+                  <input type="radio" name="useAt" id="useAtTrue" value="Y" v-model="data.useAt" :disabled="!hasAuthorCode">
+                  <label for="useAtTrue">사용</label>
+                </div>
+                <div class="form-check">
+                  <input type="radio" name="useAt" id="useAtFalse" value="N" v-model="data.useAt" :disabled="!hasAuthorCode">
+                  <label for="useAtFalse">미사용</label>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+// API
+import { findAuthorsProc, saveAuthorProc, findAuthorProc, updateAuthorProc, deleteAuthorProc } from '../../../../resources/api/author'
+
+export default {
+  data() {
+    return {
+      require: "/client/resources/img/require.png",
+      h3icon: "/client/resources/img/h3icon.png",
+
+      isEditMode: false, // 수정 모드 여부
+      selectedAuthor: null,
+
+      initData: {
+        authorCode: null, // 권한코드
+        authorNm: null,   // 권한명
+        dc: null,         // 권한설명
+        useAt: "Y",       // 사용여부 (Y:사용-기본값 / N:미사용)
+      },
+
+      lists: [], // 권한 목록 정보
+      data: {}, // 권한 상세 정보
+    }
+  },
+
+  computed: {
+    hasAuthorCode() {
+      return !!(this.data?.authorCode);
+    }
+  },
+
+  created() {
+    this.fnReset(); // 데이터 초기화
+  },
+
+  mounted() {
+    this.findList(); // 목록 조회
+  },
+
+  methods: {
+    // 목록 조회
+    async findList() {
+      try {
+        const response = await findAuthorsProc();
+        const result = response.data.data;
+
+        this.lists = result.authors;
+      } catch (error) {
+        if (error.response) {
+          alert(error.response.data.message);
+        } else {
+          alert("에러가 발생했습니다.");
+        }
+        console.error(error.message);
+      };
+    },
+
+    // 상세 조회 (권한 선택 시 실행)
+    async findData(id) {
+      try {
+        this.selectedAuthor = id;
+
+        const response = await findAuthorProc(id);
+        const result = response.data.data;
+
+        this.data = result.author;
+        this.isEditMode = true; // 수정 모드로 변경
+      } catch (error) {
+        if (error.response) {
+          alert(error.response.data.message);
+        } else {
+          alert("에러가 발생했습니다.");
+        }
+        console.error(error.message);
+      };
+    },
+
+    // 신규 (리셋)
+    fnResetForm() {
+      const isCheck = confirm("신규 등록 화면으로 전환 시, 현재 작업중인 내용이 초기화됩니다.\n전환하시겠습니까?");
+      if (!isCheck) {
+        return;
+      }
+
+      this.fnReset(); // 데이터 초기화
+    },
+
+    // 데이터 초기화
+    fnReset() {
+      this.data = { ...this.initData };
+      this.isEditMode = false; // 신규 모드로 변경
+    },
+
+    // 등록
+    async fnSave() {
+      try {
+        const data = {
+          authorCode: this.data.authorCode,
+          authorNm: this.data.authorNm,
+          dc: this.data.dc,
+        }
+
+        const response = await saveAuthorProc(data);
+        const result = response.data.data;
+
+        alert("등록되었습니다.");
+
+        this.fnReset(); // 데이터 초기화
+        this.findList(); // 목록 조회
+      } catch (error) {
+        if (error.response) {
+          alert(error.response.data.message);
+        } else {
+          alert("에러가 발생했습니다.");
+        }
+        console.error(error.message);
+      };
+    },
+
+    // 수정
+    async fnUpdate() {
+      try {
+        const data = {
+          authorCode: this.data.authorCode,
+          authorNm: this.data.authorNm,
+          dc: this.data.dc,
+          useAt: this.data.useAt,
+        }
+
+        const response = await updateAuthorProc(data);
+        const result = response.data.data;
+
+        alert("수정되었습니다.");
+
+        this.fnReset(); // 데이터 초기화
+        this.findList(); // 목록 조회
+      } catch (error) {
+        if (error.response) {
+          alert(error.response.data.message);
+        } else {
+          alert("에러가 발생했습니다.");
+        }
+        console.error(error.message);
+      };
+    },
+
+    // 삭제
+    async fnDelete() {
+      try {
+        const isCheck = confirm("삭제 할 경우 삭제된 데이터를 복구할 수 없습니다.\n삭제하시겠습니까?");
+        if (!isCheck) {
+          return;
+        }
+
+        const response = await deleteAuthorProc(this.data.authorCode);
+        const result = response.data.data;
+
+        alert("삭제되었습니다.");
+
+        this.fnReset(); // 데이터 초기화
+        this.findList(); // 목록 조회
+      } catch (error) {
+        if (error.response) {
+          alert(error.response.data.message);
+        } else {
+          alert("에러가 발생했습니다.");
+        }
+        console.error(error.message);
+      };
+    },
+  },
+}
+</script>
+<style scoped>
+tr {
+  cursor: pointer;
+}
+
+.tbl-wrap #myTable .muted {
+  background-color: rgba(255, 255, 255, 0.3);
+}
+
+.tbl-wrap #myTable .selected {
+  color: #FFFFFF;
+  background-color: #213F9A;
+}
+</style>(파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/Manager/system/MenuAuthorManagement.vue (added)
+++ client/views/pages/Manager/system/MenuAuthorManagement.vue
@@ -0,0 +1,208 @@
+<template>
+  <div class="card">
+    <div class="card-body">
+      <h2 class="card-title">접근제어관리</h2>
+      <div class="flex align-top">
+        <div class="sch-form-wrap search">
+          <div class="tbl-wrap table-scroll">
+            <table id="myTable" class="tbl data">
+              <thead>
+                <tr>
+                  <th>권한목록</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr v-for="(item, idx) of authorLists" :key="idx" @click="findMenuAuthorList(item.authorCode)">
+                  <td v-if="item.useAt === 'Y'" :class="{ 'selected': selectedAuthor == item.authorCode }">{{ item.authorNm }}</td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
+        <div style="width: 100%;">
+          <div class="tbl-wrap chk-area">
+            <table id="myTable" class="tbl data">
+              <thead>
+                <tr>
+                  <th>메뉴명</th>
+                  <th>전체</th>
+                  <th>읽기</th>
+                  <th>쓰기</th>
+                  <th>수정</th>
+                  <th>삭제</th>
+                </tr>
+              </thead>
+              <tbody>
+                <template v-if="menuAuthorLists.length > 0">
+                  <tr v-for="(item, idx) in menuAuthorLists" :key="idx">
+                    <td>{{ item.menuNm }}</td>
+                    <td>
+                      <div class="form-check">
+                        <input type="checkbox" :id="`all_${idx}`" :checked="isAllChecked(item)" @change="toggleAll(idx)" />
+                        <label :for="`all_${idx}`"></label>
+                      </div>
+                    </td>
+                    <td>
+                      <div class="form-check">
+                        <input type="checkbox" :id="`redng_${idx}`" :checked="item.redng === 'Y'" @change="updatePermission(idx, 'redng', $event)" />
+                        <label :for="`redng_${idx}`"></label>
+                      </div>
+                    </td>
+                    <td>
+                      <div class="form-check">
+                        <input type="checkbox" :id="`write_${idx}`" :checked="item.write === 'Y'" @change="updatePermission(idx, 'write', $event)" />
+                        <label :for="`write_${idx}`"></label>
+                      </div>
+                    </td>
+                    <td>
+                      <div class="form-check">
+                        <input type="checkbox" :id="`updt_${idx}`" :checked="item.updt === 'Y'" @change="updatePermission(idx, 'updt', $event)" />
+                        <label :for="`updt_${idx}`"></label>
+                      </div>
+                    </td>
+                    <td>
+                      <div class="form-check">
+                        <input type="checkbox" :id="`delete_${idx}`" :checked="item.delete === 'Y'" @change="updatePermission(idx, 'delete', $event)" />
+                        <label :for="`delete_${idx}`"></label>
+                      </div>
+                    </td>
+                  </tr>
+                </template>
+                <template v-else>
+                  <tr>
+                    <td colspan="6">조회된 정보가 없습니다.</td>
+                  </tr>
+                </template>
+              </tbody>
+            </table>
+          </div>
+          <div>
+            <button type="button" class="btn sm secondary" @click="fnUpdate">저장</button>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+// API
+import { findAuthorsProc } from '../../../../resources/api/author'
+import { findMenuAuthorsProc, updateMenuAuthorsProc } from '../../../../resources/api/menuAuthor'
+
+export default {
+  data() {
+    return {
+      selectedAuthor: null,
+
+      authorLists: [], // 권한 목록 정보
+      menuAuthorLists: [], // 메뉴 권한 목록 정보
+    }
+  },
+
+  mounted() {
+    this.findAuthorList(); // 권한 목록 조회
+  },
+
+  methods: {
+    // 권한 목록 조회
+    async findAuthorList() {
+      try {
+        const response = await findAuthorsProc();
+        const result = response.data.data;
+
+        this.authorLists = result.authors;
+      } catch (error) {
+        if (error.response) {
+          alert(error.response.data.message);
+        } else {
+          alert("에러가 발생했습니다.");
+        }
+        console.error(error.message);
+      };
+    },
+
+    // 메뉴 권한 목록 조회
+    async findMenuAuthorList(code) {
+      try {
+        this.selectedAuthor = code;
+
+        const response = await findMenuAuthorsProc(this.selectedAuthor);
+        const result = response.data.data;
+
+        this.menuAuthorLists = result.menuAuthors;
+      } catch (error) {
+        if (error.response) {
+          alert(error.response.data.message);
+        } else {
+          alert("에러가 발생했습니다.");
+        }
+        console.error(error.message);
+      };
+    },
+
+    // 개별 권한 업데이트
+    updatePermission(index, permission, event) {
+      this.menuAuthorLists[index][permission] = event.target.checked ? 'Y' : 'N';
+    },
+
+    // 전체 선택/해제
+    toggleAll(index) {
+      const item = this.menuAuthorLists[index];
+      const allChecked = this.isAllChecked(item);
+      const newValue = allChecked ? 'N' : 'Y';
+
+      // 모든 권한을 동일하게 설정
+      item.redng = newValue;
+      item.write = newValue;
+      item.updt = newValue;
+      item.delete = newValue;
+    },
+
+    // 전체 선택 상태 확인 (모든 권한이 'Y'인지 체크)
+    isAllChecked(item) {
+      return item.redng === 'Y' && item.write === 'Y' && item.updt === 'Y' && item.delete === 'Y';
+    },
+
+    // 수정
+    async fnUpdate() {
+      try {
+        let data = [];
+        for (let menuAuthor of this.menuAuthorLists) {
+          data.push({
+            authorCode: menuAuthor.authorCode,
+            menuId: menuAuthor.menuId,
+            redng: menuAuthor.redng,
+            write: menuAuthor.write,
+            updt: menuAuthor.updt,
+            delete: menuAuthor.delete,
+          })
+        }
+
+        const response = await updateMenuAuthorsProc(data);
+        const result = response.data.data;
+
+        alert("수정되었습니다.");
+
+        this.findMenuAuthorList(this.selectedAuthor); // 목록 조회
+      } catch (error) {
+        if (error.response) {
+          alert(error.response.data.message);
+        } else {
+          alert("에러가 발생했습니다.");
+        }
+        console.error(error.message);
+      };
+    },
+  }
+}
+</script>
+<style scoped>
+tr {
+  cursor: pointer;
+}
+
+.tbl-wrap #myTable .selected {
+  color: #FFFFFF;
+  background-color: #213F9A;
+}
+</style>(파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/Manager/system/accessControlManagement.vue (deleted)
--- client/views/pages/Manager/system/accessControlManagement.vue
@@ -1,224 +0,0 @@
-<template>
-  <div class="card ">
-    <div class="card-body ">
-      <h2 class="card-title">접근제어관리</h2>
-      <div class="flex align-top">
-        <div class="sch-form-wrap search">
-
-          <div class="tbl-wrap table-scroll">
-            <table id="myTable" class="tbl data">
-              <!-- 동적으로 <th> 생성 -->
-              <thead>
-                <tr>
-                  <th>권한목록 </th>
-                </tr>
-              </thead>
-              <!-- 동적으로 <td> 생성 -->
-              <tbody>
-                <tr v-for="(item, index) in listData" :key="index">
-                  <td></td>
-                </tr>
-              </tbody>
-            </table>
-
-          </div>
-        </div>
-
-        <div style="width: 100%;">
-          <div class="tbl-wrap chk-area">
-            <table id="myTable" class="tbl data">
-
-              <thead>
-                <tr>
-                  <th>메뉴명</th>
-                  <th>전체</th>
-                  <th>읽기</th>
-                  <th>쓰기</th>
-                  <th>수정</th>
-                  <th>삭제</th>
-                </tr>
-              </thead>
-              <!-- 동적으로 <td> 생성 -->
-              <tbody>
-                <tr v-for="(item, index) in listData" :key="index">
-      <td>{{ item.menuName }}</td>
-      <td>
-        <div class="form-check">
-          <input
-            type="checkbox"
-            :id="`all_${index}`"
-            v-model="item.permissions.all"
-            @change="toggleAll(index)"
-          />
-          <label :for="`all_${index}`"></label>
-        </div>
-      </td>
-      <td>
-        <div class="form-check">
-          <input
-            type="checkbox"
-            :id="`read_${index}`"
-            v-model="item.permissions.read"
-          />
-          <label :for="`read_${index}`"></label>
-        </div>
-      </td>
-      <td>
-        <div class="form-check">
-          <input
-            type="checkbox"
-            :id="`write_${index}`"
-            v-model="item.permissions.write"
-          />
-          <label :for="`write_${index}`"></label>
-        </div>
-      </td>
-      <td>
-        <div class="form-check">
-          <input
-            type="checkbox"
-            :id="`edit_${index}`"
-            v-model="item.permissions.edit"
-          />
-          <label :for="`edit_${index}`"></label>
-        </div>
-      </td>
-      <td>
-        <div class="form-check">
-          <input
-            type="checkbox"
-            :id="`delete_${index}`"
-            v-model="item.permissions.delete"
-          />
-          <label :for="`delete_${index}`"></label>
-        </div>
-      </td>
-    </tr>
-              </tbody>
-            </table>
-
-          </div>
-        </div>
-      </div>
-    </div>
-
-  </div>
-
-</template>
-
-<script>
-import GoogleCalendar from "../../../component/GoogleCalendar.vue"
-import { SearchOutlined } from '@ant-design/icons-vue';
-export default {
-  data() {
-    return {
-      require: "/client/resources/img/require.png",
-      h3icon: "/client/resources/img/h3icon.png",
-      photoicon: "/client/resources/img/photo_icon.png",
-      img1: "/client/resources/img/img.png",
-      icon1: "/client/resources/img/icon.png",
-      dateicon: "/client/resources/img/date.png",
-      startbtn: "/client/resources/img/start.png",
-      stopbtn: "/client/resources/img/stop.png",
-      moreicon: "/client/resources/img/more.png",
-      today: new Date().toLocaleDateString('ko-KR', {
-        year: 'numeric',
-        month: '2-digit',
-        day: '2-digit',
-        weekday: 'short',
-      }),
-      time: this.getCurrentTime(),
-      listData: [
-      {
-          menuName: '메뉴1',
-          permissions: {
-            all: false,
-            read: false,
-            write: false,
-            edit: false,
-            delete: false,
-          },
-        },
-        {
-          menuName: '메뉴2',
-          permissions: {
-            all: false,
-            read: false,
-            write: false,
-            edit: false,
-            delete: false,
-          },
-        },
-      ]
-    }
-  },
-  components: {
-    SearchOutlined
-  },
-  methods: {
-    toggleAll(index) {
-      const all = this.listData[index].permissions.all;
-      this.listData[index].permissions.read = all;
-      this.listData[index].permissions.write = all;
-      this.listData[index].permissions.edit = all;
-      this.listData[index].permissions.delete = all;
-    },
-    formatBudget(amount) {
-      return new Intl.NumberFormat().format(amount) + ' 원';
-    },
-    isPastPeriod(period) {
-      // 예: '2025-05-01 ~ 2025-05-03' → 종료일 추출
-      const endDateStr = period.split('~')[1]?.trim();
-      if (!endDateStr) return false;
-
-      const endDate = new Date(endDateStr);
-      const today = new Date();
-
-      // 현재 날짜보다 과거면 true
-      return endDate < today;
-    },
-    getStatusClass(status) {
-      return status === 'active' ? 'status-active' : 'status-inactive';
-    },
-    getStatusClass(status) {
-      if (status === '미진행') return 'status-pending';
-      if (status === '진행중') return 'status-approved';
-
-      // Default empty string
-      return '';
-    },
-    getCurrentTime() {
-      const now = new Date();
-      const hours = String(now.getHours()).padStart(2, '0');
-      const minutes = String(now.getMinutes()).padStart(2, '0');
-      const seconds = String(now.getSeconds()).padStart(2, '0');
-      return `${hours}:${minutes}:${seconds}`;
-    },
-    getCategoryClass(category) {
-      switch (category) {
-        case '용역': return 'category-service';
-        case '내부': return 'category-internal';
-        case '국가과제': return 'category-government';
-        default: return '';
-      }
-    },
-  },
-  watch: {
-
-  },
-  computed: {
-
-  },
-  mounted() {
-    console.log('main mounted');
-    setInterval(() => {
-      this.time = this.getCurrentTime();
-    }, 1000);
-  }
-}
-</script>
-<style scoped>
-tr {
-  cursor: pointer;
-}
-</style>(파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/Manager/system/system.vue (deleted)
--- client/views/pages/Manager/system/system.vue
@@ -1,87 +0,0 @@
-<template>
-  <div class="sidemenu">
-    <div class="myinfo simple">
-      <div class="name-box">
-        <div class="img-area">
-          <div><img :src="photoicon" alt="">
-            <p class="name">OOO과장</p>
-          </div>
-          <div class="info">
-            <p>솔루션 개발팀</p>
-            <i class="fa-bars"></i>
-            <p>팀장</p>
-          </div>
-        </div>
-      </div>
-
-
-        <ul class="menu-box danil">
-             <router-link :to="{ name: 'userManagement' }" exact-active-class="active-link" v-slot="{ isExactActive }">
-                <li><p>사용자권한관리</p>
-                <div class="icon"><img :src="menuicon" alt=""></div></li>
-              </router-link>
-              <router-link :to="{ name: 'accessControlManagement' }" exact-active-class="active-link" v-slot="{ isExactActive }">
-                <li>
-                <p>접근제어관리</p>
-                <div class="icon"><img :src="menuicon" alt=""></div>
-              </li>
-              </router-link>
-              <router-link :to="{ name: 'commonCodeManagement' }" exact-active-class="active-link" v-slot="{ isExactActive }">
-                <li>
-                <p>공통코드관리</p>
-                <div class="icon"><img :src="menuicon" alt=""></div>
-              </li>
-              </router-link>
-           
-            
-          </ul>
-          
-      
-    </div>
-  </div>
-  <!-- End Page Title -->
-  <div class="content">
-    <router-view></router-view>
-
-  </div>
-</template>
-
-<script>
-import { ref } from 'vue';
-
-export default {
-  data() {
-    return {
-      photoicon: "/client/resources/img/photo_icon.png",
-      menuicon: "/client/resources/img/menuicon2.png",
-      topmenuicon: "/client/resources/img/topmenuicon.png",
-      // 데이터 초기화
-      years: [2023, 2024, 2025], // 연도 목록
-      months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], // 월 목록
-      selectedYear: '',
-      selectedMonth: '',
-      DeptData: [
-        { member: '', deptNM: '', acceptTerms: false },
-        // 더 많은 데이터 추가...
-      ],
-      filteredData: [],
-    };
-  },
-  computed: {
-  },
-  methods: {
-
-    // 페이지 변경
-    changePage(page) {
-      this.currentPage = page;
-    },
-  },
-  created() {
-  },
-  mounted() {
-
-  },
-};
-</script>
-
-<style scoped></style>
 
client/views/pages/Manager/system/userManagement.vue (deleted)
--- client/views/pages/Manager/system/userManagement.vue
@@ -1,176 +0,0 @@
-<template>
-  <div class="card ">
-    <div class="card-body ">
-      <h2 class="card-title">사용자권한관리</h2>
-      <div class="flex align-top">
-        <div class="sch-form-wrap search">
-
-          <div class="tbl-wrap table-scroll">
-            <table id="myTable" class="tbl data">
-              <!-- 동적으로 <th> 생성 -->
-              <thead>
-                <tr>
-                  <th>권한목록 </th>
-                </tr>
-              </thead>
-              <!-- 동적으로 <td> 생성 -->
-              <tbody>
-                <tr v-for="(item, index) in listData" :key="index">
-                  <td></td>
-                </tr>
-              </tbody>
-            </table>
-
-          </div>
-        </div>
-
-        <div style="width: 100%;">
-          <div class=" sch-form-wrap title-wrap">
-            <h3><img :src="h3icon" alt="">권한 정보</h3>
-            <div class="buttons" style="margin: 0;">
-              <button type="submit" class="btn sm sm tertiary">신규</button>
-              <button type="reset" class="btn sm sm secondary">등록</button>
-              <button type="delete" class="btn sm sm btn-red">삭제</button>
-            </div>
-          </div>
-          <form class="row g-3 pt-3  needs-validation detail" @submit.prevent="handleSubmit"
-            style="margin-bottom: 3rem;">
-            <div class="col-12">
-              <label for="purpose" class="form-label">
-                <p>권한코드
-                <p class="require"><img :src="require" alt=""></p>
-                </p>
-              </label>
-              <input type="text" class="form-control" id="purpose" v-model="purpose" />
-            </div>
-            <div class="col-12">
-              <label for="purpose" class="form-label">
-                <p>권한명
-                <p class="require"><img :src="require" alt=""></p>
-                </p>
-              </label>
-              <input type="text" class="form-control" id="purpose" v-model="purpose" />
-            </div>
-
-            <div class="col-12 chuljang ">
-              <label for="prvonsh" class="form-label">권한설명</label>
-              <input type="text" class="form-control textarea" id="reason" v-model="reason" />
-            </div>
-            <div class="col-12 border-x input-radio">
-              <label for="prvonsh" class="form-label"> <p>사용여부
-                <p class="require"><img :src="require" alt=""></p>
-                </p></label>
-              <div class="chk-area">
-                <div class="form-check">
-                  <input type="radio" name="rdo_1" id="rdo_1">
-                  <label for="rdo_1">사용</label>
-                </div>
-                <div class="form-check">
-                  <input type="radio" name="rdo_1" id="rdo_2" checked>
-                  <label for="rdo_2">미사용</label>
-                </div>
-              </div>
-            </div>
-
-
-          </form>
-        </div>
-      </div>
-    </div>
-
-  </div>
-
-</template>
-
-<script>
-import GoogleCalendar from "../../../component/GoogleCalendar.vue"
-import { SearchOutlined } from '@ant-design/icons-vue';
-export default {
-  data() {
-    return {
-      require: "/client/resources/img/require.png",
-      h3icon: "/client/resources/img/h3icon.png",
-      photoicon: "/client/resources/img/photo_icon.png",
-      img1: "/client/resources/img/img.png",
-      icon1: "/client/resources/img/icon.png",
-      dateicon: "/client/resources/img/date.png",
-      startbtn: "/client/resources/img/start.png",
-      stopbtn: "/client/resources/img/stop.png",
-      moreicon: "/client/resources/img/more.png",
-      today: new Date().toLocaleDateString('ko-KR', {
-        year: 'numeric',
-        month: '2-digit',
-        day: '2-digit',
-        weekday: 'short',
-      }),
-      time: this.getCurrentTime(),
-      listData: Array.from({ length: 20 }, (_, i) => ({
-        department: `부서 ${i + 1}`,
-        name: `이름 ${i + 1}`,
-        position: `직급 ${i + 1}`
-      }))
-    }
-  },
-  components: {
-    SearchOutlined
-  },
-  methods: {
-    formatBudget(amount) {
-      return new Intl.NumberFormat().format(amount) + ' 원';
-    },
-    isPastPeriod(period) {
-      // 예: '2025-05-01 ~ 2025-05-03' → 종료일 추출
-      const endDateStr = period.split('~')[1]?.trim();
-      if (!endDateStr) return false;
-
-      const endDate = new Date(endDateStr);
-      const today = new Date();
-
-      // 현재 날짜보다 과거면 true
-      return endDate < today;
-    },
-    getStatusClass(status) {
-      return status === 'active' ? 'status-active' : 'status-inactive';
-    },
-    getStatusClass(status) {
-      if (status === '미진행') return 'status-pending';
-      if (status === '진행중') return 'status-approved';
-
-      // Default empty string
-      return '';
-    },
-    getCurrentTime() {
-      const now = new Date();
-      const hours = String(now.getHours()).padStart(2, '0');
-      const minutes = String(now.getMinutes()).padStart(2, '0');
-      const seconds = String(now.getSeconds()).padStart(2, '0');
-      return `${hours}:${minutes}:${seconds}`;
-    },
-    getCategoryClass(category) {
-      switch (category) {
-        case '용역': return 'category-service';
-        case '내부': return 'category-internal';
-        case '국가과제': return 'category-government';
-        default: return '';
-      }
-    },
-  },
-  watch: {
-
-  },
-  computed: {
-
-  },
-  mounted() {
-    console.log('main mounted');
-    setInterval(() => {
-      this.time = this.getCurrentTime();
-    }, 1000);
-  }
-}
-</script>
-<style scoped>
-tr {
-  cursor: pointer;
-}
-</style>(파일 끝에 줄바꿈 문자 없음)
Add a comment
List