This commit is contained in:
2025-05-24 01:49:48 +09:00
commit 62abbcf4eb
2376 changed files with 325522 additions and 0 deletions

View File

@@ -0,0 +1,435 @@
<script setup lang="ts">
import axios from 'axios'
const registerFormOpen = ref(false)
const params = reactive({
cateCd: '',
contNo: '',
title: '',
compNm: '',
signDt: '',
contAmt: '',
contStatCd: '',
contStat: '',
regsabun: '',
regNm: '',
regDt: '',
reason: '',
page: 1,
row: 10,
flexColumn: [],
modalColumn: [],
})
params.modalColumn = [
{ key: 'cateNm', label: '분야' },
{ key: 'title', label: '제목' },
{ key: 'regNm', label: '담당자' },
{ key: 'stNm', label: '등록상태' },
{ key: 'title', label: '비고' },
{ key: 'regNm', label: '선택' },
]
const selectedCode = ref()
const data = reactive({
contractData: [],
priceSearchData: [],
})
const isLoading = ref(false)
watch(registerFormOpen, async (isOpen) => {
if (isOpen) {
isLoading.value = true
// error.value = null
try {
const priceSearchDataRespone = await axios.get('/api/cont/prcs')
console.log(priceSearchDataRespone.data)
data.priceSearchData = Array.isArray(priceSearchDataRespone.data) ? priceSearchDataRespone.data : []
}
catch (error) {
console.log(error)
data.priceData = []
}
}
})
function formatDate(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
return date.toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).replace(/\./g, '-').replace(/\s/g, '').replace(/-$/, '')
}
function getDateDiff(start, end) {
if (!start || !end) return null
const startDate = new Date(start)
const endDate = new Date(end)
const diff = Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24))
return diff >= 0 ? diff + 1 : null
}
const contractPeriod = computed(() => {
const start = params.regSdt
const end = params.regEdt
const startStr = formatDate(start)
const endStr = formatDate(end)
const diff = getDateDiff(start, end)
if (startStr && endStr && diff) {
return `${startStr} ~ ${endStr} (${diff}일)`
}
else if (startStr && endStr) {
return `${startStr} ~ ${endStr}`
}
else if (startStr) {
return `${startStr} ~`
}
else if (endStr) {
return `~ ${endStr}`
}
else {
return ''
}
})
</script>
<template>
<div class="page-content is-navbar-lg">
<div class="datatable-wrapper">
<div class="table-container">
<table class="table datatable-table is-fullwidth">
<colgroup>
<col style="width: 10%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 20%;">
<col style="width: 10%;">
</colgroup>
<tbody>
<tr>
<td>분야</td>
<td>
<span class="colum">
<VField class="pr-2">
<VCodeSelect
v-model="selectedCode"
cd_grp="5/"
/></VField>
</span>
</td>
<td>
<VButton
color="primary"
@click="registerFormOpen = true"
>
가격조사 가져오기
</VButton>
<VModal
is="form"
:open="registerFormOpen"
title="계약관리 등록"
size="contract-big"
actions="right"
@submit.prevent="registerFormOpen = false"
@close="registerFormOpen = false"
>
<template #content>
<div class="modal-form">
<ComVFlexTable
:data="data.priceSearchData"
:columns="params.modalColumn"
:compact="true"
:separators="true"
/>
</div>
</template>
<template #action>
<VButton
type="submit"
color="primary"
raised
>
Save Changes
</VButton>
</template>
</VModal>
</td>
<td>
<VField class="is-flex">
<VControl raw subcontrol>
<VCheckbox
label="가격조사여부"
color="info"
/>
</VControl>
</VField>
</td>
<td colspan="3">
<div class="column is-fullhd">
<VField>
<VControl>
<input
v-model="params.reason"
class="input custom-text-filter"
placeholder="가격조사 안했을 시 예외 사유 입력(필수)"
>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>계약명</td>
<td colspan="6">
<VField>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="계약명"
>
</VControl>
</VField>
</td>
</tr>
<tr>
<td>계약상대자</td>
<td>
<VField>
<VControl>
<input
v-model="params.regNm"
class="input custom-text-filter"
placeholder="사업자번호"
>
</VControl>
</VField>
</td>
<td>
<VField>
<VControl>
<input
v-model="params.compNm"
class="input custom-text-filter"
placeholder="업체명"
>
</VControl>
</VField>
</td>
<td>
<VButton color="warning">
부정당 확인
</VButton>
</td>
<td>
<VButton color="success">
정상
</VButton>
</td>
<td>
<VButton color="warning">
분할계약 확인
</VButton>
</td>
<td>
<VButton color="success">
정상
</VButton>
</td>
</tr>
<tr>
<td>계약체결일</td>
<td>
<VField>
<VControl>
<input
v-model="params.signDt"
class="input custom-text-filter"
placeholder="계약체결일"
>
</VControl>
</VField>
</td>
<td>
<VButton
color="primary"
>
</VButton>
<VModal
actions="center"
title="계약금액"
>
<template #content>
<VPlaceholderSection
title="Go Premium"
subtitle="Unlock more features and business tools by going premium"
/>
</template>
<template #action>
<VButton color="primary" raised>
등록
</VButton>
</template>
</VModal>
</td>
<td colspan="2">
<VField>
<VControl>
<input
v-model="params.contAmt"
class="input custom-text-filter"
placeholder="금액"
>
</VControl>
</VField>
</td>
<td colspan="1">
<span class="colum">
<VField>
<VSelect>
<VOption value="">
수의계약 사유
</VOption>
</VSelect>
</VField>
</span>
</td>
<td>
<VButton>근거</VButton>
</td>
</tr>
<tr>
<td>계약기간</td>
<td colspan="1">
<div>
<div>
<VDatePicker
v-model="params.regSdt"
color="green"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="시작일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</td>
<td colspan="1">
<div class="pr-2">
<div>
<VDatePicker
v-model="params.regEdt"
color="green"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="종료일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</td>
<td colspan="5">
계약기간 : {{ contractPeriod }}
</td>
</tr>
<tr>
<td>첨부파일</td>
<td colspan="1">
<VButton color="info">
등록
</VButton>
</td>
<td colspan="5" />
</tr>
</tbody>
</table>
<div class="bottom-button">
<VButton
to="/app/DocumentManagement"
>
(임시)
</VButton>
<VButton> </VButton>
<VButton> </VButton>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.table tbody td {
color: var(--smoke-white);
}
.datatable-table {
padding: 12px 12px;
td:nth-child(1) {
background-color: var(--primary);
text-align: center;
}
tr:nth-child(3) {
text-align: center;
}
tr td button{
width: 100%;
}
tr:nth-child(5) > td:nth-child(4) {
color: black;
}
}
.bottom-button {
text-align: center;
button {
background-color: cornflowerblue;
margin: 10px;
font-weight: bold;
border-color: var(--primary);
color: white;
}
}
button:nth-child(2) {
background-color: #AB9A6c;
}
button:nth-child(3) {
background-color: silver;
}
.field {
margin-bottom: 0px;
}
</style>

View File

@@ -0,0 +1,389 @@
<script setup lang="ts">
const affiliationCode = ref()
const taxCode = ref('')
const currencyCode = ref('KRW')
const taxInvoiceCode = ref()
const accountSubjectCode = ref()
const params = reactive({
cateCd: '',
contNo: '',
title: '',
compNm: '',
signDt: '',
contAmt: 0,
contStatCd: '',
contStat: '',
regsabun: '',
regNm: '',
regDt: '',
reason: '',
page: 1,
row: 10,
flexColumn: [],
modalColumn: [],
})
const selected = ref('cost')
const formattedNumber = ref(0)
function onInput(event) {
let onlyNumber = event.target.value.replace(/[^0-9]/g, '')
formattedNumber.value = onlyNumber ? Number(onlyNumber).toLocaleString() : ''
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div class="datatable-wrapper">
<div class="table-container">
<table class="table datatable-table is-fullwidth">
<colgroup>
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
</colgroup>
<tbody>
<tr>
<td>소속</td>
<td colspan="2">
<VField>
<VCodeSelect
v-model="affiliationCode"
cd_grp=11 >
</VCodeSelect>
</VField>
</td>
<td colspan="7" />
</tr>
<tr>
<td>계약명</td>
<td colspan="9">
<VField>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="계약명"
>
</VControl>
</VField>
</td>
</tr>
<tr>
<td>계약상대자</td>
<td colspan="2">
<VField>
<VControl>
<input
v-model="params.regNm"
class="input custom-text-filter"
placeholder="사업자번호"
>
</VControl>
</VField>
</td>
<td colspan="2">
<VField>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="업체명"
>
</VControl>
</VField>
</td>
<td>
<VButton color="danger">
구매처 확인
</VButton>
</td>
<td>
<VButton color="success">
정상
</VButton>
</td>
<td colspan="2" />
</tr>
<tr>
<td>공급가액</td>
<td colspan="2">
<VField>
<VControl>
<input
:value="formattedNumber"
placeholder="금액"
@input="onInput"
class="input custom-text-filter"
>
</VControl>
</VField>
</td>
<td style="color: black">(부가세 별도)</td>
<td style="background-color: var(--primary); text-align: center">
<span>세금코드</span>
</td>
<td colspan="2">
<VField>
<VCodeSelect v-model="taxCode" cd_grp="12">
<template #code="{ item }">
{{ item.cd }}
</template>
</VCodeSelect>
</VField>
</td>
<td style="background-color: var(--primary); text-align: center">통화</td>
<td colspan="2">
<VField>
<VCodeSelect v-model="currencyCode" cd_grp="13">
<template #code="{ item }">
{{ item.cd }}
</template>
</VCodeSelect>
</VField>
</td>
</tr>
<tr>
<td>증빙일</td>
<td colspan="2">
<div>
<VDatePicker
v-model="params.regSdt"
color="green"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="증빙일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</td>
<td />
<td style="background-color: var(--primary); text-align: center">
<span>전기일</span>
</td>
<td colspan="2">
<div>
<VDatePicker
v-model="params.regSdt2"
color="green"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="전기일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</td>
</tr>
<tr>
<td>계좌관리</td>
<td>
<VField>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="계약번호"
>
</VControl>
</VField>
</td>
<td colspan="2">
<VField>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="계좌번호"
>
</VControl>
</VField>
</td>
<td>
<VButton color="info">
계좌조회
</VButton>
</td>
<td >
<VButton color="warning">
계좌확인중
</VButton>
</td>
<td>
<VButton color="success">
정상
</VButton>
</td>
<td style="background-color: var(--primary); text-align: center">
<span>세금계산서</span>
</td>
<td colspan="2">
<VField>
<VCodeSelect
v-model="taxInvoiceCode"
cd_grp=12 >
</VCodeSelect>
</VField>
</td>
</tr>
<tr>
<td>예산관리</td>
<td>
<VButton
color="primary"
@click="centeredActionsOpen = true"
>
G/L계정
</VButton>
</td>
<td colspan="2">
<VField>
<VCodeSelect
v-model="accountSubjectCode"
cd_grp=12 >
</VCodeSelect>
</VField>
</td>
<td>
<VButton
color="info"
@click="selected = 'cost'"
:class="{ 'disabled-button': selected === 'wbs' }"
>
코스트센터
</VButton>
</td>
<td>
<VButton
color="info"
@click="selected = 'wbs'"
:class="{ 'disabled-button': selected === 'cost' }"
>
WBS
</VButton>
</td>
<td colspan="2">
<VField>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="코드"
>
</VControl>
</VField>
</td>
</tr>
<tr>
<td>첨부파일</td>
<td colspan="1">
<VButton color="info">
등록
</VButton>
</td>
<td colspan="5">
<div>('준공보고서' 또는 '검수보고서' )</div>
</td>
</tr>
</tbody>
</table>
<div class="bottom-button">
<VButton> </VButton>
<VButton> </VButton>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.table tbody td {
color: var(--smoke-white);
}
.datatable-table {
td{
font-family: var(--font),serif;
vertical-align: middle;
padding: 4px 20px;
border-bottom: 1px solid var(--fade-grey);
}
td:nth-child(1) {
background-color: var(--primary);
text-align: center;
}
tr:nth-child(3) {
text-align: center;
}
tr td button{
width: 100%;
}
}
.bottom-button {
text-align: center;
button {
background-color: cornflowerblue;
margin: 10px;
font-weight: bold;
border-color: var(--primary);
color: white;
}
}
button:nth-child(2) {
background-color: #AB9A6c;
}
button:nth-child(3) {
background-color: silver;
}
.field {
margin: 0px 0px;
}
.button.v-button {
padding: 0px 0px;
}
.disabled-button {
//opacity: 0.5;
background-color: #ccc;
cursor: not-allowed;
//transition: all 0.2s;
}
</style>

View File

@@ -0,0 +1,521 @@
<script setup lang="ts">
import { savePrice } from '/src/service/priceApi'
import { Person } from '/@src/utils/types'
const notyf = useNotyf()
const showTable = ref(false)
const detailActionsOpen = ref(false)
const apprLine = defineModel<Person[]>()
const today = new Date()
const loading = ref(false)
//포커스 설정 변수 정리
const cateSelectRef = ref()
const titleRef = ref()
const contentRef = ref()
const prcsBizsRef = ref()
const generalParams = reactive({
title: "",
content: "",
regSdat: "",
regEdat: "",
prvYn: true,
prvRsn : "",
prvPwd : "",
aiYn: true,
})
const params = reactive({
cateSelect: '',
prcsAttsColumn:[ //첨부파일 입력
{ key: 'logiFnm', label: '구분'},
{ key: 'data', label: '데이터'}
],
prcsAtts: [], //첨부파일 데이터
felxColumn: [
{ key: 'gubunCd', label: '구분'},
{ key: 'deptNm', label: '부서' },
{ key: 'sabun', label: '사번' },
{ key: 'name', label: '이름' },
{ key: 'attendCd', label: '비고' },
{ key: 'actions', label: '동작'}
],
priceData:[],
prcsBizsColumn: [ //견적사 입력
{ key: 'num', label: '구분', width: '10%' },
{ key: 'email', label: '이메일', editable: true, width: '50px' },
{ key: 'bizNo', label: '사업자번호', editable: true, width: '50px'},
{ key: 'actions', label: '동작', width: '100px'}
],
prcsBizs: [], //견적사 입력 데이터
dtlSpecsColumn: [
{ key: 'num', label: '번호', editable: false },
{ key: 'itemNm', label: '품명', editable: true },
{ key: 'spec', label: '규격', editable: true },
{ key: 'unit', label: '단위', editable: true },
{ key: 'qty', label: '수량', editable: true },
{ key: '', label: '단가(VAT별도)', editable: false },
{ key: '', label: '금액(VAT별도)', editable: false },
{ key: '', label: '삭제', editable: false },
],
dtlSpecs: [], //상세 규격 입력 데이터
})
const validation = () => {
if(!params.cateSelect){
notyf.error("필수값 입니다.")
cateSelectRef.value.focus() //todo 선택박스 필수값 체크하기
console.log(params.cateSelect)
return false
}
if(!generalParams.title){
notyf.error("필수값 입니다.")
titleRef.value.focus()
return false
}
if(!generalParams.content){
notyf.error("필수값 입니다.")
contentRef.value.focus()
return false
}
if(params.prcsBizs==[]){
notyf.error("견적요청을 입력해주세요")
prcsBizsRef.value.focus()
return false
}
if(apprLine.value.length < 2){
notyf.error("결재선은 두 명이상 입력해주세요.")
return false
}
return true
}
const savePriceOne = async () => {
let res = null
try{
loading.value = true
// if (!validation()) {
// return;
// }
const paramsPrice ={
cateCd : params.cateSelect,
title: generalParams.title,
content: generalParams.content,
regSdat: formatDate(generalParams.regSdat),
regEdat: formatDate(generalParams.regEdat),
prvYn: true,
prvRsn : "",
prvPwd : "",
aiYn: true,
prcsBizs: params.prcsBizs.map(({ num, ...rest }) => rest), //견적사 입력 데이터
dtlSpecs: params.dtlSpecs.map(({ num, ...rest }) => rest), //상세 규격 입력 데이터
prcsAtts: params.prcsAtts, //첨부파일 데이터
apprReqs: apprLine.value.map(({ deptNm, ...rest }) => rest), //결재선 데이터
}
res = await savePrice(paramsPrice)
if(res.request.status == '200'){
notyf.primary('수정 되었습니다.')
router.push({path: '/app/priceManagement'})
}
}catch(e){
notyf.error(e.message)
}finally {
loading.value = false
}
}
const addNewEstimateRow = () => {
const newRow = {}
params.prcsBizsColumn.forEach(col => {
if (col.key && col.key !== 'actions') {
newRow[col.key] = ''
}
})
params.prcsBizs.push(newRow)
}
const onDelete = (index: number) => {
if(params.prcsBizs.length-1 !== params.prcsBizsColumn.length
|| (params.prcsBizsColumn.length == 4 && params.prcsBizs.length-1 == params.prcsBizsColumn.length))
{
params.prcsBizs.splice(index, 1)
}
}
const addNewDetailRow = () => {
const newRow = {}
params.dtlSpecsColumn.forEach(col => {
if (col.key) {
newRow[col.key] = ''
}
})
params.dtlSpecs.push(newRow)
console.log(params.dtlSpecs.length)
}
const onDetailDelete = (index: number) => {
if(params.dtlSpecs.length-1 !== params.dtlSpecsColumn.length
|| (params.dtlSpecsColumn.length == 8 && params.dtlSpecs.length-1 == params.dtlSpecsColumn.length))
{
params.dtlSpecs.splice(index, 1)
}
}
function formatDate(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
return date.toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).replace(/\./g, '-').replace(/\s/g, '').replace(/-$/,'')
}
const onPayDelete = (index: number) => {
if(params.dtlSpecs.length-1 !== params.dtlSpecsColumn.length
|| (params.dtlSpecsColumn.length == 8 && params.dtlSpecs.length-1 == params.dtlSpecsColumn.length))
{
params.dtlSpecs.splice(index, 1)
}
}
const fileInput = ref<HTMLInputElement | null>(null)
const fileName = ref('')
function openFileDialog() {
fileInput.value?.click()
}
function onFileChange(event: Event) {
const target = event.target as HTMLInputElement
if (target.files && target.files.length > 0) {
fileName.value = target.files[0].name
// 여기서 파일 업로드 로직을 추가할 수 있습니다.
// 예: emit('file-selected', target.files[0])
}
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div class="datatable-wrapper">
<div class="table-container">
<table class="table datatable-table is-fullwidth">
<colgroup>
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
</colgroup>
<tbody>
<tr>
<td>분야</td>
<td colspan="2">
<span class="column">
<VField class="pr-2">
<VCodeSelect
cd_grp=5
v-model="params.cateSelect"
ref="cateSelectRef"/>
</VField>
</span>
</td>
<td>제목</td>
<td colspan="6">
<div class="column is-fullhd">
<VField class="pr-2">
<VControl>
<input
v-model="generalParams.title"
class="input custom-text-filter"
placeholder="제목"
ref="titleRef"
>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>내용</td>
<td colspan="9">
<div class="column is-fullhd">
<VField class="pr-2">
<VControl>
<textarea
v-model="generalParams.content"
class="input custom-text-filter"
placeholder="내용"
ref="contentRef"
/>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>견적요청</td>
<td colspan="9">
<VButton
color="primary"
icon="fas fa-plus"
elevated
@click="showTable = !showTable"
style="width: 19%;"
>
견적사 입력
</VButton>
<div v-if="showTable" class="mt-2">
<ComVFlexTable
:key="params.prcsBizs.length"
:data="params.prcsBizs"
:columns="params.prcsBizsColumn"
:compact="true"
:separators="true"
:clickable="true"
>
<template #body-cell="{ row, column, index, value }">
<div>
<!-- 다른 editable 컬럼은 input -->
<input
v-if="column.editable"
v-model="row[column.key]"
class="editable-input"
ref="prcsBizsRef"
/>
<span v-else-if="column.key=='num'">{{index + 1}}</span>
<!-- readonly 출력 -->
<span v-else class="lnil lnil-close"
@click="onDelete(index)">{{ value }}</span>
</div>
</template>
</ComVFlexTable>
<div class="mt-2">
<VButton
color="primary"
icon="fas fa-plus"
@click="addNewEstimateRow"
>
추가
</VButton>
</div>
</div>
</td>
</tr>
<tr>
<td>규격입력</td>
<td colspan="2">
<VButton
color="primary"
icon="fas fa-plus"
elevated
@click="detailActionsOpen = true"
style="width: 100%;"
>
상세 규격 입력
</VButton>
<VModal
:open="detailActionsOpen"
actions="center"
title="상세 규격 입력"
size="contract-big"
@close="detailActionsOpen = false"
>
<template #content>
<VButton color="success">일괄업로드</VButton>
<ComVFlexTable
:key="params.dtlSpecs.length"
:data="params.dtlSpecs"
:columns="params.dtlSpecsColumn"
:separators="true"
:clickable="true"
>
<template #body-cell="{ row, column, index, value }">
<!-- : 특정 컬럼이면 input, 아니면 그냥 출력 -->
<div>
<input
v-if="column.editable"
v-model="row[column.key]"
class="editable-input"
/>
<span v-else-if="column.key=='num'">{{index+1}}</span>
<span v-else class="lnil lnil-close"
@click="onDetailDelete(index)">{{ value }}</span>
</div>
</template>
</ComVFlexTable>
<div class="mt-2">
<VButton
color="primary"
icon="fas fa-plus"
@click="addNewDetailRow"
>
추가
</VButton>
</div>
</template>
<template #action>
<VButton color="primary">
등록
</VButton>
</template>
</VModal>
</td>
<td>등록기간</td>
<td colspan="5">
<div class="columns">
<div class="column is-5">
<VDatePicker
v-model="generalParams.regSdat"
color="green"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="시작일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
<div style="transform: translateY(15px)">~</div>
<div class="column is-5">
<VDatePicker
v-model="generalParams.regEdat"
color="green"
trim-weeks
:minDate="today"
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="종료일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</td>
</tr>
<tr>
<td>첨부파일</td>
<td colspan="10">
<VField class="file has-name is-left">
<VButton @click="openFileDialog">
파일 첨부
</VButton>
<input
ref="fileInput"
type="file"
style="display: none"
@change="onFileChange"
/>
<span v-if="fileName" class="file-name">{{ fileName }}</span>
</VField>
</td>
</tr>
</tbody>
</table>
</div>
<div class="column is-12">
<VUser v-model="apprLine"/>
</div>
<div class="column is-12">
<VField class="pr-2">
<VLabel class="has-fullwidth">
결재선
</VLabel>
</VField>
<ComVFlexTable
:data="apprLine"
:columns="params.felxColumn"
:separators="true"
:clickable="true"
:compact="true">
<template #body-cell="{ row, column, index, value }">
<!-- : 특정 컬럼이면 input, 아니면 그냥 출력 -->
<div>
<!-- <span v-if="column.key=='gubunCd' && index == 0" class="column">-->
<!-- {{row.gubunNm}}-->
<!-- </span>-->
<span v-if="column.key=='gubunCd'" class="column">
<VField class="pr-1">
<VCodeSelect
cd_grp=7
v-model="row.gubunCd"
/>
<!-- :disabled="index === 0"-->
</VField>
</span>
<span v-else-if="column.key=='attendCd' && index != 0" class="column">
<VField class="pr-1">
<VCodeSelect
placeholder="재중"
cd_grp=6
v-model="row.attendCd"/>
</VField>
</span>
<span v-else
@click="onDetailDelete(index)">{{value}}</span>
</div>
</template>
</ComVFlexTable>
</div>
<VButton
color="primary"
@click.stop="savePriceOne"
raised>
등록
</VButton>
</div>
</div>
</template>
<style scoped lang="scss">
.table tbody td {
color: var(--smoke-white);
}
.datatable-table {
padding: 12px 12px;
td:nth-child(1) {
background-color: var(--primary);
}
td:nth-child(3) {
background-color: var(--primary);
}
}
</style>

187
src/pages/app/approval.vue Normal file
View File

@@ -0,0 +1,187 @@
<script setup lang="ts">
import { getPriceList } from '/@src/service/price'
onBeforeMount(async () => {
await getPriceListData()
})
const selectedCode = ref()
const paymentCode = ref()
const emits = defineEmits(['on-tr-click'])
const masks = ref({
modelValue: 'YYYY-MM-DD',
})
const params = reactive({
priceData: [],
felxColumn: [
{ key: 'cateNm', label: '분야' },
{ key: 'title', label: '제목' },
{ key: 'regNm', label: '담당자' },
{ key: 'regSdat', label: '최초등록일' },
{ key: 'stNm', label: '등록 상태' },
{ key: 'title', label: '비고' }
],
prcsParams: []
})
async function getPriceListData(){
params.prcsParams = {
params:{
regSdt: '1970-01-01',
regEdt: '2070-01-01',
page: '1',
row: '3'
}
}
const result = await getPriceList(params.prcsParams)
params.priceData = result.content
}
const searchPrice = () => {
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div>
<div class="datatable-toolbar">
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
분야
</VLabel>
<VCodeSelect
cd_grp=5
v-model="selectedCode"/>
</VField>
</div>
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
결제상태
</VLabel>
<VCodeSelect
cd_grp=2
v-model="paymentCode"/>
</VField>
</div>
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
담당자
</VLabel>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="담당자"
>
</VControl>
</VField>
</div>
<div class="column is-5">
<VField class="pr-2">
<VLabel class="has-fullwidth">
등록기간
</VLabel>
<VControl>
<div class="columns">
<div class="column is-6">
<VDatePicker
v-model.string="params.regSdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="시작일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
<div style="transform: translateY(15px)">~</div>
<div class="column is-6">
<VDatePicker
v-model.string="params.regEdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="종료일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</VControl>
</VField>
</div>
<div class="column is-1">
<div style="padding-top:20px;float:right;">
<VButtons>
<VButton
color="primary"
elevated
icon="fas fa-search"
@click.stop="searchPrice"
>
검색
</VButton>
</VButtons>
</div>
</div>
</div>
<div class="datatable-wrapper">
<ComVFlexTable
:data="params.priceData"
:columns="params.felxColumn"
:separators="true"
:clickable="true"
:compact="true"
/>
</div>
</div>
<VButtons class="v-buttons-right-align">
<VButton
color="primary"
icon="fas fa-plus"
elevated
to="/app/PriceInsert"
>
등록
</VButton>
</VButtons>
</div>
</template>
<style scoped lang="scss">
.v-buttons-right-align {
display: flex;
justify-content: flex-end;
margin-top: 1rem; // 필요 시 간격 추가
}
</style>

View File

@@ -0,0 +1,255 @@
<script setup lang="ts">
import axios from 'axios'
import {useRouter} from "vue-router";
export type MinimalTheme = 'darker' | 'light'
const emits = defineEmits(['on-search', 'on-tr-click'])
const selUser = ref()
const masks = ref({
modelValue: 'YYYY-MM-DD',
})
const selectedCode = ref()
watch(selUser, (value) => {
console.log(value)
})
const data = reactive({
contractData: [],
priceData: [],
})
onMounted(async () => {
try {
const contractResponse = await axios.get('/api/cont/prcs')
data.contractData = Array.isArray(contractResponse.data) ? contractResponse.data : []
}
catch (error) {
console.log(error)
data.contractData = []
}
})
const registerFormOpen = ref(false)
const isLoading = ref(false)
watch(registerFormOpen, async (isOpen) => {
if (isOpen) {
isLoading.value = true
// error.value = null
try {
const priceResponse = await axios.get('/api/prcs/page?regSdt=1970-01-01&regEdt=2070-01-01&page=1&row=10')
console.log(priceResponse.data.content)
data.priceData = Array.isArray(priceResponse.data.content) ? priceResponse.data.content : []
}
catch (error) {
console.log(error)
data.priceData = []
}
}
})
const router = useRouter()
function handleRowClick(row) {
router.push({
name: 'DocumentManagement',
params: { id: row.contractDetailedData },
})
}
const params = reactive({
priceData: [],
flexColumn: [
{ key: 'cateNm', label: '분야' },
{ key: 'title', label: '제목' },
{ key: 'regNm', label: '담당자' },
{ key: 'regSdat', label: '최초등록일' },
{ key: 'stNm', label: '등록 상태' },
{ key: 'title', label: '비고' },
],
prcsParams: [],
})
</script>
<template>
<div class="page-content is-navbar-lg">
<div>
<div class="datatable-toolbar">
<div class="column is-2">
<!-- <VUserList v-model="selUser"/>-->
<VField class="pr-2">
<VLabel class="has-fullwidth">
분야
</VLabel>
<VCodeSelect
cd_grp=5
v-model="selectedCode"/>
</VField>
</div>
<div class="column is-2">
<!-- <VUserList v-model="selUser"/>-->
<VField class="pr-2">
<VLabel class="has-fullwidth">
계약번호
</VLabel>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="계약번호"
>
</VControl>
</VField>
</div>
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
담당자
</VLabel>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="담당자명"
>
</VControl>
</VField>
</div>
<div class="column is-5">
<VField class="pr-2">
<VLabel class="has-fullwidth">
계약일
</VLabel>
<VControl>
<div class="columns">
<div class="column is-6">
<VDatePicker
v-model="params.regSdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="시작일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
<div style="transform: translateY(15px)">
~
</div>
<div class="column is-6">
<VDatePicker
v-model="params.regEdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="종료일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</VControl>
</VField>
</div>
<div class="column is-1" style="position:relative;">
<div style="padding-top:20px;float:right;">
<VButtons>
<VButton
color="primary"
elevated
icon="fas fa-search"
@click.stop="emits('on-search', params)"
>
검색
</VButton>
</VButtons>
</div>
</div>
</div>
<div class="datatable-wrapper">
<ComVFlexTable
:data="data.contractData"
:columns="params.flexColumn"
:separators="true"
:clickable="true"
:rounded="true"
:compact="true"
/>
</div>
</div>
<VButtons>
<VButton
color="primary"
icon="fas fa-plus"
elevated
to="/app/ContractInsert"
style="margin-top: 20px"
>
등록
</VButton>
</VButtons>
<!-- 모달창 시작-->
<VModal
is="form"
:open="registerFormOpen"
title="계약관리 등록"
size="contract-big"
actions="right"
@submit.prevent="registerFormOpen = false"
@close="registerFormOpen = false"
>
<template #content>
<div class="modal-form">
<ComVFlexTable
:data="data.priceData"
:columns="{ cateNm: '분야',
title: '제목',
content: '담당자',
stNm: '등록상태',
stCd: '비고',
regSabun: '선택',
}"
:compact="true"
:separators="true"
@row-click="handleRowClick"
/>
</div>
</template>
<template #action>
<VButton
type="submit"
color="primary"
raised
>
Save Changes
</VButton>
</template>
</VModal>
</div>>
</template>

5
src/pages/app/index.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<div>
<!-- Your content here -->
</div>
</template>

View File

@@ -0,0 +1,481 @@
<script setup lang="ts">
// import { getIntegratedApproval } from '/src/service/integratedApprovalApi'
import { getIntegratedApproval, updateIntegratedApproval } from '/src/service/integratedPayment'
import type { VFlexTableWrapperSortFunction, VFlexTableWrapperFilterFunction } from '/src/components/app-vuero/ComVFlexTableWrapper.vue'
import { users } from '/src/data/layouts/card-grid-v1'
onBeforeMount(async () => {
await getIntegratedPaymentList()
})
const isModalOpen = ref(false)
const selectedRow = ref<any>(null)
const masks = ref({
modelValue: 'YYYY-MM-DD',
})
const params = reactive({
title: '',
priceData: [],
flexColumn: [
{ key: 'apprNo', label: '결재번호', cellClass: 'paymentColumn1' },
{ key: 'title', label: '제목', cellClass: 'paymentColumn2' },
{ key: 'name', label: '작성자', cellClass: 'paymentColumn3' },
{
key: 'regDt',
label: '등록일',
cellClass: 'paymentColumn4',
format: formatRegDt,
},
{ key: 'process', label: '구분', cellClass: 'paymentColumn5' },
],
flexWrapperColumn: {
apprNo: { label: '결재번호', cellClass: 'paymentColumn1', searchable: true, sortable: true },
title: { label: '제목', cellClass: 'paymentColumn2', searchable: true, sortable: true },
name: { label: '작성자', cellClass: 'paymentColumn3', searchable: true, sortable: true },
regDt: { label: '등록일', cellClass: 'paymentColumn4', searchable: true, sortable: true },
process: { label: '구분', cellClass: 'paymentColumn5', searchable: true, sortable: true },
},
paymentParams: [],
})
function formatRegDt(value) {
return value ? value.substring(0, 16) : ''
}
async function getIntegratedPaymentList() {
const paymentParams = {
title: '',
page: '1',
row: '10',
sabun: '17131303',
}
const result = await getIntegratedApproval(paymentParams)
params.paymentParams = result.content.map(item => ({
...item,
process: gubunMap[item.gubun] || '',
}))
}
async function updateIntegratedPaymentList() {
const paymentUpdateParams = {
apprNo: selectedRow.value.apprNo,
apprOrd: selectedRow.value.apprOrd,
apprStatCd: '0200', // 결재 상태변경 (결재승인 0200 결재회수 0300 결재반려 0400)
reason: '',
sabun: selectedRow.value.sabun,
}
const result = await updateIntegratedApproval(paymentUpdateParams)
console.log(result)
alert('결재승인완료')
window.location.reload(true)
}
const gubunMap = {
SAP: '전표생성',
SVCM: '가격조사',
}
const searchApproval = async () => {
const paymentParams = {
params: {
title: params.title,
page: '1',
row: '10',
},
}
const result = await getIntegratedApproval(paymentParams)
params.paymentParams = result.content
console.log(params.paymentParams)
}
type User = (typeof users)[0]
// duplicate user data to grow the array
const data: User[] = []
for (let i = 0; i < 1000; i++) {
data.push(...users)
}
// this is a sample for custom sort function
const locationSorter: VFlexTableWrapperSortFunction<User> = ({ order, a, b }) => {
if (order === 'asc') {
return a.location.localeCompare(b.location)
}
else if (order === 'desc') {
return b.location.localeCompare(a.location)
}
return 0
}
// this is a sample for custom filter function
const userFilter: VFlexTableWrapperFilterFunction<User> = ({ searchTerm, row }) => {
if (!searchTerm) {
return true
}
// search either in the name or the bio
return (
row.name.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase())
|| row.bio.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase())
)
}
// 로우 클릭 핸들러
const onRowClick = (row: any) => {
selectedRow.value = row
isModalOpen.value = true
console.log(selectedRow)
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div>
<div class="datatable-toolbar">
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
결재제목
</VLabel>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="결재제목"
>
</VControl>
</VField>
</div>
<div class="column is-5">
<VField class="pr-2">
<VLabel class="has-fullwidth">
등록기간
</VLabel>
<VControl>
<div class="columns">
<div class="column is-6">
<VDatePicker
v-model.string="params.regSdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="시작일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
<div style="transform: translateY(15px)">
~
</div>
<div class="column is-6">
<VDatePicker
v-model.string="params.regEdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="종료일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</VControl>
</VField>
</div>
<div class="column is-1">
<div style="padding-top:20px;float:right;">
<VButtons>
<VButton
color="primary"
elevated
icon="fas fa-search"
@click.stop="searchApproval"
>
검색
</VButton>
</VButtons>
</div>
</div>
</div>
<div class="datatable-wrapper">
<ComVFlexTable
:data="params.paymentParams"
:columns="params.flexColumn"
:separators="true"
:clickable="true"
:compact="true"
:use-payment-header="true"
@row-click="onRowClick"
>
<template #payment-header>
<div class="flex-table-header">
<span class="flex-table-item paymentColumn1">결재번호</span>
<span class="flex-table-item paymentColumn2">제목</span>
<span class="flex-table-item paymentColumn3">작성자</span>
<span class="flex-table-item paymentColumn4">등록일</span>
<span class="flex-table-item paymentColumn5">구분</span>
</div>
</template>
</ComVFlexTable>
<!-- 모달 컴포넌트 추가 -->
<VModal
is="form"
v-model:open="isModalOpen"
title="결재창"
size="big"
actions="center"
@submit.prevent="isModalOpen = false"
@close="isModalOpen = false"
>
<template #content>
<div class="modal-form">
<div class="columns is-multiline">
<div class="column is-4">
<div class="field">
<label>결재번호</label>
<div class="control">
<input
type="text"
class="input"
placeholder="결재번호"
:value="selectedRow?.apprNo"
readonly
>
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label>제목</label>
<div class="control">
<input
type="text"
class="input"
placeholder="제목"
:value="selectedRow?.title"
readonly
>
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label>작성자</label>
<div class="control">
<input
type="text"
class="input"
placeholder="작성자"
:value="selectedRow?.name"
readonly
>
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label>구분</label>
<div class="control">
<input
type="text"
class="input"
placeholder="구분"
:value="selectedRow?.process"
readonly
>
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label>결재상태</label>
<div class="control">
<input
type="text"
class="input"
placeholder="결재상태"
:value="selectedRow?.apprStat"
readonly
>
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label>등록일</label>
<div class="control">
<input
type="text"
class="input"
placeholder="등록일"
:value="selectedRow?.regDt"
readonly
>
</div>
</div>
</div>
</div>
</div>
</template>
<template #action>
<VButton type="submit" color="info" raised>반려</VButton>
<VButton type="submit" color="primary" raised @click="updateIntegratedPaymentList">승인</VButton>
</template>
</VModal>
<!-- <VFlexTableWrapper :columns="params.flexWrapperColumn" :data="params.paymentParams">-->
<!--&lt;!&ndash;-->
<!-- Here we retrieve the internal wrapperState.-->
<!-- Note that we can not destructure it-->
<!-- &ndash;&gt;-->
<!-- <template #default="wrapperState">-->
<!-- <pre>data: {{ data }}</pre>-->
<!-- &lt;!&ndash; We can place any content inside the default slot&ndash;&gt;-->
<!-- <VFlexTableToolbar>-->
<!-- <template #left>-->
<!-- &lt;!&ndash; We can bind wrapperState.searchInput to any input &ndash;&gt;-->
<!-- <VField>-->
<!-- <VControl icon="lucide:search">-->
<!-- <input-->
<!-- v-model="wrapperState.searchInput"-->
<!-- type="text"-->
<!-- class="input is-rounded"-->
<!-- placeholder="Filter..."-->
<!-- >-->
<!-- </VControl>-->
<!-- </VField>-->
<!-- </template>-->
<!-- <template #right>-->
<!-- &lt;!&ndash; We can also bind wrapperState.limit &ndash;&gt;-->
<!-- <VField>-->
<!-- <VControl>-->
<!-- <div class="select is-rounded">-->
<!-- <select v-model="wrapperState.limit">-->
<!-- <option :value="1">-->
<!-- 1 results per page-->
<!-- </option>-->
<!-- <option :value="10">-->
<!-- 10 results per page-->
<!-- </option>-->
<!-- <option :value="15">-->
<!-- 15 results per page-->
<!-- </option>-->
<!-- <option :value="25">-->
<!-- 25 results per page-->
<!-- </option>-->
<!-- <option :value="50">-->
<!-- 50 results per page-->
<!-- </option>-->
<!-- </select>-->
<!-- </div>-->
<!-- </VControl>-->
<!-- </VField>-->
<!-- </template>-->
<!-- </VFlexTableToolbar>-->
<!-- &lt;!&ndash;-->
<!-- The VFlexTable "data" and "columns" props-->
<!-- will be inherited from parent VFlexTableWrapper-->
<!-- &ndash;&gt;-->
<!-- <VFlexTable rounded>-->
<!-- &lt;!&ndash; Custom "name" cell content &ndash;&gt;-->
<!-- <template #body-cell="{ row, column }">-->
<!-- <template v-if="column.key === 'name'">-->
<!-- <VAvatar-->
<!-- size="medium"-->
<!-- :picture="row.medias.avatar"-->
<!-- :badge="row.medias.badge"-->
<!-- :initials="row.initials"-->
<!-- />-->
<!-- <div>-->
<!-- <span class="dark-text" :title="row.name">-->
<!-- {{ row?.shortname }}-->
<!-- </span>-->
<!-- <VTextEllipsis-->
<!-- width="280px"-->
<!-- class="light-text"-->
<!-- :title="row.bio"-->
<!-- >-->
<!-- <small>{{ row?.bio }}</small>-->
<!-- </VTextEllipsis>-->
<!-- </div>-->
<!-- </template>-->
<!-- </template>-->
<!-- </VFlexTable>-->
<!-- &lt;!&ndash; Table Pagination with wrapperState.page binded&ndash;&gt;-->
<!-- <VFlexPagination-->
<!-- v-model:current-page="wrapperState.page"-->
<!-- class="mt-6"-->
<!-- :item-per-page="wrapperState.limit"-->
<!-- :total-items="wrapperState.total"-->
<!-- :max-links-displayed="5"-->
<!-- no-router-->
<!-- />-->
<!-- </template>-->
<!-- </VFlexTableWrapper>-->
</div>
</div>
<!-- <VButtons class="v-buttons-right-align">-->
<!-- <VButton-->
<!-- color="primary"-->
<!-- icon="fas fa-plus"-->
<!-- elevated-->
<!-- to="/app/PriceInsert"-->
<!-- >-->
<!-- 등록-->
<!-- </VButton>-->
<!-- </VButtons>-->
</div>
</template>
<style scoped lang="scss">
input[readonly] {
background-color: #f5f5f5; /* 살짝 회색 배경 */
color: #888; /* 글자색 연하게 */
cursor: not-allowed; /* 마우스 커서 변경 */
border-color: #e0e0e0; /* 테두리 연하게 */
}
.v-buttons-right-align {
display: flex;
justify-content: flex-end;
margin-top: 1rem; // 필요 시 간격 추가
}
.flex-table .flex-table-item {
background-color: var(--primary);
border: 0px;
}
.flex-table .flex-table-header {
padding: 0px;
}
.flex-table.is-table-clickable .flex-table-item:hover, .flex-table.is-table-clickable .flex-table-item:focus-within a:hover {
background-color: var(--primary)!important;
}
</style>

View File

@@ -0,0 +1,413 @@
<script setup lang="ts">
import {getDetailPrcs, updatePrcsNo} from '/src/service/priceApi'
import { type Person } from '/@src/utils/types'
const notyf = useNotyf()
const loading = ref(false)
const router = useRouter()
onBeforeMount(async ()=>{
const result = await getDetailPrcs(history.state.key)
getDetailList(result)
})
const showTable = ref(false)
const detailActionsOpen = ref(false)
const apprLine = defineModel<Person[]>()
const generalParams = reactive({
title: "",
content: "",
regSdat: "",
regEdat: "",
prvYn: true,
prvRsn : "",
prvPwd : "",
aiYn: true,
})
const params = reactive({
cateSelect: '',
prcsNo: '', // 키값
stCd:'',//결재상태 코드{ 등록중:0100[회수버튼],
// 회수: 0300, 반려: 0400, 등록 완료: 0200 }
prcsAttsColumn:[ //첨부파일 입력
{ key: 'logiFnm', label: '구분'},
{ key: 'data', label: '데이터'}
],
prcsAtts: [], //첨부파일 데이터
felxColumn: [
{ key: 'gubunNm', label: '구분'},
{ key: 'deptNm', label: '부서' },
{ key: 'sabun', label: '사번' },
{ key: 'name', label: '이름' },
{ key: 'attendNm', label: '비고' },
{ key: 'apprStat', label: '결재상태'},
{ key: 'date', label: '승인일자'},
],
priceData:[],
prcsBizsColumn: [ //견적사 입력
{ key: 'num', label: '구분', width: '10%' },
{ key: 'email', label: '이메일', editable: true, width: '50px' },
{ key: 'bizNo', label: '사업자번호', editable: true, width: '50px'},
],
prcsBizs: [], //견적사 입력 데이터
dtlSpecsColumn: [
{ key: 'num', label: '번호', editable: false },
{ key: 'itemNm', label: '품명', editable: true },
{ key: 'spec', label: '규격', editable: true },
{ key: 'unit', label: '단위', editable: true },
{ key: 'qty', label: '수량', editable: true },
{ key: '', label: '단가(VAT별도)', editable: false },
{ key: '', label: '금액(VAT별도)', editable: false },
],
dtlSpecs: [], //상세 규격 입력 데이터
})
function getDetailList(arg){
params.prcsNo = arg.prcsNo
params.stCd = arg.stCd
params.cateSelect = arg.cateNm
generalParams.title = arg.title
generalParams.content = arg.content
params.prcsBizs = arg.prcsBizs.map(req => ({
bizNo: req.bizNo,
email: req.email,
}))
params.dtlSpecs = arg.dtlSpecs
generalParams.regSdat = formatDate(arg.regSdat)
generalParams.regEdat = formatDate(arg.regEdat)
apprLine.value = arg.apprMst.apprReqs.map(req => ({
gubunNm: req.gubunNm,
deptCd: req.deptCd,
deptNm: req.deptNm,
sabun: req.sabun,
name: req.name,
apprNo: req.apprNo,
apprOrd: req.apprOrd,
apprStat: req.apprStat,
attendNm: req.attendNm
})) //비고 데이터 없음, 승인일자 없음 todo
}
const updateState = async () => {
let res = null
try {
loading.value = true
const paramsPrice = {
prcsNo : params.prcsNo
}
res = await updatePrcsNo(paramsPrice.prcsNo)
notyf.dismissAll()
if (res.request.status === 200) {
notyf.primary('회수 되었습니다.')
router.push({path: '/app/priceManagement'})
}
} catch (e) {
notyf.error(e.message)
} finally {
loading.value = false
}
}
function formatDate(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
return date.toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).replace(/\./g, '-').replace(/\s/g, '').replace(/-$/,'')
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div class="datatable-wrapper">
<div class="table-container">
<table class="table datatable-table is-fullwidth">
<colgroup>
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
</colgroup>
<tbody>
<tr>
<td>분야</td>
<td colspan="2">
<span class="column is-7">
<input
:readonly=true
v-model="params.cateSelect"
class="input custom-text-filter"
placeholder="제목"
>
</span>
</td>
<td>제목</td>
<td colspan="3">
<div class="column is-fullhd">
<VField class="pr-2">
<VControl>
<input
:readonly=true
v-model="generalParams.title"
class="input custom-text-filter"
placeholder="제목"
>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>내용</td>
<td colspan="6">
<div class="column is-fullhd">
<VField class="pr-2">
<VControl>
<textarea
:readonly=true
v-model="generalParams.content"
class="input custom-text-filter"
placeholder="내용"
/>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>규격입력</td>
<td colspan="2">
<VButton
:color="params.btnChangeFlag? 'success':'primary'"
icon="fas fa-plus"
elevated
@click="detailActionsOpen = true"
>
<span> 상세 규격 입력</span>
</VButton>
<VModal
:open="detailActionsOpen"
actions="center"
title="상세 규격 입력"
size="contract-big"
cancelLabel="닫기"
@close="detailActionsOpen = false"
>
<template #content>
<ComVFlexTable
:key="params.dtlSpecs.length"
:data="params.dtlSpecs"
:columns="params.dtlSpecsColumn"
:separators="true"
:clickable="true"
>
<template #body-cell="{ row, column, index, value }">
<!-- : 특정 컬럼이면 input, 아니면 그냥 출력 -->
<div>
<input
:readonly=true
v-if="column.editable"
v-model="row[column.key]"
class="editable-input"
/>
<span v-else-if="column.key=='num'">{{index+1}}</span>
<span v-else>{{ value }}</span>
</div>
</template>
</ComVFlexTable>
</template>
<template #cancel>
</template>
</VModal>
</td>
<td>등록기간</td>
<td colspan="5">
<div class="columns">
<div class="column is-5">
<!-- <VDatePicker-->
<!-- v-model="generalParams.regSdat"-->
<!-- color="green"-->
<!-- disabled-->
<!-- trim-weeks-->
<!-- style="pointer-events: none;"-->
<!-- >-->
<!-- <template #default="{ inputValue, inputEvents }">-->
<!-- <VField>-->
<!-- <VControl icon="lucide:calendar">-->
<!-- <input-->
<!-- :readonly=true-->
<!-- class="input v-input"-->
<!-- type="text"-->
<!-- :value="inputValue"-->
<!-- v-on="inputEvents"-->
<!-- placeholder="시작일"-->
<!-- >-->
<!-- </VControl>-->
<!-- </VField>-->
<!-- </template>-->
<!-- </VDatePicker>-->
<input
:readonly=true
v-model="generalParams.regSdat"
class="input custom-text-filter"
placeholder="제목"
>
</div>
<div style="transform: translateY(15px)">~</div>
<div class="column is-5">
<!-- <VDatePicker-->
<!-- v-model="generalParams.regEdat"-->
<!-- color="green"-->
<!-- trim-weeks-->
<!-- disabledDates=""-->
<!-- >-->
<!-- <template #default="{ inputValue, inputEvents }">-->
<!-- <VField>-->
<!-- <VControl icon="lucide:calendar">-->
<!-- <input-->
<!-- :readonly=true-->
<!-- class="input v-input"-->
<!-- type="text"-->
<!-- :value="inputValue"-->
<!-- v-on="inputEvents"-->
<!-- placeholder="종료일"-->
<!-- >-->
<!-- </VControl>-->
<!-- </VField>-->
<!-- </template>-->
<!-- </VDatePicker>-->
<input
:readonly=true
v-model="generalParams.regEdat"
class="input custom-text-filter"
placeholder="제목"
>
</div>
</div>
</td>
</tr>
<tr>
<td>첨부파일</td>
<td colspan="3">
<VField class="file has-name is-right">
<div class="file-label">
<input
class="file-input"
type="file"
name="resume">
<span class="file-cta">
<span class="file-icon">
<i class="fas fa-cloud-upload-alt"/>
</span>
<span class="file-label">첨부파일</span>
</span>
<span class="file-name light-text">2022.xls</span>
</div>
</VField>
</td>
</tr>
<tr>
<td>견적요청</td>
<td colspan="6">
<VButton
color="primary"
icon="fas fa-plus"
elevated
@click="showTable = !showTable"
>
견적사 입력
</VButton>
<div v-if="showTable" class="mt-2">
<ComVFlexTable
:key="params.prcsBizs.length"
:data="params.prcsBizs"
:columns="params.prcsBizsColumn"
:compact="true"
:separators="true"
:clickable="true"
>
<template #body-cell="{ row, column, index, value }">
<div>
<!-- 다른 editable 컬럼은 input -->
<input
:readonly=true
v-if="column.editable"
v-model="row[column.key]"
class="editable-input"
/>
<span v-else-if="column.key=='num'">{{index + 1}}</span>
<!-- readonly 출력 -->
<span v-else>{{ value }}</span>
</div>
</template>
</ComVFlexTable>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="column is-12">
<VField class="pr-2">
<VLabel class="has-fullwidth">
결재선
</VLabel>
</VField>
<ComVFlexTable
:data="apprLine"
:columns="params.felxColumn"
:separators="true"
:clickable="true"
:compact="true">
<template #body-cell="{ row, column, index, value }">
<!-- : 특정 컬럼이면 input, 아니면 그냥 출력 -->
<div>
<span>{{value}}</span>
</div>
</template>
</ComVFlexTable>
</div>
<div>
<VButton
v-if="params.stCd === '0100'"
color="warning"
@click.stop="updateState"
>
회수
</VButton>
<VButton
to="/app/priceManagement"
v-if="params.stCd === '0100' | '0200' | '0300' | '0400'"
color="info"
>
닫기
</VButton>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.table tbody td {
color: var(--smoke-white);
}
.datatable-table {
padding: 12px 12px;
td:nth-child(1) {
background-color: var(--primary);
}
td:nth-child(3) {
background-color: var(--primary);
}
}
</style>

View File

@@ -0,0 +1,217 @@
<script setup lang="ts">
import { getPriceList } from '/@src/service/priceApi'
const router = useRouter()
onBeforeMount(async () => {
await getPriceListData()
const userSession = useUserSession()
params.sessionUser = userSession.user.data
})
const masks = ref({
modelValue: 'YYYY-MM-DD',
})
const searchParamsList = reactive({
cateCd : '', //분야코드
stCd : '', //등록상태
regNm : '', //담당자
regSdt: '',//등록시작일
regEdt: '',//등록종료일
page: 1,//페이지
row: 3 //아이템갯수
})
const params = reactive({
sessionUser:'',
priceData: [],
felxColumn: [
{ key: 'cateNm', label: '분야' },
{ key: 'title', label: '제목' },
{ key: 'regNm', label: '담당자' },
{ key: 'regDt', label: '등록일' },
{ key: 'stNm', label: '등록 상태' },
]
})
async function getPriceListData(){
const priceBase = {
params:{
regSdt: '1970-01-01',
regEdt: '2070-01-01',
page: '1',
row: '20'
}
}
const result = await getPriceList(priceBase)
params.priceData = result.content
}
const searchPrice = async () => {
const searchParams = {
params: {
cateCd : searchParamsList.cateCd, //분야코드
stCd : searchParamsList.stCd, //등록상태
regNm : searchParamsList.regNm, //담당자
regSdt: searchParamsList.regSdt,//등록시작일
regEdt: searchParamsList.regEdt,//등록종료일
page: 1,//페이지
row: 3 //아이템갯수
}
}
const result = await getPriceList(searchParams)
params.priceData = result.content
}
function getPriceDetail(){
//stCd 결재상태 코드 [등록전:0000, 등록중:0100, ]
if(params.sessionUser.sabun == arguments[0].regSabun && arguments[0].stCd == '0000'){
router.push({ path: '/app/priceUpdate', state: { key: arguments[0].prcsNo }})
}else{
router.push({ path: '/app/priceDetail', state: { key: arguments[0].prcsNo }})
}
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div>
<div class="datatable-toolbar">
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
분야
</VLabel>
<VCodeSelect
cd_grp=5
v-model="searchParamsList.cateCd"
placeholder="전체"/>
</VField>
</div>
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
등록상태
</VLabel>
<VCodeSelect
cd_grp=1
v-model="searchParamsList.stCd"
placeholder="전체"/>
</VField>
</div>
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
담당자
</VLabel>
<VControl>
<input
v-model="searchParamsList.regNm"
class="input custom-text-filter"
placeholder="담당자"
>
</VControl>
</VField>
</div>
<div class="column is-5">
<VField class="pr-2">
<VLabel class="has-fullwidth">
등록기간
</VLabel>
<VControl>
<div class="columns">
<div class="column is-6">
<VDatePicker
v-model.string="searchParamsList.regSdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="시작일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
<div style="transform: translateY(15px)">~</div>
<div class="column is-6">
<VDatePicker
v-model.string="searchParamsList.regEdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="종료일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</VControl>
</VField>
</div>
<div class="column is-1">
<div style="padding-top:20px;float:right;">
<VButtons>
<VButton
color="primary"
elevated
icon="fas fa-search"
@click.stop="searchPrice"
>
검색
</VButton>
</VButtons>
</div>
</div>
</div>
<div class="datatable-wrapper">
<ComVFlexTable
:data="params.priceData"
:columns="params.felxColumn"
:separators="true"
:clickable="true"
:compact="true"
@rowClick="getPriceDetail"
/>
</div>
</div>
<VButtons class="v-buttons-right-align">
<VButton
color="primary"
icon="fas fa-plus"
elevated
to="/app/PriceInsert"
>
등록
</VButton>
</VButtons>
</div>
</template>
<style scoped lang="scss">
.v-buttons-right-align {
display: flex;
justify-content: flex-end;
margin-top: 1rem; // 필요 시 간격 추가
}
</style>

View File

@@ -0,0 +1,509 @@
<script setup lang="ts">
import {getDetailPrcs, updatePrice} from '/src/service/priceApi'
import { type Person } from '/@src/utils/types'
const notyf = useNotyf()
const loading = ref(false)
const router = useRouter()
onBeforeMount(async ()=>{
const result = await getDetailPrcs(history.state.key)
getDetailList(result)
})
const showTable = ref(false)
const detailActionsOpen = ref(false)
const apprLine = defineModel<Person[]>()
const generalParams = reactive({
title: "",
content: "",
regSdat: "",
regEdat: "",
prvYn: true,
prvRsn : "",
prvPwd : "",
aiYn: true,
})
const params = reactive({
cateSelect: '',
prcsNo: '',
stCd: '', //상태코드 등록 전 : 0000
prcsAttsColumn:[ //첨부파일 입력
{ key: 'logiFnm', label: '구분'},
{ key: 'data', label: '데이터'}
],
prcsAtts: [], //첨부파일 데이터
felxColumn: [
{ key: 'gubunCd', label: '구분'},
{ key: 'deptNm', label: '부서' },
{ key: 'sabun', label: '사번' },
{ key: 'name', label: '이름' },
{ key: 'attendCd', label: '비고' },
{ key: 'apprStat', label: '결재상태'},
{ key: 'date', label: '승인일자'},
],
priceData:[],
prcsBizsColumn: [ //견적사 입력
{ key: 'num', label: '구분', width: '10%' },
{ key: 'email', label: '이메일', editable: true, width: '50px' },
{ key: 'bizNo', label: '사업자번호', editable: true, width: '50px'},
{ key: 'actions', label: '동작', width: '100px'}
],
prcsBizs: [], //견적사 입력 데이터
dtlSpecsColumn: [
{ key: 'num', label: '번호', editable: false },
{ key: 'itemNm', label: '품명', editable: true },
{ key: 'spec', label: '규격', editable: true },
{ key: 'unit', label: '단위', editable: true },
{ key: 'qty', label: '수량', editable: true },
{ key: '', label: '단가(VAT별도)', editable: false },
{ key: '', label: '금액(VAT별도)', editable: false },
{ key: '', label: '삭제', editable: false },
],
dtlSpecs: [], //상세 규격 입력 데이터
detailData :[
{ cateNm: '홍길동', age: 30, email: 'hong@example.com' },
{ name: '김철수', age: 28, email: 'kim@example.com' },
],
btnChangeFlag: false
})
function getDetailList(arg){
params.prcsNo = arg.prcsNo
params.stCd = arg.stCd
params.cateSelect = arg.cateCd
generalParams.title = arg.title
generalParams.content = arg.content
params.prcsBizs = arg.prcsBizs.map(req => ({
bizNo: req.bizNo,
email: req.email,
}))
params.dtlSpecs = arg.dtlSpecs
generalParams.regSdat = arg.regSdat
generalParams.regEdat = arg.regEdat
apprLine.value = arg.apprMst.apprReqs.map(req => ({
gubunCd: req.gubunCd,
deptCd: req.deptCd,
deptNm: req.deptNm,
sabun: req.sabun,
name: req.name,
apprNo: req.apprNo,
apprOrd: req.apprOrd,
apprStat: req.apprStat,
attendCd: req.attendCd
})) //비고 데이터 없음, 승인일자 없음 todo
}
const changeButton = () => {
//todo 상세 규격 api 테우기
params.btnChangeFlag = true
close()
}
const validation = () => {
notyf.dismissAll() //todo
if(generalParams.regSdat > generalParams.regEdat){
notyf.error("등록 종료일은 등록 시작일보다 빠를 수 없습니다.")
return
}
if(apprLine.value.length < 2){
notyf.error("결재선은 두 명이상 입력해주세요.")
return
}
}
const updatePriceOne = async () => {
let res = null
try{
loading.value = true
validation()
const paramsPrice ={
prcsNo : params.prcsNo,
cateCd : params.cateSelect,
title: generalParams.title,
content: generalParams.content,
regSdat: formatDate(generalParams.regSdat),
regEdat: formatDate(generalParams.regEdat),
prvYn: false,
prvRsn : "",
prvPwd : "",
aiYn: false,
prcsBizs: params.prcsBizs.map(({ num, ...rest }) => rest), //견적사 입력 데이터
dtlSpecs: [], //todo
// params.dtlSpecs.map(({ num, ...rest }) => rest), //상세 규격 입력 데이터
prcsAtts: params.prcsAtts, //첨부파일 데이터
apprReqs: apprLine.value.map(({ deptNm, ...rest }) => rest), //결재선 데이터
}
res = await updatePrice(paramsPrice)
notyf.dismissAll()
if(res.request.status == '200'){
notyf.primary('수정 되었습니다.')
router.push({path: '/app/priceManagement'})
}
}catch(e){
notyf.error(e.message)
}finally {
loading.value = false
}
}
const addNewEstimateRow = () => {
const newRow = {}
params.prcsBizsColumn.forEach(col => {
if (col.key && col.key !== 'actions') {
newRow[col.key] = ''
}
})
params.prcsBizs.push(newRow)
}
const onDelete = (index: number) => {
if(params.prcsBizs.length-1 !== params.prcsBizsColumn.length
|| (params.prcsBizsColumn.length == 4 && params.prcsBizs.length-1 == params.prcsBizsColumn.length))
{
params.prcsBizs.splice(index, 1)
}
}
const addNewDetailRow = () => {
const newRow = {}
params.dtlSpecsColumn.forEach(col => {
if (col.key) {
newRow[col.key] = ''
}
})
params.dtlSpecs.push(newRow)
console.log(params.dtlSpecs.length)
}
const onDetailDelete = (index: number) => {
if(params.dtlSpecs.length-1 !== params.dtlSpecsColumn.length
|| (params.dtlSpecsColumn.length == 8 && params.dtlSpecs.length-1 == params.dtlSpecsColumn.length))
{
params.dtlSpecs.splice(index, 1)
}
}
function formatDate(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
return date.toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).replace(/\./g, '-').replace(/\s/g, '').replace(/-$/,'')
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div class="datatable-wrapper">
<div class="table-container">
<table class="table datatable-table is-fullwidth">
<colgroup>
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
</colgroup>
<tbody>
<tr>
<td>분야</td>
<td colspan="2">
<span class="column is-7">
<VField class="pr-2">
<VCodeSelect
cd_grp=5
v-model="params.cateSelect"/>
</VField>
</span>
</td>
<td>제목</td>
<td colspan="3">
<div class="column is-fullhd">
<VField class="pr-2">
<VControl>
<input
v-model="generalParams.title"
class="input custom-text-filter"
placeholder="제목"
>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>내용</td>
<td colspan="6">
<div class="column is-fullhd">
<VField class="pr-2">
<VControl>
<textarea
v-model="generalParams.content"
class="input custom-text-filter"
placeholder="내용"
/>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>규격입력</td>
<td colspan="2">
<VButton
:color="params.btnChangeFlag? 'success':'primary'"
icon="fas fa-plus"
elevated
@click="detailActionsOpen = true"
>
<span v-if="params.btnChangeFlag == false"> 상세 규격 입력</span>
<span v-else-if="params.btnChangeFlag"> 상세 규격 등록 완료</span>
</VButton>
<VModal
:open="detailActionsOpen"
actions="center"
title="상세 규격 입력"
size="contract-big"
@close="detailActionsOpen = false"
>
<template #content>
<VButton color="success">일괄업로드</VButton>
<ComVFlexTable
:key="params.dtlSpecs.length"
:data="params.dtlSpecs"
:columns="params.dtlSpecsColumn"
:separators="true"
:clickable="true"
>
<template #body-cell="{ row, column, index, value }">
<!-- : 특정 컬럼이면 input, 아니면 그냥 출력 -->
<div>
<input
v-if="column.editable"
v-model="row[column.key]"
class="editable-input"
/>
<span v-else-if="column.key=='num'">{{index+1}}</span>
<span v-else class="lnil lnil-close"
@click="onDetailDelete(index)">{{ value }}</span>
</div>
</template>
</ComVFlexTable>
<div class="mt-2">
<VButton
color="primary"
icon="fas fa-plus"
@click="addNewDetailRow"
>
추가
</VButton>
</div>
</template>
<template #action>
<VButton color="primary" @click="changeButton">
등록
</VButton>
</template>
</VModal>
</td>
<td>등록기간</td>
<td colspan="5">
<div class="columns">
<div class="column is-5">
<VDatePicker
v-model="generalParams.regSdat"
color="green"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="시작일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
<div style="transform: translateY(15px)">~</div>
<div class="column is-5">
<VDatePicker
v-model="generalParams.regEdat"
color="green"
trim-weeks
disabledDates=""
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="종료일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</td>
</tr>
<tr>
<td>첨부파일</td>
<td colspan="3">
<VField class="file has-name is-right">
<div class="file-label">
<input
class="file-input"
type="file"
name="resume">
<span class="file-cta">
<span class="file-icon">
<i class="fas fa-cloud-upload-alt"/>
</span>
<span class="file-label">첨부파일</span>
</span>
<span class="file-name light-text">2022.xls</span>
</div>
</VField>
</td>
</tr>
<tr>
<td>견적요청</td>
<td colspan="6">
<VButton
color="primary"
icon="fas fa-plus"
elevated
@click="showTable = !showTable"
>
견적사 입력
</VButton>
<div v-if="showTable" class="mt-2">
<ComVFlexTable
:key="params.prcsBizs.length"
:data="params.prcsBizs"
:columns="params.prcsBizsColumn"
:compact="true"
:separators="true"
:clickable="true"
>
<template #body-cell="{ row, column, index, value }">
<div>
<!-- 다른 editable 컬럼은 input -->
<input
v-if="column.editable"
v-model="row[column.key]"
class="editable-input"
/>
<span v-else-if="column.key=='num'">{{index + 1}}</span>
<!-- readonly 출력 -->
<span v-else class="lnil lnil-close"
@click="onDelete(index)">{{ value }}</span>
</div>
</template>
</ComVFlexTable>
<div class="mt-2">
<VButton
color="primary"
icon="fas fa-plus"
@click="addNewEstimateRow"
>
추가
</VButton>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="column is-12">
<VUser v-model="apprLine"/>
</div>
<div class="column is-12">
<VField class="pr-2">
<VLabel class="has-fullwidth">
결재선
</VLabel>
</VField>
<ComVFlexTable
:data="apprLine"
:columns="params.felxColumn"
:separators="true"
:clickable="true"
:compact="true">
<template #body-cell="{ row, column, index, value }">
<!-- : 특정 컬럼이면 input, 아니면 그냥 출력 -->
<div>
<span v-if="column.key=='gubunCd'" class="column">
<VField class="pr-1">
<VCodeSelect
cd_grp=7
v-model="row.gubunCd"/>
</VField>
</span>
<span v-else-if="column.key=='attendCd'" class="column">
<VField class="pr-1">
<VCodeSelect
cd_grp=6
v-model="row.attendCd"/>
</VField>
</span>
<span v-else
@click="onDetailDelete(index)">{{value}}</span>
</div>
</template>
</ComVFlexTable>
</div>
<VButton
color="primary"
@click.stop="updatePriceOne"
raised>
수정
</VButton>
<VButton
to="/app/priceManagement"
v-if="params.stCd === '0000'"
color="info"
>
닫기
</VButton>
</div>
</div>
</template>
<style scoped lang="scss">
.table tbody td {
color: var(--smoke-white);
}
.datatable-table {
padding: 12px 12px;
td:nth-child(1) {
background-color: var(--primary);
}
td:nth-child(3) {
background-color: var(--primary);
}
}
</style>