로그인 백엔드 구현 및 로그인 프론트 기능 구현
This commit is contained in:
parent
001bba7cb1
commit
fcfbd1fd96
|
|
@ -1,8 +1,4 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import 'ant-design-vue/dist/reset.css';
|
|
||||||
// import '~/assets/font/PretendardGOV/font.css';
|
|
||||||
// import '~/assets/css/index.css';
|
|
||||||
// import 'dayjs/locale/ko';
|
|
||||||
import 'uno.css';
|
import 'uno.css';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
import axios, { type AxiosError, type AxiosResponse } from 'axios';
|
||||||
|
// import { useAuthStore } from '~/stores/login';
|
||||||
|
// import { useDefaultStore, useLoadingStore } from '~/stores';
|
||||||
|
|
||||||
|
const baseURL = import.meta.env.VITE_API_URL as string;
|
||||||
|
|
||||||
|
export const useAxios = () => {
|
||||||
|
// const loadingStore = useLoadingStore();
|
||||||
|
// const defaultStore = useDefaultStore();
|
||||||
|
// const { siteInfo } = storeToRefs(defaultStore);
|
||||||
|
// const authStore = useAuthStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const instance = axios.create({
|
||||||
|
baseURL,
|
||||||
|
withCredentials: true
|
||||||
|
});
|
||||||
|
|
||||||
|
instance.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
return Promise.resolve(config);
|
||||||
|
},
|
||||||
|
(error: AxiosError) => {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
instance.interceptors.response.use(
|
||||||
|
(response: AxiosResponse<any, any>) => {
|
||||||
|
return Promise.resolve(response);
|
||||||
|
},
|
||||||
|
(error: AxiosError) => {
|
||||||
|
if (error.status === 403) {
|
||||||
|
return router.push('/');
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import type { LoginReqType, LoginResType } from '~/types/login';
|
||||||
|
|
||||||
|
export const DEFAULT_AUTHENTICATION_VALUE: LoginReqType = {
|
||||||
|
memberId: '',
|
||||||
|
password: '',
|
||||||
|
remember: false
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DEFAULT_AUTHORIZATION_VALUE: LoginResType = {
|
||||||
|
memberName: '',
|
||||||
|
deptNm: '',
|
||||||
|
instNm: '',
|
||||||
|
menuList: [],
|
||||||
|
permitApiList: [],
|
||||||
|
authenticated: false
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
import type { CellRendererProps } from 'tui-grid/types/renderer';
|
||||||
|
|
||||||
|
export class ConditionButtonRenderer {
|
||||||
|
el: HTMLElement;
|
||||||
|
|
||||||
|
constructor(props: CellRendererProps) {
|
||||||
|
const { rowKey, grid } = props;
|
||||||
|
const { options } = props.columnInfo.renderer;
|
||||||
|
const data = grid.getRow(rowKey);
|
||||||
|
|
||||||
|
console.log(!data.baAnswerYn);
|
||||||
|
if (!data.baAnswerYn) {
|
||||||
|
const el = document.createElement('button');
|
||||||
|
el.className = 'ant-btn ant-btn-primary';
|
||||||
|
el.onclick = () => options?.onClick(data);
|
||||||
|
el.innerHTML = `<span>${options?.buttonName}</span>`;
|
||||||
|
this.el = el;
|
||||||
|
} else {
|
||||||
|
const el = document.createElement('span');
|
||||||
|
el.innerHTML = options?.spanName;
|
||||||
|
this.el = el;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeDestroy(): void {}
|
||||||
|
|
||||||
|
focused(): void {}
|
||||||
|
|
||||||
|
getElement(): Element {
|
||||||
|
return this.el;
|
||||||
|
}
|
||||||
|
|
||||||
|
mounted(parent: HTMLElement): void {}
|
||||||
|
|
||||||
|
render(props: CellRendererProps): void {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import type { CellRendererProps } from 'tui-grid/types/renderer';
|
||||||
|
|
||||||
|
export class ConditionIconButtonRenderer {
|
||||||
|
el: HTMLElement;
|
||||||
|
|
||||||
|
constructor(props: CellRendererProps) {
|
||||||
|
const { options } = props.columnInfo.renderer;
|
||||||
|
const data = props.grid.getRow(props.rowKey);
|
||||||
|
|
||||||
|
if (options?.condition(data)) {
|
||||||
|
const el = document.createElement('a');
|
||||||
|
// @ts-ignore
|
||||||
|
const { icon } = options;
|
||||||
|
el.innerHTML = `
|
||||||
|
<svg focusable="false" data-icon="${icon.name}" width="1em" height="2em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896">
|
||||||
|
<path d="${icon.icon.children[0].attrs.d}" />
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
|
el.className = 'ant-btn ant-btn-primary';
|
||||||
|
el.onclick = () => options?.onClick(data);
|
||||||
|
|
||||||
|
this.el = el;
|
||||||
|
} else {
|
||||||
|
this.el = document.createElement('span');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeDestroy(): void {}
|
||||||
|
|
||||||
|
focused(): void {}
|
||||||
|
|
||||||
|
getElement(): Element {
|
||||||
|
return this.el;
|
||||||
|
}
|
||||||
|
|
||||||
|
mounted(parent: HTMLElement): void {}
|
||||||
|
|
||||||
|
render(props: CellRendererProps): void {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
import type { CellRendererProps } from 'tui-grid/types/renderer';
|
||||||
|
|
||||||
|
export class FunctionalButtonRenderer {
|
||||||
|
el: HTMLElement;
|
||||||
|
|
||||||
|
constructor(props: CellRendererProps) {
|
||||||
|
const el = document.createElement('button');
|
||||||
|
const options = props.columnInfo.renderer.options;
|
||||||
|
const data = props.grid.getRow(props.rowKey);
|
||||||
|
el.className = 'ant-btn ant-btn-primary';
|
||||||
|
el.onclick = () => options?.onClick(data);
|
||||||
|
el.innerHTML = `<span>${options?.buttonName}</span>`;
|
||||||
|
|
||||||
|
this.el = el;
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeDestroy(): void {}
|
||||||
|
|
||||||
|
focused(): void {}
|
||||||
|
|
||||||
|
getElement(): Element {
|
||||||
|
return this.el;
|
||||||
|
}
|
||||||
|
|
||||||
|
mounted(parent: HTMLElement): void {}
|
||||||
|
|
||||||
|
render(props: CellRendererProps): void {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
import type { CellEditorProps } from 'tui-grid/types/editor';
|
||||||
|
|
||||||
|
export class MaxLengthTextEditor {
|
||||||
|
el: HTMLInputElement;
|
||||||
|
|
||||||
|
constructor(props: CellEditorProps) {
|
||||||
|
const el = document.createElement('input');
|
||||||
|
el.type = 'text';
|
||||||
|
el.value = props.value as string;
|
||||||
|
el.maxLength = props.columnInfo.editor?.options?.maxlength;
|
||||||
|
el.placeholder = props.columnInfo.editor?.options?.placeholder;
|
||||||
|
|
||||||
|
this.el = el;
|
||||||
|
}
|
||||||
|
|
||||||
|
getElement() {
|
||||||
|
return this.el;
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue() {
|
||||||
|
return this.el.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.el.select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
import type { CellRendererProps } from 'tui-grid/types/renderer';
|
||||||
|
import type { MenuType } from '~/types/sys/menu';
|
||||||
|
|
||||||
|
export class MenuSatisChargerRenderer {
|
||||||
|
el: HTMLElement;
|
||||||
|
|
||||||
|
constructor(props: CellRendererProps) {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
|
||||||
|
el.style['gap'] = '8px';
|
||||||
|
el.className = 'flex justify-center';
|
||||||
|
|
||||||
|
const { rowKey, grid } = props;
|
||||||
|
const options = props.columnInfo.renderer.options as any;
|
||||||
|
|
||||||
|
const data = grid.getRow(rowKey) as unknown as MenuType;
|
||||||
|
if (!data.menuType.endsWith('API')) {
|
||||||
|
if (data?.menuMngId) {
|
||||||
|
const { onView, onDelete } = options;
|
||||||
|
const viewLink = document.createElement('a');
|
||||||
|
const deleteLink = document.createElement('a');
|
||||||
|
|
||||||
|
viewLink.innerHTML = '보기';
|
||||||
|
viewLink.onclick = () => onView(data);
|
||||||
|
|
||||||
|
deleteLink.innerHTML = '삭제';
|
||||||
|
deleteLink.onclick = () => onDelete(data);
|
||||||
|
|
||||||
|
el.append(viewLink, deleteLink);
|
||||||
|
} else {
|
||||||
|
const { onEdit } = options;
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.innerHTML = '등록';
|
||||||
|
link.onclick = () => onEdit(data);
|
||||||
|
|
||||||
|
el.append(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.el = el;
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeDestroy(): void {}
|
||||||
|
|
||||||
|
focused(): void {}
|
||||||
|
|
||||||
|
getElement(): Element {
|
||||||
|
return this.el;
|
||||||
|
}
|
||||||
|
|
||||||
|
mounted(parent: HTMLElement): void {}
|
||||||
|
|
||||||
|
render(props: CellRendererProps): void {
|
||||||
|
this.el.innerHTML = '';
|
||||||
|
const { rowKey, grid } = props;
|
||||||
|
const options = props.columnInfo.renderer.options as any;
|
||||||
|
|
||||||
|
const data = grid.getRow(rowKey) as unknown as MenuType;
|
||||||
|
if (!data.menuType.endsWith('API')) {
|
||||||
|
if (data?.menuMngId) {
|
||||||
|
const { onView, onDelete } = options;
|
||||||
|
const viewLink = document.createElement('a');
|
||||||
|
const deleteLink = document.createElement('a');
|
||||||
|
|
||||||
|
viewLink.innerHTML = '보기';
|
||||||
|
viewLink.onclick = () => onView(data);
|
||||||
|
|
||||||
|
deleteLink.innerHTML = '삭제';
|
||||||
|
deleteLink.onclick = () => onDelete(data);
|
||||||
|
|
||||||
|
this.el.append(viewLink, deleteLink);
|
||||||
|
} else {
|
||||||
|
const { onEdit } = options;
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.innerHTML = '등록';
|
||||||
|
link.onclick = () => onEdit(data);
|
||||||
|
|
||||||
|
this.el.append(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
import type { CellRendererProps } from 'tui-grid/types/renderer';
|
||||||
|
|
||||||
|
export class RadioHeaderRenderer {
|
||||||
|
el: HTMLElement;
|
||||||
|
|
||||||
|
constructor(props: CellRendererProps) {
|
||||||
|
const { rowKey, grid } = props;
|
||||||
|
|
||||||
|
const { options } = props.columnInfo.renderer;
|
||||||
|
const data = grid.getRow(rowKey);
|
||||||
|
|
||||||
|
const el = document.createElement('input');
|
||||||
|
el.name = 'gridRadio';
|
||||||
|
el.type = 'radio';
|
||||||
|
el.className = '';
|
||||||
|
el.addEventListener('change', () => options?.onChange(data));
|
||||||
|
|
||||||
|
this.el = el;
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeDestroy(): void {}
|
||||||
|
|
||||||
|
focused(): void {}
|
||||||
|
|
||||||
|
getElement(): Element {
|
||||||
|
return this.el;
|
||||||
|
}
|
||||||
|
|
||||||
|
mounted(parent: HTMLElement): void {}
|
||||||
|
|
||||||
|
render(props: CellRendererProps): void {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
import type { OptPreset } from 'tui-grid/types/options';
|
||||||
|
|
||||||
|
export const TUI_GRID_THEME: OptPreset = {
|
||||||
|
selection: {
|
||||||
|
background: '#4daaf9',
|
||||||
|
border: '#004082'
|
||||||
|
},
|
||||||
|
scrollbar: {
|
||||||
|
background: '#f5f5f5',
|
||||||
|
thumb: '#d9d9d9',
|
||||||
|
active: '#c1c1c1'
|
||||||
|
},
|
||||||
|
outline: {
|
||||||
|
border: '#e1e2e5'
|
||||||
|
},
|
||||||
|
area: {
|
||||||
|
header: {
|
||||||
|
border: '#e1e2e5',
|
||||||
|
background: '#f8f8f9'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
even: {
|
||||||
|
background: '#EFFAFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cell: {
|
||||||
|
normal: {
|
||||||
|
background: 'white',
|
||||||
|
border: '#eee',
|
||||||
|
showVerticalBorder: true
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
background: '#f8f8f9',
|
||||||
|
showHorizontalBorder: true,
|
||||||
|
showVerticalBorder: true
|
||||||
|
},
|
||||||
|
rowHeader: {
|
||||||
|
border: '#e1e2e5',
|
||||||
|
background: '#f8f8f9',
|
||||||
|
showHorizontalBorder: false,
|
||||||
|
showVerticalBorder: false
|
||||||
|
},
|
||||||
|
editable: {
|
||||||
|
// 수정 가능 셀 색상은 아래에
|
||||||
|
background: 'white'
|
||||||
|
},
|
||||||
|
selectedHeader: {
|
||||||
|
background: '#e0e0e0'
|
||||||
|
},
|
||||||
|
focused: {
|
||||||
|
border: '#418ed4'
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
text: '#333',
|
||||||
|
background: 'white'
|
||||||
|
},
|
||||||
|
invalid: {
|
||||||
|
background: '#D60440'
|
||||||
|
},
|
||||||
|
required: {
|
||||||
|
background: 'white'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue';
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
layout: 'empty'
|
|
||||||
});
|
|
||||||
|
|
||||||
const dummy = ref('phone');
|
|
||||||
const domain = ref(''); // 도메인 선택 부분
|
|
||||||
|
|
||||||
const domains = [
|
|
||||||
{ value: 'gmail.com', label: 'gmail.com' },
|
|
||||||
{ value: 'naver.com', label: 'naver.com' },
|
|
||||||
{ value: 'daum.net', label: 'daum.net' },
|
|
||||||
{ value: '직접 입력', label: '직접 입력' }
|
|
||||||
];
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<a-row justify="center" class="w-full h-full items-center">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-card
|
|
||||||
bordered
|
|
||||||
style="padding: 24px; background-color: #f9f9f9; border-radius: 8px"
|
|
||||||
>
|
|
||||||
<!-- 아이디 찾기 타이틀 -->
|
|
||||||
<h2 style="text-align: center; font-weight: bold; font-size: 24px">
|
|
||||||
아이디 찾기
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<!-- 검색 방법 선택 -->
|
|
||||||
<div style="display: flex; justify-content: center; margin: 16px 0">
|
|
||||||
<a-radio-group v-model:value="dummy">
|
|
||||||
<a-radio value="phone">휴대폰으로 찾기</a-radio>
|
|
||||||
<a-radio value="email">이메일로 찾기</a-radio>
|
|
||||||
</a-radio-group>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a-form :colon="false" label-align="left">
|
|
||||||
<!-- 이름 -->
|
|
||||||
<a-form-item
|
|
||||||
label="이름"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
:wrapper-col="{ span: 20 }"
|
|
||||||
>
|
|
||||||
<a-input placeholder="이름을 입력하세요" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<!-- 자동입력방지문자 (라디오 값이 'phone'일 때만 표시) -->
|
|
||||||
<a-form-item
|
|
||||||
v-if="dummy === 'phone'"
|
|
||||||
label="자동입력방지문자"
|
|
||||||
:label-col="{ span: 6 }"
|
|
||||||
:wrapper-col="{ span: 18 }"
|
|
||||||
>
|
|
||||||
<div style="display: flex; gap: 8px">
|
|
||||||
<a-input
|
|
||||||
disabled
|
|
||||||
value="564866"
|
|
||||||
style="width: 100px; text-align: center"
|
|
||||||
/>
|
|
||||||
<a-button>새로고침</a-button>
|
|
||||||
<a-button>음성듣기</a-button>
|
|
||||||
</div>
|
|
||||||
<a-input
|
|
||||||
placeholder="자동입력 방지문자를 입력하세요."
|
|
||||||
style="margin-top: 8px"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<!-- 휴대전화 (라디오 값이 'phone'일 때만 표시) -->
|
|
||||||
<a-form-item
|
|
||||||
v-if="dummy === 'phone'"
|
|
||||||
label="휴대전화"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
:wrapper-col="{ span: 20 }"
|
|
||||||
>
|
|
||||||
<div style="display: flex; gap: 8px">
|
|
||||||
<a-input style="width: 60px" maxlength="4" />
|
|
||||||
<a-input style="width: 60px" maxlength="4" />
|
|
||||||
<a-input style="width: 60px" maxlength="4" />
|
|
||||||
<a-button>인증번호</a-button>
|
|
||||||
</div>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item
|
|
||||||
v-if="dummy === 'email'"
|
|
||||||
label="이메일"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
:wrapper-col="{ span: 20 }"
|
|
||||||
>
|
|
||||||
<!-- 이메일 입력 필드 -->
|
|
||||||
<a-input v-model="dummy" style="width: 120px" />
|
|
||||||
<span> @ </span>
|
|
||||||
<!-- 도메인 입력 필드 -->
|
|
||||||
<a-input v-model="dummy" style="width: 150px; margin-right: 18px" />
|
|
||||||
<!-- 직접 입력 드롭다운 -->
|
|
||||||
<a-select
|
|
||||||
v-model="domains"
|
|
||||||
style="width: 150px"
|
|
||||||
@change="handleDomainChange"
|
|
||||||
>
|
|
||||||
<a-select-option value="직접 입력">직접 입력</a-select-option>
|
|
||||||
<a-select-option value="gmail.com">gmail.com</a-select-option>
|
|
||||||
<a-select-option value="naver.com">naver.com</a-select-option>
|
|
||||||
<a-select-option value="daum.net">daum.net</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<!-- 인증번호 (라디오 값이 'phone'일 때만 표시) -->
|
|
||||||
<a-form-item
|
|
||||||
v-if="dummy === 'phone'"
|
|
||||||
label="인증번호"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
:wrapper-col="{ span: 20 }"
|
|
||||||
>
|
|
||||||
<a-input placeholder="인증번호를 입력하세요" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<!-- 아이디 찾기 버튼 -->
|
|
||||||
<div style="text-align: center; margin-top: 16px">
|
|
||||||
<a-button
|
|
||||||
type="primary"
|
|
||||||
style="background-color: #1e90ff; color: white; width: 100px"
|
|
||||||
>
|
|
||||||
아이디 찾기
|
|
||||||
</a-button>
|
|
||||||
</div>
|
|
||||||
</a-form>
|
|
||||||
</a-card>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</template>
|
|
||||||
|
|
@ -1,49 +1,28 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const useAuthStore = ref('');
|
import { useAuthStore } from '~/stores/login';
|
||||||
const remember = ref(false);
|
|
||||||
const memberId = ref('');
|
|
||||||
const password = ref('');
|
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'empty'
|
layout: 'empty'
|
||||||
});
|
});
|
||||||
|
|
||||||
// const router = useRouter();
|
const router = useRouter();
|
||||||
// const store = useAuthStore();
|
const store = useAuthStore();
|
||||||
// const { authentication } = storeToRefs(store);
|
const { loginRequest } = storeToRefs(store);
|
||||||
//
|
|
||||||
// onBeforeMount(() => {
|
|
||||||
// store.loadRemember();
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// onBeforeUnmount(() => {
|
|
||||||
// authentication.value = {
|
|
||||||
// ...authentication.value,
|
|
||||||
// memberId: ''
|
|
||||||
// };
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// watch(authentication.value, (newValue) => {
|
|
||||||
// if (newValue.remember) {
|
|
||||||
// store.setRemember();
|
|
||||||
// } else {
|
|
||||||
// store.initRemember();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
const login = async () => {
|
const login = async () => {
|
||||||
// try {
|
try {
|
||||||
// const { data } = await store.authenticate();
|
const { data } = await store.LoginAPI();
|
||||||
// store.authorize(data);
|
store.loginResponse = data;
|
||||||
//
|
console.log(JSON.stringify(store.loginResponse));
|
||||||
// await router.push('/');
|
|
||||||
// } catch (e) {
|
await router.push('/');
|
||||||
// message.error('아이디 또는 비밀번호를 확인해주세요.');
|
} catch (e) {
|
||||||
// }
|
message.error('아이디 또는 비밀번호를 확인해주세요.');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateLogin = computed(() => {
|
const validateLogin = computed(() => {
|
||||||
return false;
|
return loginRequest.value.memberId && loginRequest.value.password;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -64,7 +43,7 @@ const validateLogin = computed(() => {
|
||||||
<div style="height: 18px"></div>
|
<div style="height: 18px"></div>
|
||||||
|
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-checkbox v-model:checked="remember">
|
<a-checkbox v-model:checked="loginRequest.remember">
|
||||||
키보드보안 프로그램적용
|
키보드보안 프로그램적용
|
||||||
</a-checkbox>
|
</a-checkbox>
|
||||||
<div style="height: 10px"></div>
|
<div style="height: 10px"></div>
|
||||||
|
|
@ -78,7 +57,7 @@ const validateLogin = computed(() => {
|
||||||
<a-form :colon="false" label-align="left">
|
<a-form :colon="false" label-align="left">
|
||||||
<a-form-item label="아이디" :label-col="{ span: 5 }">
|
<a-form-item label="아이디" :label-col="{ span: 5 }">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="memberId"
|
v-model:value="loginRequest.memberId"
|
||||||
placeholder="아이디를 입력하세요"
|
placeholder="아이디를 입력하세요"
|
||||||
maxLength="20"
|
maxLength="20"
|
||||||
/>
|
/>
|
||||||
|
|
@ -86,10 +65,10 @@ const validateLogin = computed(() => {
|
||||||
|
|
||||||
<a-form-item label="비밀번호" :label-col="{ span: 5 }">
|
<a-form-item label="비밀번호" :label-col="{ span: 5 }">
|
||||||
<a-input-password
|
<a-input-password
|
||||||
v-model:value="password"
|
v-model:value="loginRequest.password"
|
||||||
placeholder="비밀번호를 입력하세요"
|
placeholder="비밀번호를 입력하세요"
|
||||||
maxLength="20"
|
maxLength="20"
|
||||||
@keyup.enter="login"
|
@keyup.enter="login()"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
|
|
@ -97,7 +76,7 @@ const validateLogin = computed(() => {
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
block
|
block
|
||||||
@click="login"
|
@click="login()"
|
||||||
:disabled="!validateLogin"
|
:disabled="!validateLogin"
|
||||||
style="font-weight: bold"
|
style="font-weight: bold"
|
||||||
>
|
>
|
||||||
|
|
@ -106,15 +85,15 @@ const validateLogin = computed(() => {
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<div style="display: flex; justify-content: center; margin-top: 16px">
|
<div style="display: flex; justify-content: center; margin-top: 16px">
|
||||||
<router-link to="/login/id" style="color: #1890ff"
|
<router-link to="#" style="color: #1890ff"
|
||||||
>아이디 찾기</router-link
|
>아이디 찾기</router-link
|
||||||
>
|
>
|
||||||
<span style="margin: 0 8px; color: #888">|</span>
|
<span style="margin: 0 8px; color: #888">|</span>
|
||||||
<router-link to="/login/pw" style="color: #1890ff"
|
<router-link to="#" style="color: #1890ff"
|
||||||
>비밀번호 찾기</router-link
|
>비밀번호 찾기</router-link
|
||||||
>
|
>
|
||||||
<span style="margin: 0 8px; color: #888">|</span>
|
<span style="margin: 0 8px; color: #888">|</span>
|
||||||
<router-link to="/login/join" style="color: #1890ff"
|
<router-link to="#" style="color: #1890ff"
|
||||||
>회원가입</router-link
|
>회원가입</router-link
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import locale from 'ant-design-vue/es/locale/ko_KR';
|
|
||||||
import { DEFAULT_THEME } from '~/constants/theme/ui';
|
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
const router = useRouter();
|
|
||||||
const adminJoinStore = ref('');
|
|
||||||
const member = ref('');
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
layout: 'empty'
|
|
||||||
});
|
|
||||||
|
|
||||||
const changeUrl = (activeKey: string) => {
|
|
||||||
router.push(activeKey);
|
|
||||||
};
|
|
||||||
|
|
||||||
function activeKey(key) {
|
|
||||||
console.log('Active Tab:', key);
|
|
||||||
}
|
|
||||||
|
|
||||||
const disabledInst = computed(() => {
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
const disabledCert = computed(() => {
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
const disabledForm = computed(() => {
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
const disabledCmptn = computed(() => {
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<a-config-provider :locale="locale" :theme="DEFAULT_THEME">
|
|
||||||
<a-row class="w-full h-full">
|
|
||||||
<a-row justify="center" class="w-full h-full items-center">
|
|
||||||
<a-col :span="15">
|
|
||||||
<a-card bordered class="p-5">
|
|
||||||
<a-typography-title :level="4" type="secondary" class="text-center">
|
|
||||||
참여기관 회원가입
|
|
||||||
</a-typography-title>
|
|
||||||
|
|
||||||
<a-tabs default-active-key="1" v-model:active-key="activeKey">
|
|
||||||
<a-tab-pane key="/admin/login/join/trms" tab="01.약관동의" />
|
|
||||||
<a-tab-pane
|
|
||||||
key="/admin/login/join/inst"
|
|
||||||
tab="02.기관선택"
|
|
||||||
:disabled="disabledInst"
|
|
||||||
/>
|
|
||||||
<a-tab-pane
|
|
||||||
key="/admin/login/join/cert"
|
|
||||||
tab="03.본인인증"
|
|
||||||
:disabled="disabledCert"
|
|
||||||
/>
|
|
||||||
<a-tab-pane
|
|
||||||
key="/admin/login/join"
|
|
||||||
tab="04.정보입력"
|
|
||||||
:disabled="disabledForm"
|
|
||||||
/>
|
|
||||||
<a-tab-pane
|
|
||||||
key="/admin/login/join/cmptn"
|
|
||||||
tab="05.가입완료"
|
|
||||||
:disabled="disabledCmptn"
|
|
||||||
/>
|
|
||||||
</a-tabs>
|
|
||||||
|
|
||||||
<slot />
|
|
||||||
</a-card>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</a-row>
|
|
||||||
</a-config-provider>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue';
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
layout: 'empty'
|
|
||||||
});
|
|
||||||
|
|
||||||
const dummy = ref<string>('phone');
|
|
||||||
const domain = ref(''); // 도메인 선택 부분
|
|
||||||
|
|
||||||
const domains = [
|
|
||||||
{ value: 'gmail.com', label: 'gmail.com' },
|
|
||||||
{ value: 'naver.com', label: 'naver.com' },
|
|
||||||
{ value: 'daum.net', label: 'daum.net' },
|
|
||||||
{ value: '직접 입력', label: '직접 입력' }
|
|
||||||
];
|
|
||||||
|
|
||||||
const handleDomainChange = (value) => {
|
|
||||||
if (value !== '직접 입력') {
|
|
||||||
domain.value = value;
|
|
||||||
} else {
|
|
||||||
domain.value = '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<a-row justify="center" class="w-full h-full items-center">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-card
|
|
||||||
bordered
|
|
||||||
style="padding: 24px; background-color: #f9f9f9; border-radius: 8px"
|
|
||||||
>
|
|
||||||
<!-- 아이디 찾기 타이틀 -->
|
|
||||||
<h2 style="text-align: center; font-weight: bold; font-size: 24px">
|
|
||||||
비밀번호 재설정
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<!-- 검색 방법 선택 -->
|
|
||||||
<div style="display: flex; justify-content: center; margin: 16px 0">
|
|
||||||
<a-radio-group v-model:value="dummy">
|
|
||||||
<a-radio defaultValue="phone">휴대폰으로 찾기</a-radio>
|
|
||||||
<a-radio value="email">이메일로 찾기</a-radio>
|
|
||||||
</a-radio-group>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a-form :colon="false" label-align="left">
|
|
||||||
<!-- 이름 -->
|
|
||||||
<a-form-item
|
|
||||||
label="이름"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
:wrapper-col="{ span: 20 }"
|
|
||||||
>
|
|
||||||
<a-input placeholder="이름을 입력하세요" />
|
|
||||||
</a-form-item>
|
|
||||||
<!-- 아이디 -->
|
|
||||||
<a-form-item
|
|
||||||
label="아이디"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
:wrapper-col="{ span: 20 }"
|
|
||||||
>
|
|
||||||
<a-input placeholder="아이디를 입력하세요" />
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<!-- 휴대전화 (라디오 값이 'phone'일 때만 표시) -->
|
|
||||||
<a-form-item
|
|
||||||
v-if="dummy === 'phone'"
|
|
||||||
label="휴대전화"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
:wrapper-col="{ span: 20 }"
|
|
||||||
>
|
|
||||||
<div style="display: flex; gap: 8px">
|
|
||||||
<a-input style="width: 90px" maxlength="4" />
|
|
||||||
<a-input style="width: 90px" maxlength="4" />
|
|
||||||
<a-input style="width: 90px" maxlength="4" />
|
|
||||||
</div>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item
|
|
||||||
v-if="dummy === 'email'"
|
|
||||||
label="이메일"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
:wrapper-col="{ span: 20 }"
|
|
||||||
>
|
|
||||||
<!-- 이메일 입력 필드 -->
|
|
||||||
<a-input v-model="dummy" style="width: 120px" />
|
|
||||||
<span> @ </span>
|
|
||||||
<!-- 도메인 입력 필드 -->
|
|
||||||
<a-input v-model="dummy" style="width: 150px; margin-right: 18px" />
|
|
||||||
<!-- 직접 입력 드롭다운 -->
|
|
||||||
<a-select
|
|
||||||
v-model="domains"
|
|
||||||
style="width: 150px"
|
|
||||||
@change="handleDomainChange"
|
|
||||||
>
|
|
||||||
<a-select-option value="직접 입력">직접 입력</a-select-option>
|
|
||||||
<a-select-option value="gmail.com">gmail.com</a-select-option>
|
|
||||||
<a-select-option value="naver.com">naver.com</a-select-option>
|
|
||||||
<a-select-option value="daum.net">daum.net</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<!-- 암호 찾기 버튼 -->
|
|
||||||
<div style="text-align: center; margin-top: 16px">
|
|
||||||
<a-button
|
|
||||||
v-if="dummy === 'phone'"
|
|
||||||
type="primary"
|
|
||||||
style="background-color: #1e90ff; color: white; width: 100px"
|
|
||||||
>
|
|
||||||
휴대폰 인증
|
|
||||||
</a-button>
|
|
||||||
<a-button
|
|
||||||
v-else
|
|
||||||
type="primary"
|
|
||||||
style="background-color: #1e90ff; color: white; width: 100px"
|
|
||||||
>
|
|
||||||
이메일 인증
|
|
||||||
</a-button>
|
|
||||||
</div>
|
|
||||||
</a-form>
|
|
||||||
</a-card>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</template>
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
import {useAxios} from "~/composables/useAxios";
|
||||||
|
import type {LoginRequestType, LoginResponseType} from "~/types/login";
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
import {
|
||||||
|
DEFAULT_AUTHENTICATION_VALUE,
|
||||||
|
DEFAULT_AUTHORIZATION_VALUE
|
||||||
|
} from '~/constants/login';
|
||||||
|
|
||||||
|
export const useAuthStore = defineStore('authStore', () => {
|
||||||
|
const loginRequest = ref<LoginRequestType>(
|
||||||
|
cloneDeep(DEFAULT_AUTHENTICATION_VALUE)
|
||||||
|
);
|
||||||
|
|
||||||
|
const loginResponse = ref<LoginResponseType>(
|
||||||
|
cloneDeep(DEFAULT_AUTHORIZATION_VALUE)
|
||||||
|
);
|
||||||
|
|
||||||
|
const LoginAPI = async () => {
|
||||||
|
return await useAxios().post(`/api/login`, loginRequest.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
loginRequest,
|
||||||
|
loginResponse,
|
||||||
|
LoginAPI
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import type { MenuType } from '../sys/menu';
|
||||||
|
|
||||||
|
export type LoginRequestType = {
|
||||||
|
memberId: string;
|
||||||
|
password: string;
|
||||||
|
remember: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LoginResponseType = {
|
||||||
|
memberName: string;
|
||||||
|
instNm: string;
|
||||||
|
deptNm: string;
|
||||||
|
menuList: AuthorizationMenuType[];
|
||||||
|
permitApiList: PermitApiType[];
|
||||||
|
authenticated: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AuthorizationMenuType = {
|
||||||
|
menuId: string;
|
||||||
|
upMenuId: string;
|
||||||
|
menuDepth: number;
|
||||||
|
menuName: string;
|
||||||
|
menuType: MenuType;
|
||||||
|
menuUrl: string;
|
||||||
|
bcId: string;
|
||||||
|
contentId: string;
|
||||||
|
|
||||||
|
children: AuthorizationMenuType[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PermitApiType = {
|
||||||
|
menuUrl: string;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
export type MenuType = {
|
||||||
|
menuId: string;
|
||||||
|
siteId: string;
|
||||||
|
upMenuId: string;
|
||||||
|
menuDepth: number;
|
||||||
|
menuOrder: number;
|
||||||
|
menuName: string;
|
||||||
|
menuType: 'MENU' | 'PAGE' | 'API' | 'TAB' | 'COMMON_MENU' | 'COMMON_API';
|
||||||
|
menuFeature: 'PAGE' | 'LIST' | 'DETAIL' | 'CREATE' | 'UPDATE' | 'DELETE';
|
||||||
|
menuLayout: string;
|
||||||
|
menuUrl: string;
|
||||||
|
menuMethod: string;
|
||||||
|
menuDescription: string;
|
||||||
|
menuLinkTarget: 'CURRENT' | 'BLANK';
|
||||||
|
menuUseSatisfaction: boolean;
|
||||||
|
menuUseMngInfo: boolean;
|
||||||
|
menuMngId: string;
|
||||||
|
menuStatus: 'ENABLED' | 'HIDDEN' | 'DISABLED';
|
||||||
|
delYn: boolean;
|
||||||
|
useYn: boolean;
|
||||||
|
frstRgtrId: string;
|
||||||
|
frstRegDt: string;
|
||||||
|
lastMdfrId: string;
|
||||||
|
lastMdfcnDt: string;
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue