IBSheet
Events: { 내에 on~이벤트 작성
onExportFinish: function (evtParam) {
console.log(evtParam.eventName + "엑셀 내보내기 실행");
},
onMunted { 외부에 함수 작성
// excel 내보내기
function excelExport() {
if (sheet1) {
sheet1.exportData({
fileName: "AssetsList.xlsx",
fileType: "xlsx",
sheetDesign: 1,
exceptCol: ["acqDelete"] // 삭제 버튼 컬럼은 제외
});
console.log("엑셀 내보내기 완료");
} else {
openAlertDialog("엑셀 내보내기를 할 수 없습니다.");
}
}
제외 전체 Vue 코드
<template>
<v-app class="v-application">
<v-app-bar :elevation="2" style="background-color: whitesmoke" height="60">
<v-app-bar-nav-icon class="ml-2" @click="sideBarClicked = !sideBarClicked"/>
<div class="header-logo" >
<v-img class="header-img" src="/kloz.png" alt="KLOZ 로고" />
</div>
<!-- 우측 상단 -->
<div class="mr-8"><span v-if="userId">{{userId}}</span></div>
<div><v-icon @click="logout" class="v-icon-img">mdi-logout</v-icon></div>
</v-app-bar>
<div class="content-container">
<!-- 왼쪽 사이드바 -->
<div class="sidebar" @mouseenter="sideBarHovered = true" @mouseleave="sideBarHovered = false"
:class="{ open: isSideBarOpen }">
<div v-if="isSideBarOpen" class="date-time sidebar-img">
<div class="menu-userInfo">{{displayName}}</div>
<div class="menu-userInfo">{{position}}</div>
<div style="margin-top: 20px;">{{ today }}</div>
<div>{{ nowTime }}</div>
</div>
<div class="menu-item" @click="">
<v-icon class="v-icon-img">mdi-home</v-icon>
<span v-if="isSideBarOpen">홈</span>
</div>
<div class="menu-item" @click="goToAsset">
<v-icon class="v-icon-img">mdi-office-building</v-icon>
<span v-if="isSideBarOpen">고정자산</span>
</div>
<div class="menu-item">
<v-icon class="v-icon-img">mdi-file-document</v-icon>
<span v-if="isSideBarOpen">메뉴2</span>
</div>
</div>
<div class="main" :class="{ shift: sideBarClicked }">
<div variant="h6" class="ml-2 font-weight-bold">자산관리</div>
<div class="dropdown-container">
<div class="left-controls">
<v-select
v-model="selectedSubject"
:items="selectTitleOptions"
label="계정과목"
variant="outlined"
density="compact"
@update:modelValue="searchAsset"
></v-select>
<v-text-field
v-model="kwd"
label="자산명"
@keydown.enter="searchAsset"
clearable
variant="outlined" density="compact" icon>
</v-text-field>
<VueDatePicker
v-model="dateRange"
range
:partial-range="false"
:enable-time-picker="false"
auto-apply
clearable
placeholder="취득일자"
format="yyyy-MM-dd"
@update:model-value="searchAsset"
style="max-width: 270px;"
/>
<v-btn @click="searchAsset" color="info" rounded="lg" class="btn">검색
<v-icon icon="mdi-magnify" style="margin-left: 10px;"/>
</v-btn>
</div>
<v-btn @click="doOpenPDF"> PDF </v-btn>
<v-btn @click="doOpenExcel"> EXCEL </v-btn>
</div>
<div class="insertBtn">
<v-btn @click="openDialog" color="success" rounded="lg" class="btn">등록
<v-icon icon="mdi-pencil" style="margin-left: 10px;"/>
</v-btn>
</div>
<!-- IBSheet -->
<div class="klozSheet" id="klozSheet" ></div>
</div>
</div>
</v-app>
<!-- 등록 모달 -->
<v-dialog v-model="dialog" width="600px">
<v-card class="v-card" style="background-color: whitesmoke">
<v-card-title class="v-card-title">{{ editAssetCode ? '수정' : '등록' }}</v-card-title>
<v-card-text>
<v-row>
<v-col cols="6">
<v-select label="계정과목*" v-model="titleModal" :items="selectTitleOptions" variant="outlined"
density="compact" hide-details></v-select>
</v-col>
<v-col cols="6">
<v-text-field label="자산명*" v-model="nameModal" variant="outlined"
density="compact" hide-details></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="4">
<v-text-field label="수량*" v-model="quantityModal" variant="outlined"
density="compact" hide-details></v-text-field>
</v-col>
<v-col cols="4">
<v-text-field label="취득일*" v-model="regDateModal" type="date"
variant="outlined" density="compact" hide-details></v-text-field>
</v-col>
<v-col cols="4">
<v-text-field label="취득금액*" v-model="priceModal" variant="outlined" density="compact"
hide-details></v-text-field>
</v-col>
</v-row>
<hr class="middle-line">
<v-row>
<v-col cols="4">
<v-text-field label="프로젝트" v-model="projectModal" variant="outlined" density="compact"
hide-details></v-text-field>
</v-col>
<v-col cols="4">
<v-text-field label="사용부서" v-model="deptModal" variant="outlined" density="compact"
hide-details></v-text-field>
</v-col>
<v-col cols="4">
<v-text-field label="사용자" v-model="assetUserIdModal" variant="outlined" density="compact"
hide-details></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="4">
<v-text-field label="매입처" v-model="vendorModal" variant="outlined" density="compact"
hide-details></v-text-field>
</v-col>
<v-col cols="4">
<v-text-field label="규격" v-model="specModal" variant="outlined" density="compact"
hide-details></v-text-field>
</v-col>
<v-col cols="4">
<v-text-field label="모델" v-model="modelModal" variant="outlined" density="compact"
hide-details></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-text-field label="비고" v-model="remarkModal" variant="outlined" density="compact"
hide-details></v-text-field>
</v-col>
</v-row>
<h4 style="font-size:18px; font-weight:500; margin: 20px 0 0 10px;">* 은 필수 값 입니다.</h4>
</v-card-text>
<v-card-actions class="justify-end">
<v-btn @click="cancelDialog">취소</v-btn>
<v-btn color="primary" @click="dialogSave">{{ editAssetCode ? '수정 완료' : '등록 완료' }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="alertDialog" max-width="400">
<v-card>
<v-card-title class="headline">알림</v-card-title>
<v-card-text>{{ alertMessage }}</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary" @click="alertDialog = false">확인</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup>
import {ref, onMounted, onUnmounted, computed} from 'vue'
import {doRequest} from "@/assets/common/js/common.js";
import loader from '@ibsheet/loader'
import axios from 'axios'
import dayjs from 'dayjs'
import VueDatePicker from '@vuepic/vue-datepicker'
import '@vuepic/vue-datepicker/dist/main.css';
import {isProduct} from "@/assets/common/js/common.js";
const today = ref('')
const nowTime = ref('')
let timer = null
const alertDialog = ref(false);
const alertMessage = ref('');
let sheet1 = null;
const dateRange = ref([]); // 날짜 검색
const kwd = ref('') // 자산명 검색
const userId = ref('') // 사용자 아이디
const displayName = ref('') // 사용자 이름
const position = ref('') // 직급
const editAssetCode = ref(null);
const sideBarHovered = ref(false)
const sideBarClicked = ref(false)
const isSideBarOpen = computed(() => sideBarClicked.value || sideBarHovered.value)
// 사이드바에서 고정자산 클릭 시
function goToAsset() {
window.location.reload();
}
// dialog
const dialog = ref(false)
const titleModal = ref('')
const nameModal = ref('')
const quantityModal = ref('')
const regDateModal = ref('')
const priceModal = ref('')
const projectModal = ref('')
const deptModal = ref('')
const assetUserIdModal = ref('')
const vendorModal = ref('')
const specModal = ref('')
const modelModal = ref('')
const remarkModal = ref('')
const resetDialog = () => {
titleModal.value = '';
nameModal.value = '';
quantityModal.value = '';
regDateModal.value = '';
priceModal.value = '';
projectModal.value = '';
deptModal.value = '';
assetUserIdModal.value = '';
vendorModal.value = '';
specModal.value = '';
modelModal.value = '';
remarkModal.value = '';
editAssetCode.value = null;
};
const openAlertDialog = (message) => {
alertMessage.value = message;
alertDialog.value = true;
};
const openDialog = () => {
dialog.value = true;
resetDialog();
setTimeout(() => {
document.activeElement?.blur();
}, 100);
}
const cancelDialog = () => dialog.value = false
const openEditDialog = (rowData) => {
titleModal.value = rowData.accountTitle;
nameModal.value = rowData.assetName;
quantityModal.value = rowData.acqQty;
regDateModal.value = rowData.acqDate ? dayjs(rowData.acqDate).format('YYYY-MM-DD') : new Date();
priceModal.value = rowData.acqPrice;
projectModal.value = rowData.project || '';
deptModal.value = rowData.useDept || '';
assetUserIdModal.value = rowData.assetUserId || '';
vendorModal.value = rowData.vendor || '';
specModal.value = rowData.spec || '';
modelModal.value = rowData.model || '';
remarkModal.value = rowData.remark || '';
editAssetCode.value = rowData.assetCode;
dialog.value = true;
};
const selectTitleOptions = ref([])
const selectedSubject = ref('')
// 계정과목 불러오기
const loadAccountTitles = async () => {
try {
const res = await axios.get('/main/loadAccountTitles.html')
const list = res.data
selectTitleOptions.value = list.map(item => item.accountTitle)
} catch (err) {
console.error('계정과목 불러오기 실패', err)
}
}
const dialogSave = async () => {
const modalData = {
accountTitle: titleModal.value,
assetName: nameModal.value,
acqQty: quantityModal.value,
acqDate: regDateModal.value ? dayjs(regDateModal.value).format('YYYY-MM-DD') : null,
acqPrice: priceModal.value,
project: projectModal.value || null,
assetUserId: assetUserIdModal.value || null,
useDept: deptModal.value || null,
vendor: vendorModal.value || null,
spec: specModal.value || null,
model: modelModal.value || null,
remark: remarkModal.value || null
}
let url = '';
let method = '';
if (!titleModal.value || !nameModal.value || !quantityModal.value || !regDateModal.value || !priceModal.value) {
openAlertDialog('필수 항목을 모두 입력해 주세요.');
return;
}
if (editAssetCode.value) {
url = '/main/updateAsset.do';
method = 'put';
modalData.assetCode = editAssetCode.value;
} else {
url = '/main/insertAsset.do';
method = "post";
}
try {
const res = await axios({method: method, url: url, data: modalData});
if (res.data && res.data.result === 1) {
openAlertDialog(editAssetCode.value ? "수정되었습니다." : "등록되었습니다.");
dialog.value = false;
resetDialog();
} else {
// 서버에서 보낸 에러 메시지가 있다면 사용, 없다면 일반 메시지
openAlertDialog(res.data && res.data.message ? res.data.message : "등록/수정 실패");
}
} catch (error) {
console.error(editAssetCode.value ? "수정 실패하였습니다." : "등록 실패하였습니다.", error);
const errorMessage = error.response && error.response.data && error.response.data.message
? error.response.data.message : '네트워크 오류 또는 서버 응답 문제';
openAlertDialog(`작업 중 오류 발생: ${errorMessage}`);
}
};
// 현재 시간
function updateTime() {
const now = new Date()
nowTime.value = now.toLocaleTimeString('ko-KR', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
}
onMounted(async () => {
// 계정과목 데이터 로드
await loadAccountTitles();
// IBSheet 로더 설정
loader.config({
registry: [{
name: 'ibsheet',
baseUrl: isProduct? '../sheet' : '/sheet',
theme: 'mint',
locales: ['ko', 'en'],
plugins: ['common', 'dialog', 'excel']
}]
});
loader.load();
// IBSheet 초기화 옵션 정의
const initOptions = {
Cfg: {
DebugOption:"error",
SearchMode: 3,
CustomScroll: 0,
HeaderCheck: 1,
UseButton: 1,
FilterDefaultsIconLeft: true,
"Alternate": 2, // IBSheet 행 배경색
// InfoRowConfig: {Visible: false},
FitWidth: false,
IgnoreFocused: 1
},
Cols: [
{ Header: "계정과목", Name: "accountTitle", Type: "Text", Align: "Center", CanEdit: 3, RelWidth: 3 },
{ Header: "자산코드", Name: "assetCode", Type: "Text", Align: "Center", CanEdit: 3, RelWidth: 5 },
{ Header: "자산명", Name: "assetName", Type: "Text", Align: "Center", CanEdit: 1, RelWidth: 5 },
{ Header: "취득수량 (개)", Name: "acqQty", Type: "Float", Align: "right", Format: "#,###.00", CanEdit: 1, RelWidth: 3 },
{ Header: "취득일자", Name: "acqDate", Type: "Date", Align: "Center", Format: "yyyy-MM-dd", CanEdit: 1, RelWidth: 5 },
{ Header: "취득금액 (원)", Name: "acqPrice", Type: "Float", Format: "#,###.00", CanEdit: 1, RelWidth: 3 },
{ Header: "삭제", Name: "acqDelete", Type: "Button", ButtonText: "❌", RelWidth:1}
],
Events: {
onDataLoad: function (evtParam) {
console.log(evtParam.eventName + "데이터 로드 실행");
},
onRenderFirstFinish: function (evtParam) {
console.log(evtParam.eventName + "시트 처음 렌더링 실행");
const sheet = evtParam.sheet;
sheet.doSearchPaging({
url: "/main/searchPaging.do",
method: "POST",
reqHeader: {"Content-Type": "application/json"},
param: {
kwd: kwd.value,
accountTitle: selectedSubject.value,
startDate: dateRange.value?.[0],
endDate: dateRange.value?.[1]
},
});
},
onSearchFinish: function (evtParam) {
console.log(evtParam.eventName + "검색 및 페이징 실행");
},
onAfterChange: function (evtParam) {
console.log(evtParam.eventName + ' 단일 셀 수정 실행');
const rowData = evtParam.row;
const colName = evtParam.col;
const newValue = evtParam.val;
const oldValue = rowData[colName + 'BeforeVal'] ?? rowData[colName + 'Orig'] ?? null;
if (newValue === oldValue) {
console.log("단일 셀 수정 : 값이 변경되지 않아 서버 요청을 보내지 않음");
return;
}
const changedData = {
assetCode: rowData.assetCode,
col: colName,
value: newValue
};
console.log("단일 셀 수정 시 서버로 전송하는 데이터:", JSON.stringify(changedData));
try {
const res = axios.put(`/main/updateColAsset.do`, changedData);
if (res.data && res.data.result === 1) {
} else {
openAlertDialog(res.data.message || "단일 셀 수정 실패");
}
} catch (err) {
console.error("단일 셀 수정 중 오류:", err);
}
},
onAfterClick: function (evtParam) {
if (evtParam.col === 'assetCode') {
const rowData = evtParam.row;
openEditDialog(rowData);
}else if(evtParam.col === 'acqDelete') {
}
},
onClick: function (evtParam) {
if (evtParam.col === 'acqDelete') {
const rowData = evtParam.row;
const formattedPrice = new Intl.NumberFormat('ko-KR', {
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(rowData.acqPrice);
if (confirm(`정말 삭제하시겠습니까?\n\n계정과목 : ${rowData.accountTitle}\n자 산 명 : ${rowData.assetName}\n취득금액 : ${formattedPrice}원`)) {
try {
const res = axios.delete(`/main/deleteAsset.do?assetCode=${rowData.assetCode}`);
if (res.data && res.data.result === 1) {
openAlertDialog('삭제 되었습니다.');
evtParam.sheet.deleteRow(rowData);
} else {
openAlertDialog(res.data.message || '삭제에 실패하였습니다.');
}
} catch (error) {
console.error('삭제 중 오류 발생', error);
const errorMessage = error.response && error.response.data && error.response.data.message
? error.response.data.message : '네트워크 오류 또는 서버 응답 문제';
openAlertDialog(`삭제 중 오류가 발생했습니다: ${errorMessage}`);
}
}
}
},
}
};
try {
// IBSheet 인스턴스 생성
loader.createSheet({
id: 'klozSheet',
el: 'klozSheet',
options: initOptions
}).then(sheet => {
// 주의: 해당 구간에서 데이터 조회를 하면 안됩니다. 데이터 조회는 onRenderFirstFinish 이벤트에서 실행해야합니다.
// 생성된 시트객체를 페이지 공통 객체에 넣어두고 사용
sheet1 = sheet;
});
console.log("시트 생성 완료")
} catch (err) {
console.error("IBSheet 생성 실패", err);
openAlertDialog('데이터 로드에 실패했습니다. 시스템 관리자에게 문의하세요.');
}
// 로그인
try {
const response = await axios.get('/login/getUserId.do', {
withCredentials: true
});
if(response.data.success) {
userId.value = response.data.userId;
displayName.value = response.data.displayName;
position.value = response.data.position;
console.log('로그인 유저 정보 조회 완료');
} else {
console.log('로그인 유저 정보 조회 실패');
window.location.href = '/login/login.html';
}
} catch (error) {
console.error('로그인 유저 정보 조회 중 오류 발생:', error);
window.location.href = '/login/login.html';
}
// 현재 시간
const now = new Date();
today.value = now.toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
weekday: 'short'
})
updateTime();
timer = setInterval(updateTime, 1000);
});
onUnmounted(() => {
loader.removeSheet('klozSheet'); // 새로고침 시 동일ID Sheet 호출 방어
clearInterval(timer);
function updateTime() {
const now = new Date()
nowTime.value = now.toLocaleTimeString('ko-KR', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
}
return {today, nowTime}
})
function doOpenPDF() {
location.href = '../report/reportPDF.do'
}
function doOpenExcel() {
location.href= '../report/reportExcel.do'
}
// 검색
const searchAsset = async () => {
console.log("searchAsset 실행 시작");
const params = {
kwd: kwd.value,
accountTitle: selectedSubject.value,
startDate: dateRange.value?.[0],
endDate: dateRange.value?.[1],
};
sheet1.doSearchPaging({
url: "/main/searchPaging.do",
method: "POST",
reqHeader: {"Content-Type": "application/json"},
param: params,
});
console.log("searchAsset 실행 완료");
// }
};
// 로그아웃
const logout = async () => {
try {
const response = await axios.post('/login/logout.html', {}, {
withCredentials: true
});
if (response.status === 200) {
alert('로그아웃 되었습니다.');
window.location.href = '/login/login.html';
} else {
alert('로그아웃 중 문제가 발생했습니다.');
console.error('Logout failed with status:', response.status);
}
} catch (error) {
// 네트워크 오류, 서버 오류 등 예외 처리
console.error('로그아웃 중 오류 발생:', error);
alert('로그아웃 중 서버 오류가 발생했습니다.');
window.location.href = '/login/login.html';
}
};
</script>
<style scoped>
</style>
'웹 개발 > IBSheet8' 카테고리의 다른 글
| [IBSheet] 시트 내 이미지 삽입 / 반환타입과 파라미터요청, JSON요청(1) (3) | 2025.08.01 |
|---|---|
| [IBSheet] onClick(), onDblClick(), onAfterClick() 이벤트로 수정, 삭제 시 열이름 클릭 제외 (0) | 2025.06.30 |
| [IBSheet] 단일 행 수정 (JS 동적 속성 접근, 요청/응답) (1) | 2025.06.10 |
| [IBSheet] loader (0) | 2025.06.04 |
| [IBSheet] IBSheet8 활용 데이터 출력 (2) | 2025.06.04 |