myInfoUpdate.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. <template>
  2. <!-- 내 정보 수정 -->
  3. <v-dialog v-model="props.isMyInfoUpdateModal" persistent width="50rem">
  4. <div class="v-common-dialog-wrapper custom-dialog">
  5. <div class="modal-tit">
  6. <strong>{{$t('common.header.myInfoUpdateModal.title')}}</strong>
  7. <button class="btn-close" @click="fnMyInfoClose()"></button>
  8. </div>
  9. <div class="v-common-dialog-content">
  10. <div class="form-style1 col4 shadow--type">
  11. <table>
  12. <colgroup>
  13. <col style="width:9.375rem;">
  14. <col style="width: 9.5rem">
  15. <col style="width:7.375rem;">
  16. <col style="width: calc(50% - 0.375rem)">
  17. </colgroup>
  18. <tbody>
  19. <tr>
  20. <th>{{$t('common.header.myInfoUpdateModal.id')}}</th>
  21. <td>{{ myInfo.id }}</td>
  22. <th>{{$t('common.header.myInfoUpdateModal.name')}}</th>
  23. <td>{{ myInfo.name }}</td>
  24. </tr>
  25. <tr>
  26. <th>{{$t('common.header.myInfoUpdateModal.tenantName')}}</th>
  27. <td>{{ myInfo.tenantName }}</td>
  28. <th>{{$t('common.header.myInfoUpdateModal.priority')}}</th>
  29. <td>{{ myInfo.accountRole }}</td>
  30. </tr>
  31. <tr>
  32. <th>{{$t('common.header.myInfoUpdateModal.alarmMsgType')}}</th>
  33. <td>{{ myInfo.emailRecvYN === 'Y' ? $t('common.header.myInfoUpdateModal.alarmEmail') : '' }}{{ myInfo.smsRecvYN === 'Y' ? ' / '+$t('common.header.myInfoUpdateModal.alarmMobile') : '' }}</td>
  34. <th>{{$t('common.header.myInfoUpdateModal.alarmMsgTime')}}</th>
  35. <td>{{ myInfo.recvSdate }} ~ {{ myInfo.recvEdate }}</td>
  36. </tr>
  37. <tr v-if="!errorCheck.pwdChg">
  38. <th>{{$t('common.header.myInfoUpdateModal.password')}}</th>
  39. <td>
  40. <v-btn class="custom-btn btn-password" style="width: 7.875rem;" @click="errorCheck.pwdChg = true">{{$t('common.header.myInfoUpdateModal.passwordChange')}}</v-btn>
  41. </td>
  42. </tr>
  43. <tr v-if="errorCheck.pwdChg">
  44. <th>{{$t('common.header.myInfoUpdateModal.newPassword')}}<span class="cir"></span></th>
  45. <td colspan="3">
  46. <div class="txt-field-box" :class="errorCheck.newPassword1Error ? 'error' : ''">
  47. <v-text-field type="password" :placeholder="$t('common.header.myInfoUpdateModal.newPasswordDesc')" class="custom-input mini2" v-model="myInfo.newPassword1" style="width:100%;" @input="setInputField('newPassword1')"></v-text-field>
  48. <i class="ico"></i>
  49. </div>
  50. </td>
  51. </tr>
  52. <tr v-if="errorCheck.pwdChg">
  53. <th class="align-top">{{$t('common.header.myInfoUpdateModal.newPassword2')}}<span class="cir"></span></th>
  54. <td colspan="3">
  55. <div class="txt-field-box" :class="errorCheck.newPassword2Error ? 'error' : ''">
  56. <v-text-field type="password" :placeholder="$t('common.header.myInfoUpdateModal.newPasswordDesc2')" class="custom-input mini2" style="width:100%;" v-model="myInfo.newPassword2" @input="setInputField('newPassword2')"></v-text-field>
  57. <i class="ico"></i>
  58. </div>
  59. <p class="error-txt" v-if="errorCheck.newPassword1Error || errorCheck.newPassword2Error">{{ errorCheck.inputValidPasswordTxt }}</p>
  60. </td>
  61. </tr>
  62. <tr>
  63. <th class="align-top">{{$t('common.header.myInfoUpdateModal.email')}}<span class="cir"></span></th>
  64. <td colspan="3" class="pr--0">
  65. <div class="input-wrap">
  66. <div class="txt-field-box" :class="errorCheck.isEmailErr ? 'error' : ''">
  67. <v-text-field placeholder="admin@test.com" class="custom-input mini2" v-model="myInfo.email" style="width: 100%" @input="setInputField('email')"></v-text-field>
  68. <i class="ico"></i>
  69. </div>
  70. <v-btn class="custom-btn btn-black mini2" style="width:5.1875rem; height:2.25rem;" @click="fnDupCheck('EMAIL')" :disabled="!errorCheck.isEmailBtn">{{$t('common.header.myInfoUpdateModal.dupCheck')}}</v-btn>
  71. </div>
  72. <p class="error-txt" v-if="errorCheck.isEmailErr">{{errorCheck.emailErrText}}</p>
  73. <p class="success-txt" v-if="errorCheck.isEmailDupCheck">{{$t('common.header.myInfoUpdateModal.valid.dupSuccessEmail')}}</p>
  74. </td>
  75. </tr>
  76. <tr>
  77. <th>{{$t('common.header.myInfoUpdateModal.phone')}}<span class="cir"></span></th>
  78. <td colspan="3" class="pr--0">
  79. <div class="input-wrap">
  80. <div class="txt-field-box" :class="errorCheck.isPhoneErr ? 'error' : ''">
  81. <v-text-field placeholder="010-1234-5678" class="custom-input mini2" v-model="myInfo.phone" @input="setInputField('phone')"></v-text-field>
  82. <i class="ico"></i>
  83. </div>
  84. <v-btn class="custom-btn btn-black mini2" style="width:5.1875rem; height: 2.25rem;" @click="fnDupCheck('PHONE_NUMBER')" :disabled="!errorCheck.isPhoneBtn">{{$t('common.header.myInfoUpdateModal.dupCheck')}}</v-btn>
  85. </div>
  86. <p class="error-txt" v-if="errorCheck.isPhoneErr">{{errorCheck.phoneErrText}}</p>
  87. <p class="success-txt" v-if="errorCheck.isPhoneDupCheck">{{$t('common.header.myInfoUpdateModal.valid.dupSuccessPhone')}}</p>
  88. </td>
  89. </tr>
  90. </tbody>
  91. </table>
  92. </div>
  93. </div>
  94. <div class="btn-wrap">
  95. <v-btn class="custom-btn btn-white mini" @click="fnMyInfoClose()"><i class="ico"></i>
  96. {{$t('common.cancel')}}
  97. </v-btn>
  98. <v-btn class="custom-btn btn-blue2 btn-mod mini" @click="fnUpdate" :disabled="!errorCheck.isUpdateBtn"><i class="ico"></i> 수정 </v-btn>
  99. </div>
  100. </div>
  101. </v-dialog>
  102. </template>
  103. <script setup>
  104. /***********************
  105. * import
  106. ************************/
  107. import apiUrl from '@/composables/useApi';
  108. import useUtil from '@/composables/useUtil';
  109. import useValid from '@/composables/useValid';
  110. import { useI18n } from 'vue-i18n';
  111. /***********************
  112. * plugins inject
  113. ************************/
  114. const { $toast, $log, $dayjs, $eventBus } = useNuxtApp()
  115. // 현재 입력 중인 필드를 설정하는 함수
  116. const setInputField = (name) => {
  117. fnValidCheck(name)
  118. }
  119. // props
  120. const props = defineProps({
  121. isMyInfoUpdateModal: Boolean,
  122. })
  123. // 참조가능 데이터 설정
  124. defineExpose({
  125. fnInit
  126. })
  127. // 발신 이벤트 선언
  128. const emit = defineEmits(['closeModal'])
  129. const i18n = useI18n()
  130. /***********************
  131. * data & created
  132. ************************/
  133. const errorCheck = ref({
  134. pwdChg: false, // 비밀번호 활성화 YN
  135. newPassword1Error: false, // 신규비밀번호 입력 에러
  136. newPassword2Error: false, // 변경비밀번호 체크 입력 에러
  137. inputValidPasswordTxt: '', // 비밀번호 validation 문구
  138. isEmailErr: false, // 이메일 에러
  139. emailErrText: 'success', // 이메일 중복체크 문구
  140. isEmailBtn: false,
  141. isPhoneErr: false, // 전화번호 에러
  142. phoneErrText: 'success', // 전화번호 중복체크 문구
  143. isPhoneBtn: false,
  144. isEmailDupCheck: false, //
  145. isPhoneDupCheck: false,
  146. isUpdateBtn: false,
  147. })
  148. const myInfo = ref({
  149. id: '',
  150. name: '',
  151. newPassword1: '',
  152. newPassword2: '',
  153. tenantName: '',
  154. accountRole: '',
  155. email: '',
  156. phone: ''
  157. })
  158. const myInfoClone = ref({})
  159. const errorCheckClone = ref({})
  160. /*********************
  161. * computed
  162. *********************/
  163. // 변경사항 여부 체크
  164. const isUpdateContents = computed(() => {
  165. const isUpdate = JSON.stringify(myInfo.value) != JSON.stringify(myInfoClone.value)
  166. return isUpdate
  167. })
  168. watch(() => errorCheck.value, (newV, oldV) => {
  169. let isEmailDupCheck = true
  170. let isPhoneDupCheck = true
  171. // 비밀번호 입력 란이 활성화 되어있을 경우
  172. if(errorCheck.value.pwdChg) {
  173. if(errorCheck.value.inputValidPasswordTxt === 'success') {
  174. if(errorCheck.value.emailErrText != 'success') isEmailDupCheck = false
  175. else isEmailDupCheck = true
  176. if(errorCheck.value.phoneErrText != 'success') isPhoneDupCheck = false
  177. else isPhoneDupCheck = true
  178. if(isEmailDupCheck && isPhoneDupCheck) {
  179. errorCheck.value.isUpdateBtn = true
  180. } else {
  181. errorCheck.value.isUpdateBtn = false
  182. }
  183. } else {
  184. errorCheck.value.isUpdateBtn = false
  185. }
  186. } else {
  187. if(errorCheck.value.emailErrText != 'success') isEmailDupCheck = false
  188. else isEmailDupCheck = true
  189. if(errorCheck.value.phoneErrText != 'success') isPhoneDupCheck = false
  190. else isPhoneDupCheck = true
  191. if(isEmailDupCheck && isPhoneDupCheck) {
  192. errorCheck.value.isUpdateBtn = true
  193. } else {
  194. errorCheck.value.isUpdateBtn = false
  195. }
  196. }
  197. },{ deep: true, immediate: false })
  198. /***********************
  199. * Methods
  200. ************************/
  201. function fnInit(){
  202. // 데이터 초기화
  203. errorCheckClone.value = _cloneDeep(errorCheck.value)
  204. fnGetMyInfo()
  205. }
  206. /**
  207. * @SCRIPT
  208. * 내 정보 조회
  209. */
  210. function fnGetMyInfo(){
  211. useAxios().get(apiUrl.myInfo).then((res) => {
  212. let dataObj = {}
  213. dataObj = res.data.data
  214. dataObj.accessToken = useAuthStore().getAccessToken
  215. dataObj.refreshToken = useAuthStore().getRefreshToken
  216. useAuthStore().setAuth(dataObj)
  217. myInfo.value = {
  218. id: dataObj.accountId,
  219. name: dataObj.accountName,
  220. newPassword1: '',
  221. newPassword2: '',
  222. tenantName: dataObj.tenantName,
  223. accountRole: dataObj.accountRole,
  224. emailRecvYN: dataObj.emailRecvYN,
  225. smsRecvYN: dataObj.smsRecvYN,
  226. recvSdate: $dayjs(dataObj.recvSdate).format('YYYY-MM-DD HH:mm:ss'),
  227. recvEdate: $dayjs(dataObj.recvEdate).format('YYYY-MM-DD HH:mm:ss'),
  228. email: dataObj.email,
  229. phone: dataObj.phoneNumber
  230. }
  231. myInfoClone.value = _cloneDeep(myInfo.value)
  232. $log.debug("[myInfoUpdate][fnGetMyInfo][success]")
  233. }).catch((error)=>{
  234. $log.debug("[myInfoUpdate][fnGetMyInfo][error]")
  235. useErrorHandler().fnSetCommErrorHandle(error, fnGetMyInfo)
  236. }).finally(()=>{
  237. $log.debug("[myInfoUpdate][fnGetMyInfo][finished]")
  238. })
  239. }
  240. /**
  241. * @SCRIPT
  242. * 이메일 validation 검사 및 중복확인 처리
  243. */
  244. function fnDupCheck(type){
  245. if(type === 'EMAIL') {
  246. if(myInfo.value.email === myInfoClone.value.email) {
  247. errorCheck.value.isEmailErr = true
  248. errorCheck.value.isEmailDupCheck = false
  249. errorCheck.value.emailErrText = i18n.t('common.header.myInfoUpdateModal.valid.dupEmail')
  250. } else {
  251. fnDupCheckProc('EMAIL')
  252. }
  253. } else {
  254. if(myInfo.value.phone.replace(/\D/g, '') === myInfoClone.value.phone.replace(/\D/g, '')) {
  255. errorCheck.value.isPhoneErr = true
  256. errorCheck.value.isPhoneDupCheck = false
  257. errorCheck.value.phoneErrText = i18n.t('common.header.myInfoUpdateModal.valid.dupPhone')
  258. } else {
  259. fnDupCheckProc('PHONE_NUMBER')
  260. }
  261. }
  262. }
  263. function fnMyInfoClose() {
  264. errorCheck.value = _cloneDeep(errorCheckClone.value)
  265. emit('closeModal')
  266. }
  267. /**
  268. * @API
  269. * 이메일 & 전화번호 중복체크
  270. */
  271. function fnDupCheckProc(type){
  272. let _req = {
  273. checkType: type
  274. }
  275. if(type === 'EMAIL') {
  276. _req.checkValue = myInfo.value.email
  277. } else {
  278. _req.checkValue = myInfo.value.phone
  279. }
  280. useAxios().get(apiUrl.accountDupChk, {params: _req}).then((res) => {
  281. if(type === 'EMAIL') {
  282. errorCheck.value.isEmailDupCheck = true
  283. errorCheck.value.emailErrText = 'success'
  284. } else {
  285. errorCheck.value.isPhoneDupCheck = true
  286. errorCheck.value.phoneErrText = 'success'
  287. }
  288. $log.debug("[myInfoUpdate][fnDupCheckProc][success]")
  289. }).catch((error)=>{
  290. $log.debug("[myInfoUpdate][fnDupCheckProc][error]")
  291. // 에러 메시지 체크
  292. let errData = error.response.data
  293. let errorMessage = ''
  294. if(errData.message === 'DUPLICATE_ERROR') {
  295. if(type === 'EMAIL') {
  296. errorMessage = i18n.t('common.header.myInfoUpdateModal.valid.dupFailEmail')
  297. errorCheck.value.isEmailBtn = false
  298. errorCheck.value.isEmailErr = true
  299. errorCheck.value.emailErrText = errorMessage
  300. } else {
  301. errorMessage = i18n.t('common.header.myInfoUpdateModal.valid.dupFailPhone')
  302. errorCheck.value.isPhoneBtn = false
  303. errorCheck.value.isPhoneErr = true
  304. errorCheck.value.phoneErrText = errorMessage
  305. }
  306. }
  307. //$toast.error(errorMessage)
  308. }).finally(()=>{
  309. $log.debug("[myInfoUpdate][fnDupCheckProc][finished]")
  310. })
  311. }
  312. /**
  313. * @API
  314. * 수정API
  315. */
  316. function fnUpdate(){
  317. // // 1. 변경사항 체크 2. 필수입력값 체크 3. validation 검사
  318. if(isUpdateContents.value){
  319. let _req = {
  320. newPassword: btoa(myInfo.value.newPassword2),
  321. email: myInfo.value.email,
  322. phoneNumber: myInfo.value.phone
  323. }
  324. useAxios().post(apiUrl.myInfoUpdate, _req).then((res) => {
  325. useAuthStore().setHeaderMyInfo(_req)
  326. console.log('%c 내 정보가 수정되었습니다.' ,'color:#bada55','')
  327. $toast.success(i18n.t('common.header.myInfoUpdateModal.successMyInfo'))
  328. emit('closeModal')
  329. $log.debug("[myInfoUpdate][fnUpdate][success]")
  330. }).catch((error)=>{
  331. $log.debug("[myInfoUpdate][fnUpdate][error]")
  332. useErrorHandler().fnSetCommErrorHandle(error, fnUpdate)
  333. }).finally(()=>{
  334. $log.debug("[myInfoUpdate][fnUpdate][finished]")
  335. })
  336. }
  337. }
  338. /**
  339. * @SCRIPT
  340. * 비밀번호확인 validation
  341. */
  342. function fnValidCheck(type) {
  343. if(type === 'newPassword1') {
  344. let digits1 = myInfo.value.phone.replace(/\D/g, '')
  345. let digits2 = myInfo.value.newPassword1.replace(/\D/g, '')
  346. let lastEightDigits1 = digits1.slice(-8)
  347. let lastEightDigits2 = digits2.slice(-8)
  348. if(_includes(myInfo.value.newPassword1 , myInfo.value.id)) {
  349. // 아이디는 비밀번호로 사용할 수 없습니다.
  350. errorCheck.value.newPassword1Error = true
  351. errorCheck.value.inputValidPasswordTxt = i18n.t('common.header.myInfoUpdateModal.valid.includeId')
  352. } else if(lastEightDigits1 === lastEightDigits2) {
  353. // 연락처와 유사한 비밀번호는 사용할 수 없습니다.
  354. errorCheck.value.newPassword1Error = true
  355. errorCheck.value.inputValidPasswordTxt = i18n.t('common.header.myInfoUpdateModal.valid.includePhone')
  356. } else if(/(.)\1{2,}/.test(myInfo.value.newPassword1)) {
  357. // 3자리 이상 연속 숫자, 문자는 사용 불가합니다.
  358. errorCheck.value.newPassword1Error = true
  359. errorCheck.value.inputValidPasswordTxt = i18n.t('common.header.myInfoUpdateModal.valid.continuousUse')
  360. } else if(!/^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,30}$/i.test(myInfo.value.newPassword1)) {
  361. // 비밀번호는 문자,숫자,특수문자 조합 8~30자리로입력
  362. errorCheck.value.newPassword1Error = true
  363. errorCheck.value.inputValidPasswordTxt = i18n.t('common.header.myInfoUpdateModal.valid.validPassword')
  364. } else {
  365. errorCheck.value.newPassword1Error = false
  366. }
  367. } else if(type === 'newPassword2') {
  368. if(myInfo.value.newPassword1 != myInfo.value.newPassword2) {
  369. // 신규비밀번호가 일치 하지 않습니다.
  370. errorCheck.value.newPassword2Error = true
  371. errorCheck.value.inputValidPasswordTxt = i18n.t('common.header.myInfoUpdateModal.valid.missmatchPassword')
  372. } else if(!/^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,30}$/i.test(myInfo.value.newPassword1)) {
  373. errorCheck.value.newPassword2Error = true
  374. errorCheck.value.inputValidPasswordTxt = i18n.t('common.header.myInfoUpdateModal.valid.validPassword')
  375. } else {
  376. if(!errorCheck.value.newPassword1Error) {
  377. errorCheck.value.newPassword2Error = false
  378. errorCheck.value.inputValidPasswordTxt = 'success'
  379. }
  380. }
  381. } else if(type === 'email') {
  382. let isValid = useValid.emailStrChk(myInfo.value.email)
  383. if(useUtil.isNull(myInfo.value.email)) {
  384. errorCheck.value.isEmailErr = true
  385. errorCheck.value.isEmailBtn = false
  386. errorCheck.value.isEmailDupCheck = false
  387. errorCheck.value.emailErrText = i18n.t('login.findId.valid.isNullEmail')
  388. } else if(!isValid) {
  389. errorCheck.value.isEmailErr = true
  390. errorCheck.value.isEmailBtn = false
  391. errorCheck.value.isEmailDupCheck = false
  392. errorCheck.value.emailErrText = i18n.t('login.findId.valid.isTypeEmail')
  393. } else {
  394. errorCheck.value.isEmailErr = false
  395. errorCheck.value.isEmailBtn = true
  396. }
  397. }else {
  398. // phone
  399. //let isValid = useValid.phoneStrChk(myInfo.value.phone)
  400. myInfo.value.phone = useValid.p5gNumCheck(myInfo.value.phone, 'phone')
  401. if(useUtil.isNull(myInfo.value.phone) || myInfo.value.phone.length <= 10) {
  402. errorCheck.value.isPhoneErr = true
  403. errorCheck.value.isPhoneBtn = false
  404. errorCheck.value.isPhoneDupCheck = false
  405. errorCheck.value.phoneErrText = i18n.t('common.header.myInfoUpdateModal.valid.validPhone')
  406. } else {
  407. errorCheck.value.isPhoneErr = false
  408. errorCheck.value.isPhoneBtn = true
  409. }
  410. }
  411. }
  412. </script>