세모튜브

小程序 앱만들기 - 6 : 코드관리 페이지 만들기 - <picker>컴포넌트 사용 본문

미니프로그램-小程序/위챗-관광정보 앱

小程序 앱만들기 - 6 : 코드관리 페이지 만들기 - <picker>컴포넌트 사용

iDevKim 2020. 5. 18. 21:34

유튜브강좌 : youtu.be/cmjkpETaLmw

 

 

우선 관광정보API 가이드 파일내에 한국관광공사_서비스분류코드_vx.x.xlsx파일을 참조한다.

참조) 한국관광공사_서비스분류코드_v3.2_160623.xlsx

 

코드종류

- 관광타입 코드 : 전체, 관광지, 문화시설, 행사/공연/축제, 여행코스, 레포츠, 숙박, 쇼핑, 음식점

- 분류 코드 : 대분류, 중분류, 소분류로 나누어진다.

- 지역 코드 : 지역, 시군구로 나누어 진다.

- 정렬 코드 : 제목순, 인기순, 최근수정순, 등록순, 제목순(이미지), 인기순(이미지), 최근수정순(이미지), 등록순(이미지)

 

관광타입 코드와 정렬코드는 메뉴얼상에 제공되고 있으며...

분류코드와 지역코드는 각각 categoryCode 오퍼레이션과 areaCode 오퍼레이션을 이용해 서버에서 제공받을수 있다.

활용메뉴얼(국문) "서비스 분류코드 조회", "지역코드 조회" 참조

 

처리방식 

모든 코드를 읽어들인후 storage()에 보관을 할것이며....

관광타입 코드와 정렬코드는 간단한 1차배열이며 내용도 적어 코드상에 처리하며

분류코드와 지역코드는 storage()로 저장한다. 

각각 스토레이지명은 "areaCodeArray", "categoryCodeArray"으로 한다.

define개념으로 처리하게 위해 utils폴더안에 def.js파일을 생성후 처리하자.

// def.js
const AreaCode = "areaCodeArray";
const CategoryCode = "categoryCodeArray";

module.exports = {
  AreaCode,
  CategoryCode,
}

 

 

페이지 구성

코드별 Picker처리

- 타입 코드용 주요 변수 : contentIndex: 0, contentArray: []

- 분류 코드용 주요 변수  : categoryIndex: [0, 0, 0], categoryArray: [[], [], []]

    - 대분류 : categoryIndex[0], categoryArray[0]

    - 중분류 : categoryIndex[1], categoryArray[1] 

    - 소분류 : categoryIndex[2], categoryArray[2]

- 지역 코드용 주요 변수 : areaIndex: [0,0], areaArray: [[],[]]

    - 지역 : areaIndex[0], areaArray[0]

    - 시군구 : areaIndex[1], areaArray[1] 

- 정렬 코드용 주요 변수 : arrangeIndex: 0, arrangeArray: []

 

변수 정의

// pages/user/code/code.js
const api = require("../../../utils/api.js");
const util = require("../../../utils/util.js");
const def = require("../../../utils/def.js");

Page({
  data: {
    contentIndex: 0,
    categoryIndex: [0, 0, 0],
    areaIndex: [0, 0],
    arrangeIndex: 0,
    contentArray: [],//콘텐츠타입(ContentTypeId) 코드표
    categoryArray: [[],[],[]],//분류코드
    areaArray: [[],[]],//지역코드,시군구코드
    arrangeArray: [],//정렬코드
  },

레이아웃 정의

<!--pages/user/code/code.wxml-->
<cu-custom bgImage="http://tong.visitkorea.or.kr/cms/resource/72/1536672_image2_1.jpg" isBack="{{true}}">
  <view slot="backText">뒤로</view>
  <view slot="content">코드관리</view>
</cu-custom>

<view class="fixedPage">
<!-- 상단 텍스트 처리 -->
   <view class="cu-list menu card-menu margin-top">
    <view class="cu-item">
      <view class="content padding-tb-sm">
        <view>
          <text class="cuIcon-pulldown text-blue margin-right-xs"></text> 코드테이블 업데이트</view>
        <view class="text-gray text-sm text-content margin-right-xs">
          <text class="cuIcon-infofill margin-right-xs"></text> 서버의 코드테이블을 업데이트 합니다.</view>
        <view class="text-gray text-sm text-content margin-right-xs">
          <text class="cuIcon-infofill margin-right-xs"></text> 타입코드와 정렬코드는 코드상에 처리합니다.</view>
        <view class="text-gray text-sm text-content margin-right-xs">
          <text class="cuIcon-infofill margin-right-xs"></text> 분류코드와 지역코드는 서버에서 다운로드하여 처리합니다.</view>
      </view>
    </view>
  </view>
<!-- Picker 처리 -->
  <view class="cu-list menu card-menu margin-top-xl margin-bottom-xl shadow-lg">
    <form>
<!-- 타입 Picker -->
      <view class="cu-form-group">
        <view class="title">타입</view>
        <picker bindchange="contentChange" value="{{contentIndex}}" range-key="name" range="{{contentArray}}">
          <view class="picker">
          {{contentArray[contentIndex].name}}
          </view>
        </picker>
        <button class="cu-btn line-blue margin-left sm" bindtap="contentUpdate" disabled>업데이트</button>
      </view>
<!-- 분류 Picker -->      
      <view class="cu-form-group">
        <view class="title">분류</view>
        <picker mode="multiSelector" bindcolumnchange="categoryColumnChange" value="{{categoryIndex}}" range-key="name" range="{{categoryArray}}">
          <view class="picker">
            <block wx:if="{{categoryArray[0].length}}">
              {{categoryArray[0][categoryIndex[0]].name}},
              {{categoryArray[1][categoryIndex[1]].name}},
              {{categoryArray[2][categoryIndex[2]].name}}
            </block>
            <block wx:else>
              <text class="text-red">업데이트가 필요합니다.</text>
            </block>
          </view>
        </picker>
        <button class="cu-btn bg-blue margin-left sm" bindtap="categoryUpdate">업데이트</button>
      </view>
<!-- 지역 Picker -->      
      <view class="cu-form-group">
        <view class="title">지역</view>
        <picker mode="multiSelector" bindcolumnchange="areaColumnChange" value="{{areaIndex}}" range-key="name" range="{{areaArray}}">
          <view class="picker">
            <block wx:if="{{areaArray[0].length}}">
              {{areaArray[0][areaIndex[0]].name}},
              {{areaArray[1][areaIndex[1]].name}}
            </block>
            <block wx:else>
              <text class="text-red">업데이트가 필요합니다.</text>
            </block>
          </view>
        </picker>
        <button class="cu-btn bg-blue margin-left sm" bindtap="areaUpdate">업데이트</button>
      </view>
<!-- 정렬 Picker -->      
      <view class="cu-form-group">
        <view class="title">정렬</view>
        <picker bindchange="arrangeChange" value="{{arrangeIndex}}" range-key="name" range="{{arrangeArray}}">
          <view class="picker">
          {{arrangeArray[arrangeIndex].name}}
          </view>
        </picker>
        <button class="cu-btn line-blue margin-left sm" bindtap="arrangeUpdate" disabled>업데이트</button>
      </view>
    </form>
  </view>
</view>

 

페이지전체를 움직이지 않게 고정하기 위해 class="fixedPage" 사용.

/* pages/user/code/code.wxss */

.fixedPage {
  position: fixed;
  height: 100vh;
  width: 100vw;
  display: flex;
  flex-direction: column;
}

 

실행

코드 데이타는 아직 처리되지않았다.

* 아래와 같이 레이아웃이 발생될때

app.json의 

"style": "v2", => 삭제

 

- 스타일 설명 참조 

developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#style

WeChat 클라이언트 7.0부터 UI 인터페이스가 크게 수정되었습니다. 애플릿도 기본 구성 요소로 업그레이드되었습니다. 

app.json configure "style": "v2"는 새로운 스타일의 컴포넌트 사용을 표시 할 수 있습니다.

이 변경과 관련된 구성 요소는 button icon radio checkbox switch slider다음  같습니다. 

샘플 애플릿으로 이동하여 경험할 수 있습니다

 

 

코드 업데이트 처리하기

관광타입 코드와 정렬코드는 간단한 1차배열이면 내용도 적어 코드상에서 처리한다.

코드 처리를 위해 utils/code.js파일을 생성하여 관광타입 코드와 정렬코드 데이터를 정의하자.

// code.js
const def = require("./def.js")

let contentArray = [
  { code: "", name: "관광타입 전체" },
  { code: "12", name: "관광지" },
  { code: "14", name: "문화시설" },
  { code: "15", name: "행사/공연/축제" },
  { code: "25", name: "여행코스" },
  { code: "28", name: "레포츠" },
  { code: "32", name: "숙박" },
  { code: "38", name: "쇼핑" },
  { code: "39", name: "음식점" }
];
let arrangeArray= [
  {code : "A", name: "제목순"},
  {code : "B", name: "인기순"},
  {code : "C", name: "최근수정순"},
  {code : "D", name: "등록순"},
  {code : "O", name: "제목순(이미지)"},
  {code : "P", name: "인기순(이미지)"},
  {code : "Q", name: "최근수정순(이미지)"},
  {code : "R", name: "등록순(이미지)"},
];

// init
////////////////////////////////////////////////////////////////////////////////////
function init() {
  return [contentArray, arrangeArray];
}

 

분류코드와 지역코드는 서버와 request로 처리해야 한다.

우선, request시 하나씩 counter 처리하기 위해 변수를 설정하자 : 현재 마지막시점을 첵크하기 위해 사용.

    tot: 0, //전체갯수 누적
    cnt: 0, //성공갯수 누적

 

코드 업데이트(request)

업데이트 버튼을 클릭하면 아래 순서로 처리하도록 한다.

=> 초기화

      => storage 삭제

      => 변수, 배열 초기화

=> api.request() 호출

      => item.forEach() 반복      예) reqCat1() : 서비스분류 - 대분류

            => item.forEach() 반복      예) reqCat2() : 서비스분류 - 중분류

                  => item.forEach() 반복      예) reqCat3() : 서비스분류 - 소분류

// 분류코드 업데이트
//////////////////////////////////////////////////////////////////////////////
  categoryUpdate() {
    try {
      wx.removeStorageSync(def.CategoryCode);
    } catch (e) {
      // Do something when catch error
    }
    this.data.cnt = 0;
    this.data.tot = 0;
    this.data.categoryArray = [];
    this.data.contentArray.forEach((item, key) => {
      this.reqCat1(key, item.code);
    });
  },
//서비스분류  - 대분류
  reqCat1(contentKey, contentTypeId) {
    let that = this;
    api.request(api.CategoryCode, {
      contentTypeId: contentTypeId,
      numOfRows: 100,
    }).then(function (res) {
      if (res.response.header.resultMsg === "OK") {
        let item = res.response.body.items.item;
        if (!Array.isArray(item)) item = [item];
        item.unshift({ code: "", name: "대분류 전체", rnum: 0 });//배열의처음에추가
        that.data.contentArray[contentKey].cat1 = item;
        that.data.tot += item.length;
        item.forEach((item, key) => {
          that.reqCat2(contentKey, contentTypeId, key, item.code);
        });
      } else { 
        util.showToast("error reqCat1","error");
        console.error("reqCat1 : ", res.response.header.resultMsg) 
      }
    })
    .catch(function (res) { 
      util.showToast("error reqCat1","error");
      console.error("reqCat1 catch : ", res) }
    )
  },
//서비스분류 - 중분류
  reqCat2(contentKey, contentTypeId, cat1Key, cat1) {
    let that = this;
    api.request(api.CategoryCode, {
      contentTypeId: contentTypeId,
      cat1: cat1,
      numOfRows: 100,
    }).then(function (res) {
      if (res.response.header.resultMsg === "OK") {
        let item = res.response.body.items.item;
        if (!Array.isArray(item)) item = [item];
        item.unshift({ code: "", name: "중분류 전체", rnum: 0 });//배열의처음에추가
        //item 재정의
        if(cat1 == "") {
          item = [{ code: "", name: '중분류 전체', rnum: 0, cat3: [{ code: "", name: '소분류 전체', rnum: 0 }] }]; }
        that.data.contentArray[contentKey].cat1[cat1Key].cat2 = item;
        that.data.cnt++;
        that.data.tot += item.length;
        item.forEach((item, key) => {
          that.reqCat3(contentKey, contentTypeId, cat1Key, cat1, key, item.code);
        });
      } else { 
        util.showToast("error reqCat2","error");
        console.error("reqCat2 : ", res.response.header.resultMsg) 
      }
    })
    .catch(function (res) { 
      util.showToast("error reqCat2","error");
      console.error("reqCat2 catch : ", res) }
    )
  },
//서비스분류 - 소분류
  reqCat3(contentKey, contentTypeId, cat1Key, cat1, cat2Key, cat2) {
    let that = this;
    api.request(api.CategoryCode, {
      contentTypeId: contentTypeId,
      cat1: cat1,
      cat2: cat2,
      numOfRows: 100,
    }).then(function (res) {
      if (res.response.header.resultMsg === "OK") {
        let item = res.response.body.items.item;
        if (!Array.isArray(item)) item = [item];
        item.unshift({ code: "", name: "소분류 전체", rnum: 0 });//배열의처음에추가
        //item 재정의
        if(cat2 == "") {
          item = [{ code: "", name: '소분류 전체', rnum: 0 }]; }
        that.data.contentArray[contentKey].cat1[cat1Key].cat2[cat2Key].cat3 = item;
        that.data.cnt++;
        // console.log("[" + that.data.cnt + " / " + that.data.tot + "] => "+contentTypeId + " : " + cat1 + " : " + cat2 + " : cat3 : ", item);
        if(that.data.tot === that.data.cnt) {
          console.log("end ==========> tot : " + that.data.tot + " cnt : " + that.data.cnt);
          try {
            wx.setStorageSync(def.CategoryCode, that.data.contentArray);
          } catch (e) {
            // Do something when catch error
          }
          util.hideLoading();
          wx.showModal({
            title: '분류코드 업데이트 완료',
            confirmColor: '#b4282d',
            content: '업데이트 중 에러발생시 다시 업데이트 해주십시요.',
            showCancel: false,
            confirmText: "확인",
            complete: function () {
              that.setData({
                categoryArray: code.init()[1],
              })
            }
          })
        } 
      } else { 
        util.showToast("error reqCat3","error");
        console.error("reqCat3 : ", res.response.header.resultMsg)
      }
    })
    .catch(function (res) {
      util.showToast("error reqCat3","error");
      console.error("reqCat3 catch : ", res)
    })
  },
// 지역코드 업데이트
//////////////////////////////////////////////////////////////////////////////
  areaUpdate() {
    try {
      wx.removeStorageSync(def.AreaCode);
    } catch (e) {
      // Do something when catch error
    }
    this.data.cnt = 0;
    this.data.tot = 0;
    this.data.areaArray = [];
    this.reqAreaCode();
  },
//지역코드
  reqAreaCode() {
    let that = this;
    api.request(api.AreaCode, {
      numOfRows: 100,
    }).then(function (res) {
      if (res.response.header.resultMsg === "OK") {
        let item = res.response.body.items.item;
        if (!Array.isArray(item)) item = [item];
        item.unshift({ code: "", name: '지역 전체', rnum: 0, sigunguCodeArray: [{ code: "", name: '시군구 전체', rnum: 0 }] });//배열의처음에추가
        that.data.areaArray = item;
        that.data.tot += item.length;
        item.forEach((item, key) => {
          that.reqSigunguCode(key, item.code);
        });
      } else { 
        util.showToast("error getAreaCode","error");
        console.error("getAreaCode : ", res.response.header.resultMsg) 
      }
    })
    .catch(function (err) { 
      util.showToast("error getAreaCode","error");
      console.error("getAreaCode catch : ", err) }
    )
  },
//시군구코드
  reqSigunguCode(key, areaCode) {
    let that = this;
    api.request(api.AreaCode, {
      areaCode: areaCode,
      numOfRows: 100,
    }).then(function (res) {
      if (res.response.header.resultMsg === "OK") {
        let item = res.response.body.items.item;
        if (!Array.isArray(item)) item = [item];
        item.unshift({ code: "", name: '시군구 전체', rnum: 0 });//배열의처음에추가
        //item 재정의
        if(areaCode == "") {
          item = [{ code: "", name: '시군구 전체', rnum: 0 }]; }
        that.data.areaArray[key].sigunguCodeArray = item;
        that.data.cnt++;
        if(that.data.tot === that.data.cnt) {
          console.log("end ==========> tot : " + that.data.tot + " cnt : " + that.data.cnt);
          try {
            wx.setStorageSync(def.AreaCode, that.data.areaArray);
          } catch (e) {
            // Do something when catch error
          }
          util.hideLoading();
          wx.showModal({
            title: '지역코드 업데이트 완료',
            confirmColor: '#b4282d',
            content: '업데이트 중 에러발생시 다시 업데이트 해주십시요.',
            showCancel: false,
            confirmText: "확인",
            complete: function () {
              that.setData({
                areaArray: code.init()[2],
              })
            }
          })
        }        
      } else { 
        util.showToast("error reqSigunguCode","error");
        console.error("reqSigunguCode : ", res.response.header.resultMsg) 
      }
    })
    .catch(function (err) {
      util.showToast("error reqSigunguCode","error");
      console.error("reqSigunguCode catch : ", err)
    })
  },

 

 

 

코드 업데이트(request) 실행

분류코드 업데이트

지역코드 업데이트

 

업데이트이후 아래와 같이 스토레이지에 코드테이블이 생성되었음을 알수 있다.

 

utils/code.js에서 코드처리 하기

- init()를 통해 기본 코드값 contentArray, categoryArray, areaArray, arrangeArray 처리하기

- getStorageSync()를 이용해서 저장된 코드테이블값을 읽은후 처리한다.

 

let contentArray = [
  { code: "", name: "관광타입 전체" },
  { code: "12", name: "관광지" },
  { code: "14", name: "문화시설" },
  { code: "15", name: "행사/공연/축제" },
  { code: "25", name: "여행코스" },
  { code: "28", name: "레포츠" },
  { code: "32", name: "숙박" },
  { code: "38", name: "쇼핑" },
  { code: "39", name: "음식점" }
];
let arrangeArray= [
  {code : "A", name: "제목순"},
  {code : "B", name: "인기순"},
  {code : "C", name: "최근수정순"},
  {code : "D", name: "등록순"},
  {code : "O", name: "제목순(이미지)"},
  {code : "P", name: "인기순(이미지)"},
  {code : "Q", name: "최근수정순(이미지)"},
  {code : "R", name: "등록순(이미지)"},
];

// init
////////////////////////////////////////////////////////////////////////////////////
function init() {
  let categoryArray = initCategoryArray(0);//contentId = 0으로 초기값 설정.
  let areaArray = initAreaArray();
  return [contentArray, categoryArray, areaArray, arrangeArray];
}

function initCategoryArray( contentIndex ) {
  let getArray= wx.getStorageSync(def.CategoryCode);
  let resArray= [[],[],[]];
  if( getArray  ) {
    getArray[contentIndex].cat1.forEach((item, key) => {
      resArray[0].push({ code: item.code, name: item.name } );
    })
    getArray[contentIndex].cat1[0].cat2.forEach((item, key) => {
      resArray[1].push({ code: item.code, name: item.name });
    })
    getArray[contentIndex].cat1[0].cat2[0].cat3.forEach((item, key) => {
      resArray[2].push({ code: item.code, name: item.name });
    })
  }
  return resArray;
}

function initAreaArray() {
  let getArray= wx.getStorageSync(def.AreaCode);
  let resArray= [[],[]];
  if( getArray  ) {
    getArray.forEach((item, key) => {
      resArray[0].push({ code: item.code, name: item.name });
    })
    getArray[0].sigunguCodeArray.forEach((item, key) => {
      resArray[1].push({ code: item.code, name: item.name });
    })
  }
  return resArray;
}

module.exports = {
  init,
}

 

user/code/code.js에서 페이지 로드시 코드 데이터 가져오기

페이지 상단에 code 정의

const code = require("../../../utils/code.js");
  onLoad: function () { 
    let res = code.init();
    this.setData({
      contentArray: res[0],
      categoryArray: res[1],
      areaArray: res[2],
      arrangeArray: res[3]
    })
  },

실행화면

 

 

코드 picker change 처리

코드별 picker change

타입 : bindchange="contentChange"

      => 타입코드변경에 의한 분류코드 재설정 : code.initCategoryArray()

 

분류 : bindcolumnchange="categoryColumnChange"

      => mode="multiSelector" : code.categoryColumnChange()

지역 : bindcolumnchange="areaColumnChange"

      => mode="multiSelector" : code.areaColumnChange()

정렬 : bindchange="arrangeChange"

user/code/code.js에서 change 처리

// picker Change 처리
////////////////////////////////////////////////////////////////////////////////
  contentChange(e) {
    let contentId = e.detail.value;
    this.setData({
      contentIndex: contentId,
      categoryIndex: [0, 0, 0],
      categoryArray: code.initCategoryArray(contentId),//contentId변경으로 인한 재설정.
    })
  },
  categoryColumnChange(e) {
    let contentIndex = this.data.contentIndex;
    let categoryIndex = this.data.categoryIndex;
    let categoryArray = this.data.categoryArray;
    let res = code.categoryColumnChange(contentIndex, categoryIndex, categoryArray, e);
    this.setData({
      categoryIndex: res[0],
      categoryArray: res[1],
    })
  },
  areaColumnChange(e) {
    let areaIndex = this.data.areaIndex;
    let areaArray = this.data.areaArray;
    let res = code.areaColumnChange(areaIndex, areaArray, e);
    this.setData({
      areaIndex: res[0],
      areaArray: res[1],
    })
  },  
  arrangeChange(e) {
    let contentId = e.currentTarget.dataset.id;
    this.setData({
      arrangeIndex: contentId,
    })
  },

 

utils/code.js에서 ColumnChange picker 처리

// ColumnChange Picker
////////////////////////////////////////////////////////////////////////////////////
function categoryColumnChange(contentIndex, categoryIndex, categoryArray, e) {
  categoryIndex[e.detail.column] = e.detail.value;
  switch (e.detail.column) {
    case 0:
      categoryArray[1] = [];
      wx.getStorageSync(def.CategoryCode)[contentIndex].cat1[categoryIndex[0]].cat2.forEach((item, key) => {
        categoryArray[1].push({ code: item.code, name: item.name });
      })
      categoryArray[2] = [];
      wx.getStorageSync(def.CategoryCode)[contentIndex].cat1[categoryIndex[0]].cat2[0].cat3.forEach((item, key) => {
        categoryArray[2].push({ code: item.code, name: item.name });
      })
      categoryIndex[1] = 0;
      categoryIndex[2] = 0;
      break;
    case 1: 
      categoryArray[2] = [];
      wx.getStorageSync(def.CategoryCode)[contentIndex].cat1[categoryIndex[0]].cat2[categoryIndex[1]].cat3.forEach((item, key) => {
        categoryArray[2].push({ code: item.code, name: item.name });
      })
      categoryIndex[2] = 0;
      break;
  }
  return [categoryIndex, categoryArray];
}

function areaColumnChange(areaIndex, areaArray, e) {
  areaIndex[e.detail.column] = e.detail.value;
  switch (e.detail.column) {
    case 0:
      areaArray[1] = [];
      wx.getStorageSync(def.AreaCode)[e.detail.value].sigunguCodeArray.forEach((item, key) => {
        areaArray[1].push({ code: item.code, name: item.name });
      })
      areaIndex[1] = 0;
      break;
  }
  return [areaIndex, areaArray];
}

module.exports = {
  init,
  initCategoryArray,
  categoryColumnChange,
  areaColumnChange,
}

 

실행화면

 

이상으로 코드 관리를 위한 페이지를 마무리하겠습니다.