시스템관리 > 권한관리 > 사이트관리 화면과 API 추가
This commit is contained in:
parent
aed1321d88
commit
843db73d12
|
|
@ -0,0 +1,28 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useAuthStore } from '~/stores/login';
|
||||||
|
import { some } from 'lodash-es';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
api: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
const { permitApiList } = storeToRefs(authStore);
|
||||||
|
|
||||||
|
const permit = computed(() => {
|
||||||
|
return some(permitApiList.value, (value) => {
|
||||||
|
return props.api === value.menuUrl;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template v-if="permit">
|
||||||
|
<slot />
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import 'tui-pagination/dist/tui-pagination.css';
|
||||||
|
import Pagination from 'tui-pagination/dist/tui-pagination';
|
||||||
|
import type { PaginationType } from '~/types/data/pagination';
|
||||||
|
|
||||||
|
const instance = ref();
|
||||||
|
const paginationRef = ref();
|
||||||
|
const props = defineProps<PaginationType>();
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
instance.value = new Pagination(paginationRef.value, {
|
||||||
|
totalItems: props.totalItems,
|
||||||
|
itemPerPage: props.itemsPerPage,
|
||||||
|
visiblePages: props.visiblePages ?? 10,
|
||||||
|
centerAlign: props.centerAlign ?? false,
|
||||||
|
usageStatistics: false
|
||||||
|
});
|
||||||
|
|
||||||
|
instance.value.on('beforeMove', (data: any) => {
|
||||||
|
emit('change', data.page);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props,
|
||||||
|
(newValue) => {
|
||||||
|
instance.value.setItemsPerPage(newValue.itemsPerPage);
|
||||||
|
instance.value.setTotalItems(newValue.itemsPerPage);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="paginationRef" class="tui-pagination"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Editor } from '@toast-ui/editor';
|
||||||
|
import '@toast-ui/editor/dist/toastui-editor.css';
|
||||||
|
|
||||||
|
const editorRef = ref();
|
||||||
|
const instance = ref();
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
const props = defineProps<{ initialValue: string }>();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
instance.value = new Editor({
|
||||||
|
el: editorRef.value,
|
||||||
|
initialValue: props.initialValue ?? '',
|
||||||
|
initialEditType: 'wysiwyg',
|
||||||
|
hideModeSwitch: true,
|
||||||
|
usageStatistics: false,
|
||||||
|
events: {
|
||||||
|
keyup: () => {
|
||||||
|
if (instance.value) {
|
||||||
|
emit('change', instance.value.getHTML());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="editorRef"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import 'tui-grid/dist/tui-grid.css';
|
||||||
|
import 'tui-date-picker/dist/tui-date-picker.min.css';
|
||||||
|
import Grid, { type RowKey } from 'tui-grid';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
import type { DataGridType } from '~/types/data/grid';
|
||||||
|
import type { GridEventName, OptRow } from 'tui-grid/types/options';
|
||||||
|
import { TUI_GRID_THEME } from '~/constants/theme/grid';
|
||||||
|
|
||||||
|
Grid.setLanguage('ko', {
|
||||||
|
display: {
|
||||||
|
noData: '조회된 데이터가 존재하지 않습니다.'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Grid.applyTheme('default', TUI_GRID_THEME);
|
||||||
|
|
||||||
|
const gridRef = ref();
|
||||||
|
const instance = ref<Grid>();
|
||||||
|
|
||||||
|
const props = defineProps<DataGridType>();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const gridOptions = {
|
||||||
|
el: gridRef.value,
|
||||||
|
columns: props.columns ?? [],
|
||||||
|
data: cloneDeep(props.data) ?? [],
|
||||||
|
bodyHeight: props.bodyHeight,
|
||||||
|
columnOptions: props.columnOptions,
|
||||||
|
keyColumnName: props.keyColumnName,
|
||||||
|
width: props.width,
|
||||||
|
heightResizable: props.heightResizable,
|
||||||
|
minBodyHeight: props.minBodyHeight,
|
||||||
|
rowHeight: props.rowHeight ?? 40,
|
||||||
|
minRowHeight: props.minRowHeight,
|
||||||
|
scrollX: props.scrollX,
|
||||||
|
scrollY: props.scrollY,
|
||||||
|
editingEvent: props.editingEvent,
|
||||||
|
tabMode: props.tabMode,
|
||||||
|
rowHeaders: props.rowHeaders,
|
||||||
|
summary: props.summary,
|
||||||
|
useClientSort: props.useClientSort,
|
||||||
|
selectionUnit: props.selectionUnit,
|
||||||
|
showDummyRows: props.showDummyRows,
|
||||||
|
copyOptions: props.copyOptions,
|
||||||
|
pageOptions: props.pageOptions,
|
||||||
|
treeColumnOptions: props.treeColumnOptions,
|
||||||
|
header: props.header,
|
||||||
|
usageStatistics: false,
|
||||||
|
disabled: props.disabled,
|
||||||
|
draggable: props.draggable,
|
||||||
|
contextMenu: props.contextMenu
|
||||||
|
};
|
||||||
|
|
||||||
|
if (props.onGridMounted) {
|
||||||
|
gridOptions['onGridMounted'] = props.onGridMounted;
|
||||||
|
}
|
||||||
|
if (props.onGridUpdated) {
|
||||||
|
gridOptions['onGridUpdated'] = props.onGridUpdated;
|
||||||
|
}
|
||||||
|
if (props.onGridBeforeDestroy) {
|
||||||
|
gridOptions['onGridBeforeDestroy'] = props.onGridBeforeDestroy;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.value = new Grid(gridOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
const appendRow = (value: OptRow) => {
|
||||||
|
instance.value?.appendRow(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const appendRows = (value: OptRow[]) => {
|
||||||
|
instance.value?.appendRows(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const prependRow = (value: OptRow) => {
|
||||||
|
instance.value?.prependRow(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCheckedRows = () => {
|
||||||
|
return instance.value?.getCheckedRows();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = () => {
|
||||||
|
return instance.value?.getData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const validate = () => {
|
||||||
|
return instance.value?.validate();
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetData = (data: OptRow[]) => {
|
||||||
|
instance.value?.resetData(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeRow = (data: RowKey) => {
|
||||||
|
instance.value?.removeRow(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeRows = (data: RowKey[]) => {
|
||||||
|
instance.value?.removeRows(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const on = (eventName: GridEventName, eventFunction: (event) => void) => {
|
||||||
|
instance.value?.on(eventName, eventFunction);
|
||||||
|
};
|
||||||
|
|
||||||
|
const off = (eventName: GridEventName) => {
|
||||||
|
instance.value?.off(eventName);
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
appendRow,
|
||||||
|
appendRows,
|
||||||
|
prependRow,
|
||||||
|
getCheckedRows,
|
||||||
|
getData,
|
||||||
|
validate,
|
||||||
|
resetData,
|
||||||
|
on,
|
||||||
|
off,
|
||||||
|
removeRow,
|
||||||
|
removeRows
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
(newValue) => {
|
||||||
|
resetData(newValue);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="gridRef" />
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { OptColumn, OptRowHeader } from 'tui-grid/types/options';
|
||||||
|
import { useSiteStore } from '~/stores/sys/site';
|
||||||
|
import type { SiteType } from '~/types/sys/site';
|
||||||
|
import { MaxLengthTextEditor } from '~/constants/theme/grid/MaxLengthTextEditor';
|
||||||
|
|
||||||
|
const siteStore = useSiteStore();
|
||||||
|
const { siteList } = storeToRefs(siteStore);
|
||||||
|
const gridRef = ref();
|
||||||
|
|
||||||
|
const gridRowHeaders: OptRowHeader[] = ['checkbox', 'rowNum'];
|
||||||
|
|
||||||
|
const columns: OptColumn[] = [
|
||||||
|
{
|
||||||
|
name: 'siteId',
|
||||||
|
header: '사이트 ID',
|
||||||
|
width: 80,
|
||||||
|
editor: {
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
validation: {
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'siteName',
|
||||||
|
header: '사이트명',
|
||||||
|
width: 120,
|
||||||
|
editor: {
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
validation: {
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'siteDescription',
|
||||||
|
header: '사이트 설명',
|
||||||
|
width: 200,
|
||||||
|
editor: {
|
||||||
|
type: 'text'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'siteDomain',
|
||||||
|
header: '사이트 도메인',
|
||||||
|
width: 150,
|
||||||
|
editor: {
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
validation: {
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'siteType',
|
||||||
|
header: '사이트 형식',
|
||||||
|
width: 80,
|
||||||
|
formatter: 'listItemText',
|
||||||
|
editor: {
|
||||||
|
type: 'select',
|
||||||
|
options: {
|
||||||
|
listItems: [
|
||||||
|
{ text: '사용자', value: 'USER' },
|
||||||
|
{ text: '관리자', value: 'ADMIN' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
validation: {
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sitePrefix',
|
||||||
|
header: '사이트 구분',
|
||||||
|
width: 120,
|
||||||
|
editor: {
|
||||||
|
type: 'select',
|
||||||
|
options: {
|
||||||
|
listItems: [
|
||||||
|
{ text: '대국민포털', value: 'portal' },
|
||||||
|
{ text: '참여기관포털', value: 'admin' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'lgnUrl',
|
||||||
|
header: '로그인 URL',
|
||||||
|
width: 120,
|
||||||
|
editor: {
|
||||||
|
type: MaxLengthTextEditor,
|
||||||
|
options: {
|
||||||
|
maxlength: 200,
|
||||||
|
placeholder: '로그인 URL을 입력해주세요.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'bscUrl',
|
||||||
|
header: '기본 URL',
|
||||||
|
width: 120,
|
||||||
|
editor: {
|
||||||
|
type: MaxLengthTextEditor,
|
||||||
|
options: {
|
||||||
|
maxlength: 200,
|
||||||
|
placeholder: '로그인 후 이동될 URL을 입력해주세요.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ name: 'siteRegdate', header: '등록일시', width: 140 }
|
||||||
|
];
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
siteStore.searchSiteList();
|
||||||
|
});
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
if (gridRef.value.validate().length) {
|
||||||
|
alert('len:::' + gridRef.value.validate().length);
|
||||||
|
message.warn('입력된 사이트 정보를 확인해주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
type: 'info',
|
||||||
|
title: '사이트 정보 저장',
|
||||||
|
content: '선택한 사이트 정보를 저장하시겠습니까?',
|
||||||
|
okText: '예',
|
||||||
|
cancelText: '아니오',
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
const data = gridRef.value.getData();
|
||||||
|
siteStore.updateSiteList(data);
|
||||||
|
} catch (e) {
|
||||||
|
message.error('사이트 정보를 저장하는 도중 에러가 발생되었습니다.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteSiteList = () => {
|
||||||
|
const checkedRows = gridRef.value.getCheckedRows() as Array<SiteType>;
|
||||||
|
|
||||||
|
if (!checkedRows.length) {
|
||||||
|
message.warn('삭제할 사이트가 없습니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
checkedRows.forEach((value) => {
|
||||||
|
params.append('siteId', value.siteId);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const addRow = () => {
|
||||||
|
gridRef.value.appendRow({});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<client-only>
|
||||||
|
<a-space direction="vertical" style="width: 100%">
|
||||||
|
<a-card>
|
||||||
|
<a-space direction="vertical" style="width: 100%">
|
||||||
|
<a-flex justify="space-between">
|
||||||
|
<common-permit-button api="/api/admin/sys/site/deleteSiteList">
|
||||||
|
<a-button type="primary" danger @click="deleteSiteList"
|
||||||
|
>삭제</a-button
|
||||||
|
>
|
||||||
|
</common-permit-button>
|
||||||
|
<common-permit-button api="/api/admin/sys/site/updateSiteList">
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="addRow">추가</a-button>
|
||||||
|
<a-button type="primary" @click="save">저장</a-button>
|
||||||
|
</a-space>
|
||||||
|
</common-permit-button>
|
||||||
|
</a-flex>
|
||||||
|
<data-grid
|
||||||
|
:key="`site-grid-${Math.random()}`"
|
||||||
|
:row-headers="gridRowHeaders"
|
||||||
|
:data="siteList"
|
||||||
|
:columns="columns"
|
||||||
|
ref="gridRef"
|
||||||
|
/>
|
||||||
|
</a-space>
|
||||||
|
</a-card>
|
||||||
|
</a-space>
|
||||||
|
</client-only>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -19,9 +19,14 @@ export const useAuthStore = defineStore('authStore', () => {
|
||||||
return await useAxios().post(`/api/admin/login`, loginRequest.value);
|
return await useAxios().post(`/api/admin/login`, loginRequest.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const permitApiList = computed(() => {
|
||||||
|
return loginResponse.value.permitApiList;
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
loginRequest,
|
loginRequest,
|
||||||
loginResponse,
|
loginResponse,
|
||||||
LoginAPI
|
LoginAPI,
|
||||||
|
permitApiList
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
import type { SiteType } from '~/types/sys/site';
|
||||||
|
|
||||||
|
export const useSiteStore = defineStore('useSiteStore', () => {
|
||||||
|
const siteList = ref<SiteType[]>([]);
|
||||||
|
|
||||||
|
const resetSiteList = () => {
|
||||||
|
siteList.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchSiteList = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await useAxios().get('/api/admin/sys/site/siteList');
|
||||||
|
siteList.value = data;
|
||||||
|
} catch (e) {
|
||||||
|
message.error('사이트 리스트를 불러오는데 실패하였습니다.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSiteList = async (data: SiteType[]) => {
|
||||||
|
try {
|
||||||
|
await useAxios().post('/api/admin/sys/site/updateSiteList', data);
|
||||||
|
message.success('사이트 정보가 저장이 되었습니다.');
|
||||||
|
} catch (e) {
|
||||||
|
message.error('사이트 저장에 실패하였습니다.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
siteList,
|
||||||
|
resetSiteList,
|
||||||
|
searchSiteList,
|
||||||
|
updateSiteList
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
import type {
|
||||||
|
GridEventListener,
|
||||||
|
OptColumn,
|
||||||
|
OptHeader,
|
||||||
|
OptRow,
|
||||||
|
OptRowHeader,
|
||||||
|
OptSummaryData,
|
||||||
|
OptTree
|
||||||
|
} from 'tui-grid/types/options';
|
||||||
|
import type { EditingEvent, TabMode } from 'tui-grid/types/store/focus';
|
||||||
|
import type { SelectionUnit } from 'tui-grid/types/store/selection';
|
||||||
|
import type {
|
||||||
|
ClipboardCopyOptions,
|
||||||
|
ColumnOptions
|
||||||
|
} from 'tui-grid/types/store/column';
|
||||||
|
import type { PageOptions } from 'tui-grid/types/store/data';
|
||||||
|
import type { CreateMenuGroups } from 'tui-grid/types/store/contextMenu';
|
||||||
|
|
||||||
|
export type DataGridType = {
|
||||||
|
data: OptRow[];
|
||||||
|
columns: OptColumn[];
|
||||||
|
bodyHeight?: number | 'fitToParent' | 'auto';
|
||||||
|
columnOptions?: ColumnOptions;
|
||||||
|
keyColumnName?: String;
|
||||||
|
width?: number | 'auto';
|
||||||
|
heightResizable?: Boolean;
|
||||||
|
minBodyHeight?: Number;
|
||||||
|
rowHeight?: number | 'auto';
|
||||||
|
minRowHeight?: Number;
|
||||||
|
scrollX?: Boolean;
|
||||||
|
scrollY?: Boolean;
|
||||||
|
editingEvent?: EditingEvent;
|
||||||
|
tabMode?: TabMode;
|
||||||
|
rowHeaders?: OptRowHeader[];
|
||||||
|
summary?: OptSummaryData;
|
||||||
|
useClientSort?: Boolean;
|
||||||
|
selectionUnit?: SelectionUnit;
|
||||||
|
showDummyRows?: Boolean;
|
||||||
|
copyOptions?: ClipboardCopyOptions;
|
||||||
|
pageOptions?: PageOptions;
|
||||||
|
treeColumnOptions?: OptTree;
|
||||||
|
header?: OptHeader;
|
||||||
|
usageStatistics?: Boolean;
|
||||||
|
disabled?: Boolean;
|
||||||
|
onGridMounted?: GridEventListener;
|
||||||
|
onGridUpdated?: GridEventListener;
|
||||||
|
onGridBeforeDestroy?: GridEventListener;
|
||||||
|
draggable?: Boolean;
|
||||||
|
contextMenu?: CreateMenuGroups;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
export type PaginationType = {
|
||||||
|
totalItems: number;
|
||||||
|
itemsPerPage: number;
|
||||||
|
visiblePages: number;
|
||||||
|
centerAlign?: boolean;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
export type SiteType = {
|
||||||
|
siteId: string;
|
||||||
|
siteName: string;
|
||||||
|
siteDescription: string;
|
||||||
|
siteDomain: string;
|
||||||
|
siteType: '' | 'ADMIN' | 'USER';
|
||||||
|
sitePrefix: string;
|
||||||
|
siteLocale: string;
|
||||||
|
siteLogo: string;
|
||||||
|
bscUrl: string;
|
||||||
|
lgnUrl: string;
|
||||||
|
siteRegdate: string;
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue