Programming/Vue
[실습] 뷰(Vue)로 회원/사용자 관리 화면 구현하기 - UI/Layout 구현하기
돌방
2023. 2. 17. 08:33
오늘의 실습 목표는 "회원 관리 기능 만들어보기!" 입니다.
본 게시글에서 다루는 사항은 회원 관리 화면의 UI/Layout을 구현하는 과정입니다.
회원/사용자 화면 구현하기 - UI/Layout
사전 준비
아래 사항에 대해서 사전 준비가 완료되지 않으신 분들은 아래 링크를 참조하여 사전 준비를 진행합니다.
- [실습] 뷰(Vue.js)로 관리자/직원 전용 Web Application 개발하기 - 프론트엔드(Font-end) 환경 구축하기
- [실습] 뷰(Vue)로 회원/사용자 관리 화면 구현하기 - 기획/설계하기
결과 이미지
구현하기에 앞서 본 글을 따라하시면 아래와 같이 화면의 레이아웃을 구성하실 수 있습니다.
▶ 메인 화면
▶ 팝업 다이얼로그 - 등록/수정
▶ 팝업 다이얼로그 - 삭제
소스 코드
src\components\admin-web\users\AdminUsersMain.vue
관리자 포털의 사용자 관리 화면의 주요 기능이 담겨있는 컴포넌트 파일입니다.
전체 코드를 확인하실 분들은 아래 더보기를 클릭해주세요.
더보기
<template>
<v-container>
<div class="text-h5 font-weight-medium ma-2">Users (Admin Web)</div>
<v-divider></v-divider>
<v-row>
<v-col class="align-self-center" :cols="4">
<div>
<v-switch
inset
color="indigo darken-3"
v-model="readAll"
:label="`${readAll ? 'All' : 'Available'}`"
></v-switch>
</div>
</v-col>
<v-col class="align-self-center" :cols="8">
<div class="d-flex justify-end">
<v-dialog persistent max-width="1000px" v-model="dialogRegister">
<template v-slot:activator="{ on, attrs }">
<v-btn
text
outlined
class="mx-md-1 elevation-2"
v-bind="attrs"
v-on="on"
>REGISTER
</v-btn>
</template>
<v-card>
<Form v-on:finishProcess="finishProcess"></Form>
</v-card>
</v-dialog>
<v-dialog persistent max-width="1000px" v-model="dialogModify">
<template v-slot:activator="{ on, attrs }">
<v-btn
text
outlined
class="mx-md-1 elevation-2"
v-bind="attrs"
v-on="on"
:disabled="!hasSelectedRow()"
>MODIFY
</v-btn>
</template>
<v-card>
<Form
:employee="this.selectedRow[0]"
v-on:finishProcess="finishProcess"
></Form>
</v-card>
</v-dialog>
<v-dialog v-model="dialogDelete" persistent max-width="300">
<template v-slot:activator="{ on, attrs }">
<v-btn
text
outlined
class="mx-md-1 elevation-2"
:disabled="!checkDeleteStatus()"
v-bind="attrs"
v-on="on"
>DELETE
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5">
Are you sure you want to delete the user?
</v-card-title>
<v-card-text
>Deleted notices cannot be restored, and are immediately
reflected on the user portal after deletion.</v-card-text
>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text @click="dialogDelete = false">
Cancel
</v-btn>
<v-btn color="blue darken-1" text @click="deleteUser">
Confirm
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</v-col>
</v-row>
<div>
<v-card-title>
Admin Users
<v-spacer></v-spacer>
<v-text-field
single-line
hide-details
v-model="search"
append-icon="mdi-magnify"
label="Search"
></v-text-field>
</v-card-title>
<v-data-table
single-select
show-select
item-key="employeeNo"
show-expand
class="mt-md-2 elevation-5"
v-model="selectedRow"
:search="search"
:headers="headers"
:items="users"
:items-per-page="10"
>
<template v-slot:expanded-item="{ headers, item }">
<td class="pa-5" :colspan="headers.length">
{{ "- Division: " + item.division }} <br />{{
"- Team: " + item.team
}}
<br />{{ "- Name (Eng): " + item.nameEng }} <br />{{
"- Email: " + item.email
}}
<br />{{ "- Phone: " + item.phone }}
</td>
</template>
</v-data-table>
</div>
</v-container>
</template>
<script>
import Form from "./../../../components/admin-web/users/AdminUsersDetail.vue";
export default {
components: {
Form,
},
data() {
return {
today: new Date(Date.now() - new Date().getTimezoneOffset() * 60000)
.toISOString()
.substring(0, 10)
.replaceAll("-", ""),
readAll: false,
dialogRegister: false,
dialogModify: false,
dialogDelete: false,
selectedRow: [],
search: "",
headers: [
{
text: "Company",
value: "company",
},
{
text: "Employee No.",
value: "employeeNo",
},
{
text: "Name (Kor)",
value: "nameKor",
},
{
text: "Position",
value: "position",
},
],
users: [],
};
},
methods: {
hasSelectedRow: function () {
return this.selectedRow.length > 0;
},
checkDeleteStatus: function () {
let isAvailable = false;
if (this.hasSelectedRow() === true) {
isAvailable = this.selectedRow[0].usageExpDate > this.today;
}
return isAvailable;
},
finishProcess: function () {
this.dialogRegister = false;
this.dialogModify = false;
window.location.reload();
},
deleteUser: function () {
this.dialogDelete = false;
},
},
};
</script>
<style scoped></style>
<template>
<template>
<v-container>
<div class="text-h5 font-weight-medium ma-2">Users (Admin Web)</div>
<v-divider></v-divider>
<v-row>
<v-col class="align-self-center" :cols="4">
<div>
<v-switch
inset
color="indigo darken-3"
v-model="readAll"
:label="`${readAll ? 'All' : 'Available'}`"
></v-switch>
</div>
</v-col>
<v-col class="align-self-center" :cols="8">
<div class="d-flex justify-end">
<v-dialog persistent max-width="1000px" v-model="dialogRegister">
<template v-slot:activator="{ on, attrs }">
<v-btn
text
outlined
class="mx-md-1 elevation-2"
v-bind="attrs"
v-on="on"
>REGISTER
</v-btn>
</template>
<v-card>
<Form v-on:finishProcess="finishProcess"></Form>
</v-card>
</v-dialog>
<v-dialog persistent max-width="1000px" v-model="dialogModify">
<template v-slot:activator="{ on, attrs }">
<v-btn
text
outlined
class="mx-md-1 elevation-2"
v-bind="attrs"
v-on="on"
:disabled="!hasSelectedRow()"
>MODIFY
</v-btn>
</template>
<v-card>
<Form
:employee="this.selectedRow[0]"
v-on:finishProcess="finishProcess"
></Form>
</v-card>
</v-dialog>
<v-dialog v-model="dialogDelete" persistent max-width="300">
<template v-slot:activator="{ on, attrs }">
<v-btn
text
outlined
class="mx-md-1 elevation-2"
:disabled="!checkDeleteStatus()"
v-bind="attrs"
v-on="on"
>DELETE
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5">
Are you sure you want to delete the user?
</v-card-title>
<v-card-text
>Deleted notices cannot be restored, and are immediately
reflected on the user portal after deletion.</v-card-text
>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text @click="dialogDelete = false">
Cancel
</v-btn>
<v-btn color="blue darken-1" text @click="deleteUser">
Confirm
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</v-col>
</v-row>
<div>
<v-card-title>
Admin Users
<v-spacer></v-spacer>
<v-text-field
single-line
hide-details
v-model="search"
append-icon="mdi-magnify"
label="Search"
></v-text-field>
</v-card-title>
<v-data-table
single-select
show-select
item-key="employeeNo"
show-expand
class="mt-md-2 elevation-5"
v-model="selectedRow"
:search="search"
:headers="headers"
:items="users"
:items-per-page="10"
>
<template v-slot:expanded-item="{ headers, item }">
<td class="pa-5" :colspan="headers.length">
{{ "- Division: " + item.division }} <br />{{
"- Team: " + item.team
}}
<br />{{ "- Name (Eng): " + item.nameEng }} <br />{{
"- Email: " + item.email
}}
<br />{{ "- Phone: " + item.phone }}
</td>
</template>
</v-data-table>
</div>
</v-container>
</template>
- 수직 구조로 크게 3개의 구역으로 나뉘어 표시되도록 <div> 태그로 분리
- 타이틀 영역
- 조작 영역: 사용자 등록, 등록된 사용자 수정 및 삭제 외에도 조회 모드(전체, 사용 가능) 변경 기능 제공
- 결과 표시 영역: 등록된 사용자가 표시되는 테이블
- 조작 영역
- 조회 모드(전체, 사용 가능): 등록된 사용자 전체 혹은 사용 가능한 사용자 조회 기능을 제공하기 위해 토글 버튼 제공
- 기본적으로 '사용 가능한 사용자'만 표시되도록 설정
- 등록/수정 버튼: 클릭 이벤트로 입력 필드가 있는 AdminUsersDetail.vue를 표시하는 다이얼로그 팝업
- 삭제 버튼: 삭제 여부를 확인하는 다이얼로그 팝업
- 조회 모드(전체, 사용 가능): 등록된 사용자 전체 혹은 사용 가능한 사용자 조회 기능을 제공하기 위해 토글 버튼 제공
- 결과 표시 영역
- 테이블 우측 상단에 'Search' 기능을 두어 테이블에서 필요한 정보만 추출
- 테이블의 컬럼에 표시되는 정보는 대표 정보만 추려서 표현
- 상세 정보는 각 행에 표시된 ▼ 버튼을 클릭하여 확인 가능
- 테이블 하단에 페이징 기능 제공
<script>
<script>
import Form from "./../../../components/admin-web/users/AdminUsersDetail.vue";
export default {
components: {
Form,
},
data() {
return {
today: new Date(Date.now() - new Date().getTimezoneOffset() * 60000)
.toISOString()
.substring(0, 10)
.replaceAll("-", ""),
readAll: false,
dialogRegister: false,
dialogModify: false,
dialogDelete: false,
selectedRow: [],
search: "",
headers: [
{
text: "Company",
value: "company",
},
{
text: "Employee No.",
value: "employeeNo",
},
{
text: "Name (Kor)",
value: "nameKor",
},
{
text: "Position",
value: "position",
},
],
users: [],
};
},
methods: {
hasSelectedRow: function () {
return this.selectedRow.length > 0;
},
checkDeleteStatus: function () {
let isAvailable = false;
if (this.hasSelectedRow() === true) {
isAvailable = this.selectedRow[0].usageExpDate > this.today;
}
return isAvailable;
},
finishProcess: function () {
this.dialogRegister = false;
this.dialogModify = false;
window.location.reload();
},
deleteUser: function () {
this.dialogDelete = false;
},
},
};
</script>
- components 영역
- 등록, 수정 버튼 클릭시 팝업되는 다이얼로그 포맷이 정의된 뷰 파일 추가
- data 영역
- today: 조작 당일자가 yyyyMMdd 형식으로 담긴 변수
- readAll: 조회 모드(전체 사용자, 사용가능 사용자) 정보가 담긴 변수
- dialogRegister: 등록 버튼 클릭시 팝업되는 다이얼로그의 활성 여부 정보가 담긴 변수
- dialogModify: 수정 버튼 클릭시 팝업되는 다이얼로그의 활성 여부 정보가 담긴 변수
- dialogDelete: 삭제 버튼 클릭시 팝업되는 다이얼로그의 활성 여부 정보가 담긴 변수
- selectedRow: 테이블에서 선택된 행 정보가 담긴 변수
- search: 테이블 상단에 위치한 검색 영역의 입력 필드의 값이 담긴 변수
- headers: 테이블의 헤더 정보가 담긴 변수
- users: 테이블의 데이터 정보가 담길 변수
- methods 영역
- hasSelectedRow(): 테이블에서 선택된 행이 있는지 확인하는 함수
- checkDeleteStatus(): 삭제 버튼이 Enable되는 조건을 판별하는 함수
- finishProcess(): 다이얼로그가 종료될 때 메인 화면에서 처리되어야 할 후처리가 정의된 함수
- deleteUser(): 삭제 다이얼로그에서 OK 버튼을 클릭했을 때 처리가 정의된 함수
src\components\admin-web\users\AdminUsersDetail.vue
등록, 수정 버튼 클릭시 팝업되는 다이얼로그가 정의되어 있는 컴포넌트 파일입니다.
전체 코드를 확인하실 분들은 아래 더보기를 클릭해주세요.
더보기
<template>
<v-container>
<div class="text-h5 font-weight-medium ma-2">
User Information (Admin Web)
</div>
<v-divider class="mb-5"></v-divider>
<div class="ma-md-2">
<v-row>
<v-col>
<v-combobox
outlined
dense
hide-selected
required
clearable
:disabled="isModifyMode()"
:items="companies"
:rules="[rules.required]"
v-model="company"
label="회사"
@change="changeCompany"
></v-combobox>
</v-col>
<v-col>
<v-text-field
text
outlined
dense
clearable
:disabled="isModifyMode()"
:rules="[rules.required, rules.employeeNo]"
v-model="employeeNo"
label="직원번호"
placeholder="000000"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
text
outlined
dense
clearable
:rules="[rules.required]"
v-model="nameKor"
label="국문이름"
placeholder="홍길동"
></v-text-field>
</v-col>
<v-col>
<v-text-field
text
outlined
dense
clearable
v-model="nameEng"
label="영문이름"
placeholder="Gildong Hong"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
text
outlined
dense
clearable
:rules="[rules.required, rules.phone]"
v-model="phone"
label="휴대폰 번호"
placeholder="010-####-####"
></v-text-field>
</v-col>
<v-col>
<v-text-field
text
outlined
dense
clearable
type="email"
:rules="[rules.required, rules.email]"
v-model="email"
label="이메일 주소"
placeholder="gildong-hong@gmail.com"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col v-show="!isModifyMode()">
<v-text-field
text
outlined
dense
clearable
:append-icon="showPw ? 'mdi-eye' : 'mdi-eye-off'"
:type="showPw ? 'text' : 'password'"
:rules="[rules.required, rules.password]"
v-model="password"
label="비밀번호"
@click:append="showPw = !showPw"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
text
outlined
dense
clearable
v-model="division"
label="부서"
></v-text-field>
</v-col>
<v-col>
<v-text-field
text
outlined
dense
clearable
v-model="team"
label="팀"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
text
outlined
dense
clearable
:rules="[rules.required]"
v-model="position"
label="직책"
></v-text-field>
</v-col>
<v-col>
<v-text-field
text
outlined
dense
clearable
type="date"
:rules="[rules.required]"
:min="minUsageExpiryDate"
v-model="usageExpiryDate"
label="사용기한일자"
></v-text-field>
</v-col>
</v-row>
</div>
<div class="d-flex justify-end">
<v-btn
text
outlined
dense
class="mx-md-1 elevation-2"
@click="clickCancel"
>Cancel
</v-btn>
<v-btn text outlined dense class="mx-md-1 elevation-2" @click="clickOk"
>OK
</v-btn>
</div>
</v-container>
</template>
<script>
const MODE_REGISTER = "REGISTER";
const MODE_MODIFY = "MODIFY";
export default {
props: ["employee", "mode"],
data() {
return {
rules: {
required: (value) => !!value || "Required.",
employeeNo: (value) =>
value.length == 6 || "직원번호는 6자리로 구성되어야 합니다.",
phone: (value) => {
const pattern = /^(\d{3})-(\d{4})-(\d{4})$/;
return pattern.test(value) || "휴대폰 번호 형식에 맞지 않습니다.";
},
email: (value) => {
const pattern =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return pattern.test(value) || "이메일 주소 형식에 맞지 않습니다.";
},
password: (value) => {
const pattern = /^(?=.*\d)(?=.*[a-z]).{8,}$/;
return (
pattern.test(value) ||
"영어 소문자, 숫자를 포함하여 8자리 이상으로 구성되어야 합니다."
);
},
},
minUsageExpiryDate: new Date(
Date.now() - new Date().getTimezoneOffset() * 60000
)
.toISOString()
.substring(0, 10),
companies: ["봄 회사", "여름 컴퍼니", "(주) 가을", "겨울 패밀리"],
showPw: false,
// V-MODEL
company: [],
employeeNo: "",
nameKor: "",
nameEng: "",
phone: "",
email: "",
password: "",
division: "",
team: "",
position: "",
usageExpiryDate: "9999-12-31",
};
},
mounted: function () {
this.$nextTick(function () {
if (this.mode === MODE_MODIFY) {
this.setData();
}
});
},
watch: {
employee: {
handler() {
this.setData();
},
},
},
methods: {
setData: function () {
this.company = this.employee.company;
this.employeeNo = this.employee.employeeNo;
this.nameKor = this.employee.nameKor;
this.nameEng = this.employee.nameEng;
this.phone = this.employee.phone;
this.email = this.employee.email;
this.division = this.employee.division;
this.team = this.employee.team;
this.position = this.employee.position;
},
changeCompany: function () {
switch (this.company) {
case "봄 회사": // 100000 ~ 199999
this.employeeNo = "1";
break;
case "여름 컴퍼니": // 300000 ~ 399999
this.employeeNo = "3";
break;
case "(주) 가을": // 500000 ~ 599999
this.employeeNo = "5";
break;
case "겨울 패밀리": // 700000 ~ 799999
this.employeeNo = "7";
break;
default:
// error
break;
}
},
isModifyMode: function () {
return this.mode === MODE_MODIFY;
},
clickOk: function () {
this.$emit("finishProcess");
},
clickCancel: function () {
this.$emit("finishProcess");
},
},
};
</script>
<style scoped></style>
<template>
<template>
<v-container>
<div class="text-h5 font-weight-medium ma-2">
User Information (Admin Web)
</div>
<v-divider class="mb-5"></v-divider>
<div class="ma-md-2">
<v-row>
<v-col>
<v-combobox
outlined
dense
hide-selected
required
clearable
:disabled="isModifyMode()"
:items="companies"
:rules="[rules.required]"
v-model="company"
label="회사"
@change="changeCompany"
></v-combobox>
</v-col>
<v-col>
<v-text-field
text
outlined
dense
clearable
:disabled="isModifyMode()"
:rules="[rules.required, rules.employeeNo]"
v-model="employeeNo"
label="직원번호"
placeholder="000000"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
text
outlined
dense
clearable
:rules="[rules.required]"
v-model="nameKor"
label="국문이름"
placeholder="홍길동"
></v-text-field>
</v-col>
<v-col>
<v-text-field
text
outlined
dense
clearable
v-model="nameEng"
label="영문이름"
placeholder="Gildong Hong"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
text
outlined
dense
clearable
:rules="[rules.required, rules.phone]"
v-model="phone"
label="휴대폰 번호"
placeholder="010-####-####"
></v-text-field>
</v-col>
<v-col>
<v-text-field
text
outlined
dense
clearable
type="email"
:rules="[rules.required, rules.email]"
v-model="email"
label="이메일 주소"
placeholder="gildong-hong@gmail.com"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col v-show="!isModifyMode()">
<v-text-field
text
outlined
dense
clearable
:append-icon="showPw ? 'mdi-eye' : 'mdi-eye-off'"
:type="showPw ? 'text' : 'password'"
:rules="[rules.required, rules.password]"
v-model="password"
label="비밀번호"
@click:append="showPw = !showPw"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
text
outlined
dense
clearable
v-model="division"
label="부서"
></v-text-field>
</v-col>
<v-col>
<v-text-field
text
outlined
dense
clearable
v-model="team"
label="팀"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field
text
outlined
dense
clearable
:rules="[rules.required]"
v-model="position"
label="직책"
></v-text-field>
</v-col>
<v-col>
<v-text-field
text
outlined
dense
clearable
type="date"
:rules="[rules.required]"
:min="minUsageExpiryDate"
v-model="usageExpiryDate"
label="사용기한일자"
></v-text-field>
</v-col>
</v-row>
</div>
<div class="d-flex justify-end">
<v-btn
text
outlined
dense
class="mx-md-1 elevation-2"
@click="clickCancel"
>Cancel
</v-btn>
<v-btn text outlined dense class="mx-md-1 elevation-2" @click="clickOk"
>OK
</v-btn>
</div>
</v-container>
</template>
- 입력 필드를 2열씩 한 행으로 이루어 수직 형태로 표현되도록 구현
- 입력 필드
- 대부분의 필드에 대해서 입력 힌트로 placeholder 및 rule을 지정
- 일부 필드에 한해서 수정 모드인 경우 Disable되도록 구현
- 비밀번호 필드: 텍스트 Show/Hide 상태를 선택할 수 있도록 구현
<script>
<script>
const MODE_REGISTER = "REGISTER";
const MODE_MODIFY = "MODIFY";
export default {
props: ["employee", "mode"],
data() {
return {
rules: {
required: (value) => !!value || "Required.",
employeeNo: (value) =>
value.length == 6 || "직원번호는 6자리로 구성되어야 합니다.",
phone: (value) => {
const pattern = /^(\d{3})-(\d{4})-(\d{4})$/;
return pattern.test(value) || "휴대폰 번호 형식에 맞지 않습니다.";
},
email: (value) => {
const pattern =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return pattern.test(value) || "이메일 주소 형식에 맞지 않습니다.";
},
password: (value) => {
const pattern = /^(?=.*\d)(?=.*[a-z]).{8,}$/;
return (
pattern.test(value) ||
"영어 소문자, 숫자를 포함하여 8자리 이상으로 구성되어야 합니다."
);
},
},
minUsageExpiryDate: new Date(
Date.now() - new Date().getTimezoneOffset() * 60000
)
.toISOString()
.substring(0, 10),
companies: ["봄 회사", "여름 컴퍼니", "(주) 가을", "겨울 패밀리"],
showPw: false,
// V-MODEL
company: [],
employeeNo: "",
nameKor: "",
nameEng: "",
phone: "",
email: "",
password: "",
division: "",
team: "",
position: "",
usageExpiryDate: "9999-12-31",
};
},
mounted: function () {
this.$nextTick(function () {
if (this.mode === MODE_MODIFY) {
this.setData();
}
});
},
watch: {
employee: {
handler() {
this.setData();
},
},
},
methods: {
setData: function () {
this.company = this.employee.company;
this.employeeNo = this.employee.employeeNo;
this.nameKor = this.employee.nameKor;
this.nameEng = this.employee.nameEng;
this.phone = this.employee.phone;
this.email = this.employee.email;
this.division = this.employee.division;
this.team = this.employee.team;
this.position = this.employee.position;
},
changeCompany: function () {
switch (this.company) {
case "봄 회사": // 100000 ~ 199999
this.employeeNo = "1";
break;
case "여름 컴퍼니": // 300000 ~ 399999
this.employeeNo = "3";
break;
case "(주) 가을": // 500000 ~ 599999
this.employeeNo = "5";
break;
case "겨울 패밀리": // 700000 ~ 799999
this.employeeNo = "7";
break;
default:
// error
break;
}
},
isModifyMode: function () {
return this.mode === MODE_MODIFY;
},
clickOk: function () {
this.$emit("finishProcess");
},
clickCancel: function () {
this.$emit("finishProcess");
},
},
};
</script>
- data 영역
- rules: 각 입력 필드의 입력 규칙을 지정한 변수
- minUsageExpiryDate: 사용기한일자의 최소 지정 가능 일자를 정의한 변수
- companies: 콤보 박스에 표시될 아이템이 정의된 변수
- 그 외: v-model로 정의된 입력 필드의 정보를 담는 변수
- mounted
- 상위 컴포넌트에서 전달받은 데이터를 입력 필드에 적용
- watch
- 상위 컴포넌트에서 전달받은 employee 정보가 기존에 정보와 다른 것을 감지하기 위함
- methods 영역
- setData(): 데이터를 입력 필드에 적용하는 함수
- changeCompany(): 회사가 변경될 때마다 자동으로 사번 Prefix가 지정되도록 설정하는 함수
- clickOk(): Ok 버튼 클릭시 발생한 이벤트 처리 함수
- clickCancel(): Cancel 버튼 클릭시 발생한 이벤트 처리 함수
src\views\admin-web\users\BaseAdminUsers.vue
메뉴/사이드바의 라우팅 결과로 표시될 최상위 컴포넌트 파일입니다.
별도의 이벤트나 속성 전달(props)은 없으며, 관리자 포털의 사용자 관리 화면만 표시되도록 구현하였습니다.
<template>
<Main></Main>
</template>
<script>
import Main from "./../../../components/admin-web/users/AdminUsersMain.vue";
export default {
components: {
Main,
},
};
</script>
<style scoped></style>