소스 검색

update i18n&userProfile

SyuanYu 1 년 전
부모
커밋
9e5188b055

+ 5 - 1
frontend/src/language/en.json

@@ -66,5 +66,9 @@
     "acceptZipMessage": "Video processing takes about 5-10 minutes, please be patient",
     "ytViews": "YouTube Views",
     "contactUs": "Contact Us",
-    "orderDetails": "Order Details"
+    "orderDetails": "Order Details",
+    "required": "This field is required.",
+    "emailRules": "Must be a valid e-mail.",
+    "phoneNumber": "Phone Number (optional)",
+    "feedback": "Feedback"
 }

+ 5 - 1
frontend/src/language/zh.json

@@ -66,5 +66,9 @@
     "acceptZipMessage": "影片處理需要約 5-10 分鐘,敬請耐心等候",
     "ytViews": "網紅加速器",
     "contactUs": "聯絡我們",
-    "orderDetails": "訂單明細"
+    "orderDetails": "訂單明細",
+    "required": "此為必填欄位",
+    "emailRules": "請輸入有效的電子郵件格式",
+    "phoneNumber": "手機號碼(非必填)",
+    "feedback": "意見回饋"
 }

+ 12 - 1
frontend/src/stores/main.ts

@@ -175,7 +175,7 @@ export const useMainStore = defineStore("MainStoreId", {
         }
       }
     },
-    async qrCheckLoggedIn() {
+    async cancelCheckLoggedIn() {
       if (!this.isLoggedIn) {
         let token = this.token;
         if (!token) {
@@ -497,5 +497,16 @@ export const useMainStore = defineStore("MainStoreId", {
         await mainStore.checkApiError(error);
       }
     },
+    async getYTViewsList() {
+      const mainStore = useMainStore();
+      try {
+        const response = await api.getYTViewsList();
+        if (response) {
+          return response.data;
+        }
+      } catch (error) {
+        await mainStore.checkApiError(error);
+      }
+    },
   }
 });

+ 17 - 6
frontend/src/utils.ts

@@ -6,14 +6,25 @@ export const removeLocalToken = () => localStorage.removeItem("token");
 
 
 import type { Ref } from "vue";
+import { useI18n } from "vue-i18n";
 
-export const emailRules =  [
-  (v:any) => /^[a-z.0-9]+@[a-z.-]+\.[a-z]+$/i.test(v) || '請輸入有效的電子郵件格式',
-];
+// export const emailRules =  [
+//   (v:any) => /^[a-z.0-9]+@[a-z.-]+\.[a-z]+$/i.test(v) || 'Must be a valid e-mail.',
+// ];
 
-export const required = [
-  (v:any) => !!v || 'This field is required.',
-];
+// export const required = [
+//   (v:any) => !!v || ('This field is required.'),
+// ];
+
+export function emailRules() {
+  const { t } = useI18n();
+  return [(v:any) => /^[a-z.0-9]+@[a-z.-]+\.[a-z]+$/i.test(v) || t('emailRules')];
+}
+
+export function required() {
+  const { t } = useI18n();
+  return [(v: any) => !!v || t('required')];
+}
 
 export const nameRules = [
   (v:any) => !!v || 'Name is required.',

+ 1 - 1
frontend/src/views/PasswordRecovery.vue

@@ -56,7 +56,7 @@ function submit() {
                 type="text"
                 prepend-icon="email"
                 v-model="email"
-                :rules="required"
+                :rules="required()"
                 required
               ></v-text-field>
             </v-form>

+ 1 - 1
frontend/src/views/Qrcode.vue

@@ -91,7 +91,7 @@ onMounted(() => {
   if (route.query["add_time_code"]) {
     ser_no.value = route.query["add_time_code"];
   }
-  mainStore.qrCheckLoggedIn();
+  mainStore.cancelCheckLoggedIn();
   if (mainStore.token) {
     checkCode("");
   } else {

+ 6 - 7
frontend/src/views/YTViews.vue

@@ -1,12 +1,11 @@
 <script setup lang="ts">
 import { ref, reactive, computed } from "vue";
-import type { YTViewsUserData } from "@/interfaces";
+import { required, emailRules } from "@/utils";
 import { useMainStore } from "@/stores/main";
-import { emailRules } from "@/utils";
+import type { YTViewsUserData } from "@/interfaces";
 import Navbar from "@/components/Navbar.vue";
 
 const mainStore = useMainStore();
-const fieldRules = [(value: string) => !!value || "此欄位為必填項目"];
 const urlRules = [
   (v:any) => /^(http|https):\/\//.test(v) || '請輸入以 http 或 https 開頭的有效網址',
 ];
@@ -272,13 +271,13 @@ async function ECPaySubmit() {
           <v-form @submit.prevent class="ECPay-form">
             <v-text-field
               v-model="userData.email"
-              :rules="emailRules"
+              :rules="emailRules()"
               label="電子郵件"
               required
             ></v-text-field>
             <v-text-field
               v-model="userData.name"
-              :rules="fieldRules"
+              :rules="required()"
               label="姓名"
               required
             ></v-text-field>
@@ -294,13 +293,13 @@ async function ECPaySubmit() {
             ></v-text-field>
             <v-text-field
               v-model="userData.area"
-              :rules="fieldRules"
+              :rules="required()"
               label="影片放送地區(國家 / 縣市)"
               required
             ></v-text-field>
             <v-text-field
               v-model="userData.language"
-              :rules="fieldRules"
+              :rules="required()"
               label="受眾語言"
               required
             ></v-text-field>

+ 1 - 1
frontend/src/views/main/Article.vue

@@ -33,7 +33,7 @@ async function Submit() {
           <v-text-field
             :label="$t('articleTitle')"
             v-model="title"
-            :rules="required"
+            :rules="required()"
             prepend-icon="title"
           >
           </v-text-field>

+ 3 - 6
frontend/src/views/main/Dashboard.vue

@@ -51,13 +51,13 @@ const greetedUser = computed(() => {
             <v-btn to="/main/profile/edit" variant="outlined" color="primary">{{
               t("edit")
             }}</v-btn>
-            <v-btn
+            <!-- <v-btn
               to="/main/profile/password"
               variant="flat"
               color="primary"
               class="ms-3"
               >{{ t("changePassword") }}</v-btn
-            >
+            > -->
           </v-card-actions>
           <!-- <v-card-actions>
         <v-btn to="/main/profile/view">View Profile</v-btn>
@@ -93,10 +93,7 @@ const greetedUser = computed(() => {
           <v-card-title primary-title>
             <h3>可使用秒數</h3>
           </v-card-title>
-          <v-card-text
-            v-if="userProfile"
-            class="mt-3"
-          >
+          <v-card-text v-if="userProfile" class="mt-3">
             <strong>{{ userProfile.available_time }}</strong
             ><small>秒</small>
           </v-card-text>

+ 2 - 2
frontend/src/views/main/Start.vue

@@ -27,9 +27,9 @@ const startRouteGuard = async (
   const mainStore = useMainStore();
   const mainStoreRef = storeToRefs(mainStore);
 
-  if (to.path === "/qrcode" || to.path === "/yt-views") {
+  if (to.path === "/qrcode" || to.path === "/yt-views" || to.path === "/orders") {
     next();
-    mainStore.qrCheckLoggedIn();
+    mainStore.cancelCheckLoggedIn();
   } else {
     mainStore.checkLoggedIn();
   }

+ 1 - 1
frontend/src/views/main/Upload.vue

@@ -140,7 +140,7 @@ async function Submit() {
           <v-text-field
             :label="$t('videoTitle')"
             v-model="title"
-            :rules="required"
+            :rules="required()"
             prepend-icon="title"
           >
           </v-text-field>

+ 1 - 1
frontend/src/views/main/admin/CreateUser.vue

@@ -7,7 +7,7 @@
       <v-card-text>
           <v-form v-model="valid" ref="form">
             <v-text-field label="Full Name" v-model="fullName" :rules="nameRules" required></v-text-field>
-            <v-text-field label="E-mail" type="email" v-model="email" :rules="emailRules" required></v-text-field>
+            <v-text-field label="E-mail" type="email" v-model="email" :rules="emailRules()" required></v-text-field>
             <div class="subheading secondary--text text--lighten-2">
               User is superuser 
               <span v-if="isSuperuser">(currently is a superuser)</span>

+ 1 - 1
frontend/src/views/main/admin/EditUser.vue

@@ -29,7 +29,7 @@
               label="E-mail"
               type="email"
               v-model="email"
-              :rules="emailRules"
+              :rules="emailRules()"
             ></v-text-field>
             <div class="subheading secondary--text text--lighten-2">User is superuser <span v-if="isSuperuser">(currently is a superuser)</span><span v-else>(currently is not a superuser)</span></div>
             <v-checkbox

+ 1 - 1
frontend/src/views/main/admin/TestCelery.vue

@@ -9,7 +9,7 @@
             <v-text-field 
               @keyup.enter="msgSubmit" 
               label="Message" v-model="msg" 
-              :rules="required"
+              :rules="required()"
               prepend-icon="message">
             </v-text-field>
           </v-form>

+ 69 - 18
frontend/src/views/main/profile/UserProfileEdit.vue

@@ -4,18 +4,28 @@ import { useMainStore } from "@/stores/main";
 import { storeToRefs } from "pinia";
 import router from "@/router";
 import type { IUserProfileUpdate } from "@/interfaces";
-import { emailRules, nameRules } from "@/utils";
+import {
+  emailRules,
+  nameRules,
+  password1Rules,
+  usePassword2Rules,
+} from "@/utils";
 import { useI18n } from "vue-i18n";
 
 const { t } = useI18n();
+const mainStore = useMainStore();
+const mainStoreRef = storeToRefs(mainStore);
+const userProfile = mainStoreRef.readUserProfile;
+
 const valid = ref(true);
+const validPassword = ref(true);
 const fullName = ref("");
 const email = ref("");
 const form = ref(null);
+const password1 = ref("");
+const password2 = ref("");
+const password2Rules = usePassword2Rules(password1);
 
-const mainStore = useMainStore();
-const mainStoreRef = storeToRefs(mainStore);
-const userProfile = mainStoreRef.readUserProfile;
 if (userProfile) {
   if (typeof userProfile.value?.full_name === "string") {
     fullName.value = userProfile.value.full_name;
@@ -36,10 +46,16 @@ function reset() {
   }
 }
 
+function resetPassword() {
+  password1.value = "";
+  password2.value = "";
+}
+
 function cancel() {
   router.back();
 }
 
+// 編輯資料
 async function submit() {
   await (form as any).value.validate();
   if (valid.value) {
@@ -51,13 +67,24 @@ async function submit() {
       updateProfile.email = email.value;
     }
     await mainStore.updateUserProfile(updateProfile);
-    router.push("/main/profile");
+    router.push("/main/dashboard");
+  }
+}
+
+// 變更密碼
+async function submitPassword() {
+  await (form as any).value.validate();
+  if (validPassword.value) {
+    const updatedProfile: IUserProfileUpdate = {};
+    updatedProfile.password = password1.value;
+    await mainStore.updateUserProfile(updatedProfile);
+    router.push("/main/dashboard");
   }
 }
 
-onMounted(() => {
-  (form as any).value.validate();
-});
+// onMounted(() => {
+//   (form as any).value.validate();
+// });
 </script>
 
 <template>
@@ -67,7 +94,7 @@ onMounted(() => {
         <h3 class="card-title">{{ t("editUserProfile") }}</h3>
       </v-card-title>
       <v-card-text class="pt-3">
-        <v-form v-model="valid" ref="form">
+        <v-form ref="form" v-model="valid">
           <v-text-field
             :label="$t('userName')"
             v-model="fullName"
@@ -78,7 +105,7 @@ onMounted(() => {
             :label="$t('emailAddress')"
             type="email"
             v-model="email"
-            :rules="emailRules"
+            :rules="emailRules()"
             required
           ></v-text-field>
         </v-form>
@@ -90,13 +117,37 @@ onMounted(() => {
         <v-btn @click="submit" :disabled="!valid">{{ t("save") }}</v-btn>
       </v-card-actions>
     </v-card>
+    <v-card class="mt-7 ma-3 pa-3">
+      <v-card-title primary-title class="mb-3">
+        <h3 class="headline primary--text">{{ t("changePassword") }}</h3>
+      </v-card-title>
+      <v-card-text>
+        <v-form ref="form" v-model="validPassword">
+          <v-text-field
+            type="password"
+            ref="password"
+            :label="$t('newPassword')"
+            v-model="password1"
+            :rules="password1Rules"
+          >
+          </v-text-field>
+          <v-text-field
+            type="password"
+            :label="$t('confirmNewPassword')"
+            :rules="password2Rules"
+            v-model="password2"
+          >
+          </v-text-field>
+        </v-form>
+      </v-card-text>
+      <v-card-actions>
+        <v-spacer></v-spacer>
+        <v-btn @click="cancel">{{ t("cancel") }}</v-btn>
+        <v-btn @click="resetPassword">{{ t("clear") }}</v-btn>
+        <v-btn @click="submitPassword" :disabled="!validPassword">{{
+          t("save")
+        }}</v-btn>
+      </v-card-actions>
+    </v-card>
   </v-container>
 </template>
-
-<style lang="scss" scoped>
-// .v-card-title {
-//   h3 {
-//     letter-spacing: 1px !important;
-//   }
-// }
-</style>