You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

577 lines
20 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. <template>
  2. <div class="editPersonalInfo xl:tw-max-w-[1246px] xl:tw-mx-auto">
  3. <div class="xl:tw-flex xl:tw-justify-between xl:tw-items-start">
  4. <userSidebar :userData="userData" :firstName="firstName" :lastName="lastName" class="tw-hidden xl:tw-block">
  5. </userSidebar>
  6. <div class="tw-bg-white xl:tw-p-[30px] xl:tw-rounded-[20px] xl:tw-min-w-[900px] xl:tw-max-w-[900px]">
  7. <div class="tw-text-[20px] tw-font-bold tw-text-base-primary tw-mb-[20px] md:t24 md:tw-mb-[30px]">
  8. <two-dots class="tw-mr-[30px]"></two-dots>{{ $t("userProfile.editPersonalInfo") }}
  9. </div>
  10. <!-- alert -->
  11. <div v-if="successUpdate" class="tw-flex tw-w-full tw-right-auto tw-left-auto">
  12. <div type="success"
  13. class="tw-absolute tw-top-0 tw-right-0 tw-left-0 tw-mx-auto tw-flex tw-flex-row tw-w-fit tw-items-center tw-bg-success-background tw-rounded-[12px] tw-px-[12px] tw-py-[10px]">
  14. <img src="~/assets/svg/alert_success.svg" class="tw-w-[16px] tw-mr-[10px] md:tw-w-[20px]" alt="" />
  15. <div class="tw-hidden md:tw-block tw-text-success-default tw-font-bold tw-text-[16px] md:tw-mr-[16px]">
  16. Awesome!
  17. </div>
  18. <div class="tw-text-[12px] tw-text-success-default tw-mr-[9px] md:tw-text-[16px] md:tw-mr-[16px]">
  19. Successfully updated.
  20. </div>
  21. <img src="~/assets/svg/alert_close.svg" @click="successUpdate = !successUpdate" />
  22. </div>
  23. </div>
  24. <div v-if="fileTooBig" class="tw-flex tw-w-full tw-right-auto tw-left-auto">
  25. <div type="error"
  26. class="tw-absolute tw-top-0 tw-right-0 tw-left-0 tw-mx-auto tw-flex tw-flex-row tw-w-fit tw-items-center tw-bg-error-background tw-rounded-[16px] tw-px-[12px] tw-py-[10px] tw-text-[12px]">
  27. <img src="~/assets/svg/alert_notice.svg" class="tw-w-[16px] tw-mr-[10px] md:tw-w-[20px]" alt="" />
  28. <div class="tw-hidden md:tw-block tw-text-error-default tw-mr-[25px] tw-text-[16px]">
  29. Oops!
  30. </div>
  31. <div class="tw-text-error-default tw-mr-[8px] md:tw-text-[16px] md:tw-mr-[16px]">
  32. Please upload an image under 2MB.
  33. </div>
  34. <img src="~/assets/svg/alert_close_red.svg" @click="fileTooBig = !fileTooBig" alt="" />
  35. </div>
  36. </div>
  37. <!-- user picture -->
  38. <div class="tw-flex tw-items-center tw-mb-[30px]">
  39. <div>
  40. <img v-if="userData.MemberPicture" :src="userData.MemberPicture"
  41. class="tw-border tw-border-solid tw-border-neutral-300 tw-h-[64px] tw-w-[64px] tw-rounded-[50px] tw-mr-[15px] md:tw-h-[90px] md:tw-w-[90px] md:tw-mr-[30px]"
  42. alt="" />
  43. <div v-else
  44. class="tw-flex tw-justify-center tw-items-center tw-bg-primary-1 tw-text-white tw-text-[32px] tw-h-[64px] tw-w-[64px] tw-rounded-[50px] tw-mr-[15px] md:tw-h-[90px] md:tw-w-[90px] md:tw-mr-[30px]">
  45. {{ lastName.split("")[0] || "" }}
  46. </div>
  47. </div>
  48. <div>
  49. <button
  50. class="tw-border tw-border-solid tw-border-primary-1 tw-text-[14px] tw-text-primary-1 tw-px-[16px] tw-py-[9px] tw-rounded-[12px] md:tw-text-[16px]"
  51. @click="handleUploadEvent">
  52. {{ $t("userProfile.uploadPicture") }}
  53. </button>
  54. <input type="file" class="d-none" ref="uploader" @change="userPictureUpload" />
  55. </div>
  56. </div>
  57. <!-- form -->
  58. <form
  59. class="tw-grid tw-grid-cols-1 tw-gap-y-[20px] tw-mb-[58px] md:tw-grid-cols-2 md:tw-gap-x-[60px] md:tw-mb-[50px]">
  60. <!-- tilte -->
  61. <div class="element md:tw-col-span-2 md:tw-max-w-[394px]">
  62. <div class="tw-text-[14px] tw-text-base-primary tw-mb-[10px]">
  63. {{ $t("userProfile.Title") }}
  64. </div>
  65. <elementSelectWithIndex :select="{
  66. id: 'Title',
  67. label: 'Title',
  68. required: false,
  69. }" :yearList="genderOptions" :default="userData.Title" :validation="validation.Title"
  70. @change="userData.Title = $event"></elementSelectWithIndex>
  71. </div>
  72. <!-- firstName -->
  73. <div class="element">
  74. <elementInput :input="{
  75. id: 'FirstName',
  76. label: 'First Name',
  77. required: true,
  78. type: 'text',
  79. }" :default="userData.FirstName" :validation="validation.FirstName"
  80. @change="userData.FirstName = $event"></elementInput>
  81. </div>
  82. <!-- lastName -->
  83. <div class="element">
  84. <elementInput :input="{
  85. id: 'LastName',
  86. label: 'Last Name',
  87. required: true,
  88. type: 'text',
  89. }" :default="userData.LastName" :validation="validation.LastName" @change="userData.LastName = $event">
  90. </elementInput>
  91. </div>
  92. <!-- email -->
  93. <div class="element">
  94. <elementInput :input="{
  95. id: 'Email',
  96. label: 'userProfile.personalMail',
  97. required: true,
  98. type: 'email',
  99. }" :default="userData.Email" :validation="validation.Email" @change="userData.Email = $event">
  100. </elementInput>
  101. </div>
  102. <!-- Phone -->
  103. <div>
  104. <div class="tw-text-[14px] tw-text-base-primary tw-mb-[10px]">
  105. {{ $t("userProfile.phone") }}
  106. </div>
  107. <div>
  108. <vue-country-code class="d-none" v-model="countryCode" enabledCountryCode :enabledFlags="false"
  109. @onSelect="showCode"></vue-country-code>
  110. <vue-phone-number-input v-model="userData.Phone" color="#EF5A5A" valid-color="#EE9546" :border-radius="5"
  111. no-flags no-example @update="getPhoneData" :translations="translateOption">
  112. </vue-phone-number-input>
  113. </div>
  114. </div>
  115. <!-- Birthday -->
  116. <div class="md:tw-col-span-2 xl:tw-col-span-1">
  117. <div class="tw-text-[14px] tw-text-base-primary tw-mb-[10px]">
  118. {{ $t("userProfile.birthday")
  119. }}<span class="tw-text-error-default">*</span>
  120. </div>
  121. <div class="tw-grid tw-grid-cols-3 tw-gap-[20px]">
  122. <div class="element">
  123. <elementSelectWithIndex :select="{
  124. id: 'yearSelect',
  125. required: true,
  126. }" :yearList="yearOptions" :default="yearSelect" :validation="validation.yearSelect"
  127. @change="yearSelect = $event"></elementSelectWithIndex>
  128. </div>
  129. <div class="element">
  130. <elementSelectWithIndex :select="{
  131. id: 'monthSelect',
  132. required: false,
  133. }" :yearList="monthOptions" :default="monthSelect" :validation="validation.monthSelect"
  134. @change="monthSelect = $event"></elementSelectWithIndex>
  135. </div>
  136. <div class="element">
  137. <elementSelectWithIndex :select="{
  138. id: 'daySelect',
  139. required: false,
  140. }" :yearList="dayOptions" :default="daySelect" :validation="validation.daySelect"
  141. @change="daySelect = $event"></elementSelectWithIndex>
  142. </div>
  143. </div>
  144. </div>
  145. <!-- Country -->
  146. <div class="element md:tw-col-span-2 xl:tw-col-span-1">
  147. <elementSelect :select="{
  148. id: 'Country',
  149. label: 'userProfile.countryAndRegion',
  150. required: true,
  151. }" :selectList="countryOptions" :default="userData.CountryID" :validation="validation.CountryID"
  152. @change="userData.CountryID = $event"></elementSelect>
  153. </div>
  154. <!-- language -->
  155. <v-spacer class="tw-items-center md:tw-flex md:tw-justify-start md:tw-mt-[20px] md:tw-flex-nowrap">
  156. <div class="tw-inline-block tw-mb-[20px] md:tw-whitespace-nowrap md:tw-mb-0">
  157. {{ $t("userProfile.preferLanguage") }}
  158. </div>
  159. <v-spacer class="tw-flex md:tw-w-fit md:tw-ml-[30px]">
  160. <v-checkbox v-model="languageSelect.en" class="mt-0" hide-details :label="$t('English')"></v-checkbox>
  161. <v-checkbox v-model="languageSelect.zhtw" class="ms-10 mt-0" hide-details :label="$t('Chinese')">
  162. </v-checkbox>
  163. </v-spacer>
  164. </v-spacer>
  165. </form>
  166. <!-- button -->
  167. <div>
  168. <button
  169. class="tw-text-[18px] tw-bg-primary-1 tw-text-white tw-py-[12px] tw-w-full tw-rounded-[16px] tw-mb-[10px] md:tw-w-fit md:tw-px-[16px] md:tw-mr-[20px]"
  170. @click="patchUserData()">
  171. {{ $t("userProfile.saveButton") }}
  172. </button>
  173. <button
  174. class="tw-text-[18px] tw-bg-white tw-text-primary-1 tw-py-[12px] tw-w-full tw-rounded-[16px] md:tw-w-fit md:tw-px-[16px] xl:tw-hidden">
  175. {{ $t("Cancel") }}
  176. </button>
  177. </div>
  178. </div>
  179. </div>
  180. <CropImageDialog :isCropImageDialogActive="isCropImageDialogActive" :cropImagePreview="cropImagePreview"
  181. @close-crop-dialog="closeCropDialog" @upload-image-success="handleImageUpdate"></CropImageDialog>
  182. </div>
  183. </template>
  184. <script>
  185. import elementSelect from "@/components/newComponent/form/ElementSelect.vue";
  186. import elementInput from "@/components/newComponent/form/ElementInput.vue";
  187. import SavedExhibitions from "../../components/user/savedExhibitions.vue";
  188. import SavedExhibitionsDialog from "../../components/user/savedExhibitionsDialog.vue";
  189. import CropImageDialog from "../../components/user/cropImageDialog.vue";
  190. import TwoDots from "@/components/TwoDots";
  191. import userSidebar from "@/components/user/userSidebar.vue";
  192. import elementSelectWithIndex from "@/components/newComponent/form/ElementSelectWithIndex.vue";
  193. import is from "is_js";
  194. export default {
  195. name: "editPersonalInfo",
  196. layout: "profile",
  197. components: {
  198. elementSelect,
  199. elementInput,
  200. SavedExhibitions,
  201. SavedExhibitionsDialog,
  202. CropImageDialog,
  203. TwoDots,
  204. userSidebar,
  205. elementSelectWithIndex,
  206. is,
  207. },
  208. data() {
  209. return {
  210. countryList: [],
  211. genderOptions: ["Mr.", "Ms."],
  212. successUpdate: false,
  213. fileTooBig: false,
  214. firstName: "",
  215. lastName: "",
  216. countryCode: "",
  217. userData: {},
  218. userCompanyId: [],
  219. propUserCompany: {},
  220. yearOptions: [],
  221. monthOptions: [],
  222. dayOptions: [],
  223. countryOptions: [],
  224. countrySortOptions: [],
  225. yearSelect: "",
  226. monthSelect: "",
  227. daySelect: "",
  228. languageSelect: {
  229. en: "",
  230. zhtw: "",
  231. },
  232. isCropImageDialogActive: false,
  233. cropImagePreview: "",
  234. dateMenu: false,
  235. translateOption: {
  236. countrySelectorLabel: this.$t("country code"),
  237. phoneNumberLabel: this.$t("phone number"),
  238. },
  239. rules: {
  240. require: (value) => !!value || this.$t("Required."),
  241. email: (v) =>
  242. /^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/.test(
  243. v
  244. ) || this.$t("Invalid email"),
  245. checkPassword: (v) =>
  246. (/(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])/.test(v) &&
  247. v.length >= 8 &&
  248. v.length <= 20) ||
  249. this.$t(
  250. "Passwords must be 8-20 characters with at least 1 number, 1 lower case letter and 1 upper case letter"
  251. ),
  252. },
  253. personalInfoValid: false,
  254. phoneValid: false,
  255. isNavActive: true,
  256. // width: 0,
  257. validation: {
  258. Title: true,
  259. FirstName: true,
  260. LastName: true,
  261. Email: true,
  262. yearSelect: true,
  263. monthSelect: true,
  264. daySelect: true,
  265. CountryID: true,
  266. },
  267. errors: null,
  268. userPic: {},
  269. payload:{
  270. picName: "",
  271. picSize: "",
  272. picType: "",
  273. },
  274. };
  275. },
  276. created() {
  277. this.fetchCountry();
  278. this.$store.dispatch("updatePicture");
  279. this.fetchUserData();
  280. },
  281. mounted() {
  282. this.yearOptions = Array.from(new Array(103), (val, index) =>
  283. (index + 1920).toString()
  284. );
  285. this.monthOptions = Array.from(new Array(13), (val, index) => {
  286. if (index < 10 && index > 0) {
  287. return "0" + index.toString();
  288. }
  289. if (index >= 10) {
  290. return index.toString();
  291. }
  292. });
  293. this.dayOptions = Array.from(new Array(32), (val, index) => {
  294. if (index < 10 && index > 0) {
  295. return "0" + index.toString();
  296. }
  297. if (index >= 10) {
  298. return index.toString();
  299. }
  300. });
  301. this.$nextTick(() => {
  302. window.addEventListener("resize", this.onResize);
  303. });
  304. },
  305. methods: {
  306. patchUserData() {
  307. if (this.width < 1366) {
  308. this.isEditInfoDialogActive = !this.isEditInfoDialogActive;
  309. }
  310. this.validators();
  311. // if (this.validators()) {
  312. // this.userData.prefer_country = JSON.stringify(this.languageSelect);
  313. // if (this.$vuetify.breakpoint.name !== "xs") {
  314. // this.userData.birth_date =
  315. // this.yearSelect + "-" + this.monthSelect + "-" + this.daySelect;
  316. // if (this.userData.birth_date.length < 10) {
  317. // this.userData.birth_date = null;
  318. // }
  319. // }
  320. if(this.$vuetify.breakpoint.name !== "xs"){
  321. this.userData.BirthDate = this.yearSelect + "-" + this.monthSelect + "-" + this.daySelect;
  322. if(this.userData.BirthDate.length < 10){
  323. this.userData.BirthDate = null;
  324. }
  325. }
  326. if(this.languageSelect.en == true){
  327. this.userData.LanguageID = "en-US";
  328. }else if(this.languageSelect.zhtw == true){
  329. this.userData.LanguageID = "zh-TW";
  330. }else{
  331. this.userData.LanguageID = "null";
  332. }
  333. const patchData = JSON.parse(JSON.stringify(this.userData));
  334. // delete patchData.LoginLog;
  335. // delete patchData.UserCompany;
  336. // delete patchData.UserSocialRelation;
  337. // delete patchData.UserExhibition;
  338. this.$axios
  339. .post(
  340. `/trending/api/Members/Member`, patchData
  341. )
  342. .then((response) => {
  343. //console.log(JSON.stringify(response));
  344. if (response.status == 200) {
  345. this.$notify({
  346. type: "success",
  347. text: "更新成功",
  348. });
  349. }
  350. this.fetchUserData();
  351. // this.$auth.$storage.setUniversal("userPicture", patchData.MemberPicture);
  352. // this.$auth.$storage.setUniversal(
  353. // "userLastName",
  354. // patchData.LastName
  355. // );
  356. // this.$store.dispatch("updatePicture");
  357. })
  358. .catch((error) => {
  359. console.log(error);
  360. });
  361. },
  362. fetchUserData() {
  363. this.$axios
  364. .get(
  365. `/trending/api/Members/Info`
  366. )
  367. .then((response) => {
  368. //console.log(JSON.stringify(response));
  369. if(response && response.data && response.data.DATA && response.data.DATA.rel){
  370. let data = response.data.DATA.rel
  371. if(data){
  372. this.userData = data;
  373. this.firstName = this.userData.FirstName;
  374. this.lastName = this.userData.LastName;
  375. this.userData.Phone ? (this.phoneValid = true): (this.phoneValid = false);
  376. if(this.userData.ArgumentID == "en-US"){
  377. this.languageSelect.en = true;
  378. }else if(this.userData.ArgumentID == "zh-TW"){
  379. this.languageSelect.zhtw = true;
  380. }else{
  381. this.languageSelect.en = "";
  382. this.languageSelect.zhtw = "";
  383. }
  384. if (
  385. this.userData.BirthDate && typeof this.userData.BirthDate === "object"
  386. ){
  387. this.yearSelect = "";
  388. this.monthSelect = "";
  389. this.daySelect = "";
  390. }else{
  391. const space = this.userData.BirthDate.split("T");
  392. const date = space[0].split("-");
  393. this.yearSelect = date[0];
  394. this.monthSelect = date[1];
  395. this.daySelect = date[2];
  396. }
  397. }
  398. }
  399. })
  400. .catch((error) => {
  401. console.log(error);
  402. });
  403. },
  404. handleUploadEvent() {
  405. window.addEventListener("focus", () => { }, { once: true });
  406. this.$refs.uploader.click();
  407. },
  408. userPictureUpload(e) {
  409. //const payload = new FormData();
  410. //payload.append("file", e.target.files[0].name);
  411. const name = (JSON.stringify(e.target.files[0].name)).toString();
  412. const size = (JSON.stringify(e.target.files[0].size)).toString();
  413. const type = (JSON.stringify(e.target.files[0].type)).toString();
  414. console.log("size:" + name);
  415. //this.payload.push(name, size, type);
  416. this.payload.picName = name;
  417. this.payload.picSize = size;
  418. this.payload.picType = type;
  419. console.log("payload:" + JSON.stringify(this.payload));
  420. const fileSize = (e.target.files[0].size / 1024 / 1024).toFixed(1);
  421. if (fileSize >= 2) {
  422. this.fileTooBig = !this.fileTooBig;
  423. } else {
  424. this.$axios
  425. //.post("/users/images", payload)
  426. //.post("/trending/api/Members/UploadAvatar)
  427. .post(
  428. `/trending/api/Members/UploadAvatar`
  429. )
  430. .then((response) => {
  431. console.log(JSON.stringify(response));
  432. if(response && response.data && response.data.DATA && response.data.DATA.rel){
  433. let data = response.data.DATA.rel
  434. if(data){
  435. this.cropImagePreview = data;
  436. }
  437. }
  438. })
  439. .catch((error) => {
  440. console.log(error);
  441. });
  442. this.isCropImageDialogActive = !this.isCropImageDialogActive;
  443. }
  444. },
  445. getPhoneData(phoneData) {
  446. this.countryCode = phoneData.countryCode;
  447. this.phoneValid = phoneData.isValid;
  448. },
  449. showCode(object) {
  450. this.userData.country_code = object.dialCode;
  451. },
  452. fetchCountry() {
  453. this.$axios
  454. .get(`/trending/api/location/countries?RegionID&Lang=${this.$i18n.localeProperties["langQuery"]}`)
  455. .then((response) => {
  456. //console.log(JSON.stringify(response));
  457. if(response && response.data && response.data.DATA && response.data.DATA.rel){
  458. let data = response.data.DATA.rel
  459. if(data){
  460. this.countryList = data;
  461. //console.log(this.countryList);
  462. this.countryOptions = this.countryList.map((item) => {
  463. return {
  464. id: item.CountryID,
  465. name: item.CountryName,
  466. };
  467. });
  468. }
  469. }
  470. })
  471. .catch((error) => {
  472. console.log(error);
  473. });
  474. },
  475. closeCropDialog() {
  476. this.isCropImageDialogActive = !this.isCropImageDialogActive;
  477. },
  478. handleImageUpdate(pictureURL) {
  479. this.userData.MemberPicture = pictureURL;
  480. this.patchUserData();
  481. this.closeCropDialog();
  482. },
  483. validators() {
  484. if (is.empty(this.userData.FirstName)) {
  485. this.validation.FirstName = false;
  486. } else {
  487. this.validation.FirstName = true;
  488. }
  489. if (is.empty(this.userData.LastName)) {
  490. this.validation.LastName = false;
  491. } else {
  492. this.validation.LastName = true;
  493. }
  494. if (is.empty(this.userData.Email) || is.not.email(this.userData.Email)) {
  495. this.validation.Email = false;
  496. } else {
  497. this.validation.Email = true;
  498. }
  499. if (is.empty(this.yearSelect)) {
  500. this.validation.yearSelect = false;
  501. } else {
  502. this.validation.yearSelect = true;
  503. }
  504. if (is.empty(this.monthSelect)) {
  505. this.validation.monthSelect = false;
  506. } else {
  507. this.validation.monthSelect = true;
  508. }
  509. if (is.empty(this.daySelect)) {
  510. this.validation.daySelect = false;
  511. } else {
  512. this.validation.daySelect = true;
  513. }
  514. if (is.null(this.userData.CountryID) || this.userData.CountryID == 0) {
  515. this.validation.CountryID = false;
  516. } else {
  517. this.validation.CountryID = true;
  518. }
  519. this.errors = Object.entries(this.validation).filter(
  520. (e) => e[1] == false
  521. );
  522. if (this.errors.length > 0) {
  523. return false;
  524. } else {
  525. return true;
  526. }
  527. },
  528. },
  529. };
  530. </script>
  531. <style scoped lang="scss" scoped>
  532. .success-alert {
  533. position: absolute;
  534. left: 13%;
  535. z-index: 10;
  536. top: -3%;
  537. }
  538. .success-alert-dialog {
  539. position: absolute;
  540. left: 13%;
  541. z-index: 10;
  542. top: 3%;
  543. }
  544. </style>