본문 바로가기
Programming/Vue

[실습] 뷰(Vue)로 회원/사용자 관리 화면 구현하기 - UI/Layout 구현하기

by 돌방로그 2023. 2. 17.


오늘의 실습 목표는 "회원 관리 기능 만들어보기!" 입니다.
본 게시글에서 다루는 사항은 회원 관리 화면의 UI/Layout을 구현하는 과정입니다.

 


회원/사용자 화면 구현하기 - UI/Layout

사전 준비

아래 사항에 대해서 사전 준비가 완료되지 않으신 분들은 아래 링크를 참조하여 사전 준비를 진행합니다.

 


결과 이미지

구현하기에 앞서 본 글을 따라하시면 아래와 같이 화면의 레이아웃을 구성하실 수 있습니다.

 

▶ 메인 화면

좌: Read Available Users 모드 우: Read All 모드

 

▶ 팝업 다이얼로그 - 등록/수정

 

 

▶ 팝업 다이얼로그 - 삭제

 


소스 코드

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>

 

 


댓글