Commit a96c3fee87e3ab70dbf04c014c5530cd931a7c53

Authored by boyang
1 parent 5359076c

feat: 开发邮件发送,prod2-55/56

nuxt.config.ts
@@ -8,13 +8,16 @@ export default defineNuxtConfig({ @@ -8,13 +8,16 @@ export default defineNuxtConfig({
8 link: [{ rel: "icon", type: "image/x-icon", href: "/fav.ico" }], 8 link: [{ rel: "icon", type: "image/x-icon", href: "/fav.ico" }],
9 }, 9 },
10 }, 10 },
  11 +
11 css: ["~/assets/css/main.css"], 12 css: ["~/assets/css/main.css"],
  13 +
12 postcss: { 14 postcss: {
13 plugins: { 15 plugins: {
14 tailwindcss: {}, 16 tailwindcss: {},
15 autoprefixer: {}, 17 autoprefixer: {},
16 }, 18 },
17 }, 19 },
  20 +
18 // runtimeConfig: { 21 // runtimeConfig: {
19 // public: { 22 // public: {
20 // baseURL: 'http://47.89.254.121:8002/shop' || 'http://39.108.227.113:8002' // Exposed to the frontend as well. 23 // baseURL: 'http://47.89.254.121:8002/shop' || 'http://39.108.227.113:8002' // Exposed to the frontend as well.
@@ -26,10 +29,12 @@ export default defineNuxtConfig({ @@ -26,10 +29,12 @@ export default defineNuxtConfig({
26 "@nuxtjs/i18n", 29 "@nuxtjs/i18n",
27 "@stefanobartoletti/nuxt-social-share", 30 "@stefanobartoletti/nuxt-social-share",
28 ], 31 ],
  32 +
29 // optional configuration, should be added manually 33 // optional configuration, should be added manually
30 socialShare: { 34 socialShare: {
31 // module options 35 // module options
32 }, 36 },
  37 +
33 vuetify: { 38 vuetify: {
34 moduleOptions: { 39 moduleOptions: {
35 /* module specific options */ 40 /* module specific options */
@@ -47,6 +52,7 @@ export default defineNuxtConfig({ @@ -47,6 +52,7 @@ export default defineNuxtConfig({
47 /* vuetify options */ 52 /* vuetify options */
48 }, 53 },
49 }, 54 },
  55 +
50 nitro: { 56 nitro: {
51 devProxy: { 57 devProxy: {
52 "/shop": { 58 "/shop": {
@@ -55,6 +61,12 @@ export default defineNuxtConfig({ @@ -55,6 +61,12 @@ export default defineNuxtConfig({
55 // target: process.env.BASE_URL || 'http://39.108.227.113:8002/shop', // 目标接口域名 61 // target: process.env.BASE_URL || 'http://39.108.227.113:8002/shop', // 目标接口域名
56 changeOrigin: true, // 表示是否跨域 62 changeOrigin: true, // 表示是否跨域
57 }, 63 },
  64 + "/email": {
  65 + target: "http://47.89.254.121:8002/email", // 线上代理地址
  66 + // target: "http://127.0.0.1:8002/shop",
  67 + // target: process.env.BASE_URL || 'http://39.108.227.113:8002/shop', // 目标接口域名
  68 + changeOrigin: true, // 表示是否跨域
  69 + },
58 "/api/front/cal": { 70 "/api/front/cal": {
59 target: "http://www.canrd.com/mshop/api/front/cal", 71 target: "http://www.canrd.com/mshop/api/front/cal",
60 // proxy: "http://127.0.0.1:8002/shop/**", 72 // proxy: "http://127.0.0.1:8002/shop/**",
@@ -69,6 +81,11 @@ export default defineNuxtConfig({ @@ -69,6 +81,11 @@ export default defineNuxtConfig({
69 // proxy: "http://127.0.0.1:8002/shop/**", 81 // proxy: "http://127.0.0.1:8002/shop/**",
70 // proxy: process.env.BASE_URL || 'http://39.108.227.113:8002/shop/**' 82 // proxy: process.env.BASE_URL || 'http://39.108.227.113:8002/shop/**'
71 }, 83 },
  84 + "/email/**": {
  85 + proxy: "http://47.89.254.121:8002/email/**",
  86 + // proxy: "http://127.0.0.1:8002/shop/**",
  87 + // proxy: process.env.BASE_URL || 'http://39.108.227.113:8002/shop/**'
  88 + },
72 89
73 "/api/front/cal/**": { 90 "/api/front/cal/**": {
74 proxy: "http://www.canrd.com/mshop/api/front/cal/**", 91 proxy: "http://www.canrd.com/mshop/api/front/cal/**",
@@ -77,6 +94,7 @@ export default defineNuxtConfig({ @@ -77,6 +94,7 @@ export default defineNuxtConfig({
77 }, 94 },
78 }, 95 },
79 }, 96 },
  97 +
80 i18n: { 98 i18n: {
81 locales: [ 99 locales: [
82 { code: "en", iso: "en-US", file: "en.json" }, 100 { code: "en", iso: "en-US", file: "en.json" },
@@ -86,4 +104,6 @@ export default defineNuxtConfig({ @@ -86,4 +104,6 @@ export default defineNuxtConfig({
86 langDir: "locales/", 104 langDir: "locales/",
87 defaultLocale: "zh", 105 defaultLocale: "zh",
88 }, 106 },
  107 +
  108 + compatibilityDate: "2024-12-30",
89 }); 109 });
package-lock.json
@@ -9,6 +9,8 @@ @@ -9,6 +9,8 @@
9 "dependencies": { 9 "dependencies": {
10 "@pinia/nuxt": "^0.5.1", 10 "@pinia/nuxt": "^0.5.1",
11 "@stefanobartoletti/nuxt-social-share": "^1.2.0", 11 "@stefanobartoletti/nuxt-social-share": "^1.2.0",
  12 + "@vuelidate/core": "^2.0.3",
  13 + "@vuelidate/validators": "^2.0.4",
12 "lodash": "^4.17.21", 14 "lodash": "^4.17.21",
13 "nuxt": "^3.11.2", 15 "nuxt": "^3.11.2",
14 "vue": "^3.4.27", 16 "vue": "^3.4.27",
@@ -2877,6 +2879,90 @@ @@ -2877,6 +2879,90 @@
2877 "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.12.tgz", 2879 "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.12.tgz",
2878 "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==" 2880 "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg=="
2879 }, 2881 },
  2882 + "node_modules/@vuelidate/core": {
  2883 + "version": "2.0.3",
  2884 + "resolved": "https://registry.npmmirror.com/@vuelidate/core/-/core-2.0.3.tgz",
  2885 + "integrity": "sha512-AN6l7KF7+mEfyWG0doT96z+47ljwPpZfi9/JrNMkOGLFv27XVZvKzRLXlmDPQjPl/wOB1GNnHuc54jlCLRNqGA==",
  2886 + "dependencies": {
  2887 + "vue-demi": "^0.13.11"
  2888 + },
  2889 + "peerDependencies": {
  2890 + "@vue/composition-api": "^1.0.0-rc.1",
  2891 + "vue": "^2.0.0 || >=3.0.0"
  2892 + },
  2893 + "peerDependenciesMeta": {
  2894 + "@vue/composition-api": {
  2895 + "optional": true
  2896 + }
  2897 + }
  2898 + },
  2899 + "node_modules/@vuelidate/core/node_modules/vue-demi": {
  2900 + "version": "0.13.11",
  2901 + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
  2902 + "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
  2903 + "hasInstallScript": true,
  2904 + "bin": {
  2905 + "vue-demi-fix": "bin/vue-demi-fix.js",
  2906 + "vue-demi-switch": "bin/vue-demi-switch.js"
  2907 + },
  2908 + "engines": {
  2909 + "node": ">=12"
  2910 + },
  2911 + "funding": {
  2912 + "url": "https://github.com/sponsors/antfu"
  2913 + },
  2914 + "peerDependencies": {
  2915 + "@vue/composition-api": "^1.0.0-rc.1",
  2916 + "vue": "^3.0.0-0 || ^2.6.0"
  2917 + },
  2918 + "peerDependenciesMeta": {
  2919 + "@vue/composition-api": {
  2920 + "optional": true
  2921 + }
  2922 + }
  2923 + },
  2924 + "node_modules/@vuelidate/validators": {
  2925 + "version": "2.0.4",
  2926 + "resolved": "https://registry.npmmirror.com/@vuelidate/validators/-/validators-2.0.4.tgz",
  2927 + "integrity": "sha512-odTxtUZ2JpwwiQ10t0QWYJkkYrfd0SyFYhdHH44QQ1jDatlZgTh/KRzrWVmn/ib9Gq7H4hFD4e8ahoo5YlUlDw==",
  2928 + "dependencies": {
  2929 + "vue-demi": "^0.13.11"
  2930 + },
  2931 + "peerDependencies": {
  2932 + "@vue/composition-api": "^1.0.0-rc.1",
  2933 + "vue": "^2.0.0 || >=3.0.0"
  2934 + },
  2935 + "peerDependenciesMeta": {
  2936 + "@vue/composition-api": {
  2937 + "optional": true
  2938 + }
  2939 + }
  2940 + },
  2941 + "node_modules/@vuelidate/validators/node_modules/vue-demi": {
  2942 + "version": "0.13.11",
  2943 + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
  2944 + "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
  2945 + "hasInstallScript": true,
  2946 + "bin": {
  2947 + "vue-demi-fix": "bin/vue-demi-fix.js",
  2948 + "vue-demi-switch": "bin/vue-demi-switch.js"
  2949 + },
  2950 + "engines": {
  2951 + "node": ">=12"
  2952 + },
  2953 + "funding": {
  2954 + "url": "https://github.com/sponsors/antfu"
  2955 + },
  2956 + "peerDependencies": {
  2957 + "@vue/composition-api": "^1.0.0-rc.1",
  2958 + "vue": "^3.0.0-0 || ^2.6.0"
  2959 + },
  2960 + "peerDependenciesMeta": {
  2961 + "@vue/composition-api": {
  2962 + "optional": true
  2963 + }
  2964 + }
  2965 + },
2880 "node_modules/@vuetify/loader-shared": { 2966 "node_modules/@vuetify/loader-shared": {
2881 "version": "2.0.3", 2967 "version": "2.0.3",
2882 "resolved": "https://registry.npmmirror.com/@vuetify/loader-shared/-/loader-shared-2.0.3.tgz", 2968 "resolved": "https://registry.npmmirror.com/@vuetify/loader-shared/-/loader-shared-2.0.3.tgz",
package.json
@@ -12,6 +12,8 @@ @@ -12,6 +12,8 @@
12 "dependencies": { 12 "dependencies": {
13 "@pinia/nuxt": "^0.5.1", 13 "@pinia/nuxt": "^0.5.1",
14 "@stefanobartoletti/nuxt-social-share": "^1.2.0", 14 "@stefanobartoletti/nuxt-social-share": "^1.2.0",
  15 + "@vuelidate/core": "^2.0.3",
  16 + "@vuelidate/validators": "^2.0.4",
15 "lodash": "^4.17.21", 17 "lodash": "^4.17.21",
16 "nuxt": "^3.11.2", 18 "nuxt": "^3.11.2",
17 "vue": "^3.4.27", 19 "vue": "^3.4.27",
pages/about.vue
1 <template> 1 <template>
2 <HotProducts /> 2 <HotProducts />
3 3
4 - <!-- <v-tabs  
5 - class="tabs"  
6 - v-model="tabRecom"  
7 - style="margin-top: 25px"  
8 - color="white"  
9 - bg-color="#eeeeee"  
10 - slider-color="blue-lighten-1"  
11 - selected-class="active"  
12 - v-if="recommendImages[0] !== null && !isMobile()"  
13 - >  
14 - <v-tab :value="1">Best Sellers</v-tab>  
15 - </v-tabs>  
16 - <div id="image-container" v-if="recommendImages[0] !== null && !isMobile()">  
17 - <div class="recommend-left-box">  
18 - <v-img  
19 - src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/76c987e13a334be481a346c19c2284f3qy4tjnxps7.png"  
20 - alt="往左移"  
21 - class="recommend-img-left"  
22 - @click="toggleRowLeft"  
23 - />  
24 - </div>  
25 - <div class="image-row" id="row1">  
26 - <div  
27 - v-for="(imageObj, index) in recommendImages"  
28 - :key="'row1-' + index"  
29 - class="imageTotal"  
30 - >  
31 - <a v-if="imageObj" :href="imageObj[0]?.productUrl" target="_blank">  
32 - <img  
33 - :src="imageObj[0]?.url"  
34 - :alt="'Image ' + (index + 1)"  
35 - class="item-imgHot"  
36 - />  
37 - <span class="image-name">  
38 - {{ imageObj[0]?.name }}  
39 - </span>  
40 - </a>  
41 - <div v-else style="width: 200px; height: 200px"></div>  
42 - </div>  
43 - </div>  
44 - <div class="recommend-right-box">  
45 - <v-img  
46 - src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/b5daa0a8f2f140f5b406e984c730a453iznzlekysg.png"  
47 - alt="往右移"  
48 - class="recommend-img-right"  
49 - @click="toggleRowRight"  
50 - />  
51 - </div>  
52 - </div>  
53 - <div style="padding-left: 16px; padding-right: 16px; margin-bottom: 30px">  
54 - <v-tabs  
55 - class="tabs2"  
56 - ref="tabs2"  
57 - v-model="tabRecom"  
58 - style="margin-top: 25px; margin-bottom: 20px"  
59 - color="white"  
60 - bg-color="#eeeeee"  
61 - slider-color="blue-lighten-1"  
62 - selected-class="active"  
63 - v-if="isMobile()"  
64 - >  
65 - <v-tab :value="1">Best Sellers</v-tab>  
66 - </v-tabs>  
67 - <div class="tw-text-center" v-if="hotLoading && isMobile()">  
68 - <v-progress-circular  
69 - color="blue-lighten-2"  
70 - indeterminate  
71 - size="64"  
72 - class="tw-m-auto"  
73 - ></v-progress-circular>  
74 - </div>  
75 - <v-item-group multiple v-if="isMobile()">  
76 - <v-row v-if="!hotLoading">  
77 - <v-col  
78 - v-for="(item, i) in recommendImages"  
79 - :key="i"  
80 - cols="6"  
81 - lg="3"  
82 - md="4"  
83 - sm="6"  
84 - >  
85 - <div v-if="item !== null">  
86 - <v-card :elevation="4" class="mx-auto" :href="item[0].productUrl">  
87 - <v-img  
88 - :src="item[0].url"  
89 - :alt="item[0].name"  
90 - eager  
91 - class="d-block"  
92 - />  
93 - <v-card-text class="tw-text-left font-weight-medium title">  
94 - <h4>{{ item[0].name }}</h4>  
95 - </v-card-text>  
96 - </v-card>  
97 - </div>  
98 - </v-col>  
99 - </v-row>  
100 - </v-item-group>  
101 - <v-row v-if="isMobile()">  
102 - <v-col>  
103 - <v-pagination  
104 - :size="isMobile() ? 'small' : 'default'"  
105 - v-if="hotTotal"  
106 - v-model="currentIndex"  
107 - @update:modelValue="toggleRowMobile"  
108 - :length="hotLength"  
109 - rounded="0"  
110 - class="tw-float-right tw-mt-[32px]"  
111 - total-visible="5"  
112 - ></v-pagination></v-col  
113 - ></v-row>  
114 - </div> -->  
115 <v-container class="pa-0 pa-sm-4"> 4 <v-container class="pa-0 pa-sm-4">
116 <div 5 <div
117 class="my-8 my-sm-16 text-blue-darken-1 text-h4 tw-text-center" 6 class="my-8 my-sm-16 text-blue-darken-1 text-h4 tw-text-center"
@@ -579,117 +468,6 @@ import MainTitle from &quot;../components/MainTitle.vue&quot;; @@ -579,117 +468,6 @@ import MainTitle from &quot;../components/MainTitle.vue&quot;;
579 import HotProducts from "../components/HotProducts.vue"; 468 import HotProducts from "../components/HotProducts.vue";
580 import { isMobile } from "../utils"; 469 import { isMobile } from "../utils";
581 470
582 -const productStore = useProductListStore();  
583 -const categoryStore = useCategoryStore();  
584 -const loading = ref(false);  
585 -const hotLoading = ref(false);  
586 -const route = useRoute(); // 获取路由信息  
587 -const router = useRouter(); // 获取路由信息  
588 -const title = ref("");  
589 -const keywordTitle = ref("");  
590 -const maxPage = ref(1);  
591 -const tabRecom = ref();  
592 -const recommendList = ref();  
593 -const recommendImages = ref();  
594 -const currentIndex = ref(1);  
595 -const hotTotal = ref(10);  
596 -const isOrNotMobile = isMobile();  
597 -  
598 -// const loadHotProducts = async () => {  
599 -// const pageSize = ref(5);  
600 -// if (isOrNotMobile) {  
601 -// pageSize.value = 4;  
602 -// }  
603 -// hotLoading.value = true;  
604 -// let { data: hotProducts } = await useAsyncData(  
605 -// "hotProducts",  
606 -// () =>  
607 -// $fetch("/shop/product/hotProducts", {  
608 -// method: "GET",  
609 -// params: {  
610 -// pageNo: currentIndex.value,  
611 -// pageSize: pageSize.value,  
612 -// },  
613 -// }),  
614 -// {  
615 -// server: true, // 仅在服务器端获取数据  
616 -// }  
617 -// );  
618 -// hotTotal.value = hotProducts.value.data.total;  
619 -// recommendList.value = hotProducts.value.data.records;  
620 -// maxPage.value = hotProducts.value.data.pages;  
621 -// // recommendImages.value = recommendList.value.slice(0, 10).map((item) => {  
622 -// recommendImages.value = Array.from({ length: pageSize.value }).map(  
623 -// (_, index) => {  
624 -// const item = recommendList.value[index];  
625 -// if (!item) {  
626 -// return null;  
627 -// }  
628 -// // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析  
629 -// if (typeof item.productimageliststore === "string") {  
630 -// try {  
631 -// item.productimageliststore = JSON.parse(item.productimageliststore);  
632 -// } catch (error) {  
633 -// item.productimageliststore = []; // 解析失败时,设置为空数组  
634 -// }  
635 -// }  
636 -// const ree = (item.productimageliststore = item?.productimageliststore.map(  
637 -// (productItem: ProductImage) => ({  
638 -// ...productItem,  
639 -// // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`,  
640 -// url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`,  
641 -// name: item.name,  
642 -// productUrl: `https://www.canrud.com/products/detail/${item.id}`,  
643 -// })  
644 -// ));  
645 -// return ree;  
646 -// }  
647 -// );  
648 -// hotLoading.value = false;  
649 -// };  
650 -  
651 -// let intervalId: any;  
652 -// const toggleRowLeft = () => {  
653 -// if (currentIndex.value !== 1) {  
654 -// currentIndex.value--;  
655 -// } else if (currentIndex.value == 1) {  
656 -// currentIndex.value = maxPage.value;  
657 -// }  
658 -// startTimer();  
659 -// };  
660 -// const toggleRowRight = () => {  
661 -// if (currentIndex.value < maxPage.value) {  
662 -// currentIndex.value++;  
663 -// } else if (currentIndex.value == maxPage.value) {  
664 -// currentIndex.value = 1;  
665 -// }  
666 -// startTimer();  
667 -// };  
668 -// const startTimer = () => {  
669 -// // 清除已有计时器,防止重复  
670 -// clearInterval(intervalId);  
671 -// intervalId = setInterval(() => {  
672 -// toggleRowRight();  
673 -// }, 5000); // 每6秒调用一次  
674 -// };  
675 -  
676 -// onMounted(() => {  
677 -// startTimer();  
678 -// });  
679 -// const toggleRowMobile = (value: number) => {  
680 -// currentIndex.value = value;  
681 -// };  
682 -  
683 -// const hotLength = computed(() =>  
684 -// hotTotal.value ? Math.ceil(hotTotal.value / 4) : 0  
685 -// );  
686 -  
687 -// watch(currentIndex, (newIndex) => {  
688 -// loadHotProducts(); // Call loadHotProducts when currentIndex changes  
689 -// });  
690 -// // Initial load of hot products  
691 -// await loadHotProducts(); // Load hot products the first time  
692 -  
693 useHead({ 471 useHead({
694 title: "About Us", 472 title: "About Us",
695 meta: [ 473 meta: [
pages/contact.vue
@@ -3,12 +3,12 @@ @@ -3,12 +3,12 @@
3 Contact Us 3 Contact Us
4 </div> 4 </div>
5 <v-card class="pa-10 tw-max-w-[800px] tw-m-auto"> 5 <v-card class="pa-10 tw-max-w-[800px] tw-m-auto">
6 - <!-- <h3 class="text-h5 tw-mb-5">Official Web</h3> 6 + <h3 class="text-h5 tw-mb-5">Official Web</h3>
7 <div class="tw-mb-10"> 7 <div class="tw-mb-10">
8 <label class="text-subtitle-1 tw-mr-4 tw-w-20 tw-inline-block">URL</label> 8 <label class="text-subtitle-1 tw-mr-4 tw-w-20 tw-inline-block">URL</label>
9 <span>http://www.canrud.com</span> 9 <span>http://www.canrud.com</span>
10 </div> 10 </div>
11 - <h3 class="text-h5 tw-mb-5">Technical Center</h3> --> 11 + <h3 class="text-h5 tw-mb-5">Technical Center</h3>
12 <!-- <div> 12 <!-- <div>
13 <label class="text-subtitle-1 tw-mr-4 tw-w-20 tw-inline-block">QQ</label> 13 <label class="text-subtitle-1 tw-mr-4 tw-w-20 tw-inline-block">QQ</label>
14 <span>3632191327</span> 14 <span>3632191327</span>
@@ -71,30 +71,30 @@ @@ -71,30 +71,30 @@
71 href="https://www.amazon.com/s?me=A3A2SQ086XUS66&marketplaceID=ATVPDKIKX0DER" 71 href="https://www.amazon.com/s?me=A3A2SQ086XUS66&marketplaceID=ATVPDKIKX0DER"
72 rel="noopener noreferrer" 72 rel="noopener noreferrer"
73 style="display: flex; align-items: center; gap: 8px" 73 style="display: flex; align-items: center; gap: 8px"
74 - ><svg  
75 - t="1733207196625" 74 + >
  75 + <svg
76 class="icon" 76 class="icon"
77 viewBox="0 0 1126 1024" 77 viewBox="0 0 1126 1024"
78 - version="1.1"  
79 - xmlns="http://www.w3.org/2000/svg"  
80 - p-id="7890"  
81 width="20" 78 width="20"
82 height="20" 79 height="20"
  80 + aria-hidden="true"
83 > 81 >
84 <path 82 <path
85 d="M2.008553 794.830142c3.382073-5.461063 8.7738-5.813248 16.349072-1.021335q256.233047 148.673652 557.663492 148.72648 200.998387 0 396.851576-74.949297l14.798359-6.589155c6.483499-2.818578 10.993664-4.686257 13.777024-6.096097 10.606261-4.122762 18.322407-2.149427 24.665032 6.096097 5.637156 8.173986 4.228417 15.785577-5.637156 22.550824-12.015 8.914674-28.187979 19.273305-47.249974 30.724809q-87.681873 52.376462-196.611486 81.07621c-71.879788 19.061995-143.054106 28.645819-212.149434 28.645819q-159.614488 0-302.316409-55.777245A840.520599 840.520599 0 0 1 7.046995 810.829231c-4.686257-3.488829-7.043693-7.043693-7.043693-10.323413a9.795136 9.795136 0 0 1 2.075688-5.745012z m308.411405-292.13277q0-70.822133 34.918008-121.063475c23.255193-33.367295 54.96612-58.736697 95.839351-75.860575 37.384401-15.750358 82.484948-27.02577 136.817135-33.825135 18.322407-2.149427 48.518939-4.827131 90.201094-8.173986v-17.37591c0-43.692909-4.932786-73.188374-14.09399-88.087986-14.197444-20.190086-36.649216-30.548717-67.651371-30.548718h-8.562489c-22.550824 2.149427-42.105877 9.196422-58.525386 21.598825-16.454727 12.684151-27.02577 29.597819-31.710926 51.478391-2.818578 14.09399-9.685078 21.845354-20.436616 23.959562l-118.389073-14.798359c-11.666117-2.818578-17.476063-8.456834-17.476063-18.322406a28.0372 28.0372 0 0 1 1.021336-7.043693q17.389117-90.907664 85.514836-135.302742C463.72704 20.401397 516.544833 3.522947 577.149209 0h25.369401c77.516943 0 138.931344 20.401397 182.658371 60.604376 6.342626 7.043693 12.684151 14.09399 19.026776 22.550824 5.637156 7.751364 10.535824 14.763141 13.283965 21.140984a90.185686 90.185686 0 0 1 9.161204 26.77814c2.818578 11.944563 4.932786 19.731145 6.342625 23.959563 1.409839 4.897568 2.924233 14.09399 3.559266 28.892349 0.45784 14.692704 0.950899 23.149538 0.950899 25.973618v248.049156a153.121084 153.121084 0 0 0 7.751364 48.659813c4.932786 14.692704 9.865573 25.369402 14.798359 31.675708l23.959562 31.675709c4.228417 6.377844 6.377844 12.015 6.377844 16.912567 0 5.637156-2.818578 10.606261-8.456834 14.763141-56.375959 49.328964-87.385818 76.107104-92.209647 80.335521q-11.627597 9.513388-29.597818 2.114208a284.646645 284.646645 0 0 1-24.700251-23.290411l-14.549629-16.312753c-2.818578-3.487729-7.786583-9.865573-14.904014-19.731146l-14.096191-20.436615c-38.053552 41.612818-75.297079 67.651371-112.751918 78.221313-23.222176 7.043693-51.337517 10.675597-85.973777 10.675597-52.150844 0.001101-95.843752-16.102543-129.664485-48.587175-33.825135-32.416396-50.736602-78.221313-50.736602-138.12242z m176.312444-20.58079c0 26.602048 6.589155 47.919125 19.978775 64.092104 13.38962 15.958367 31.710927 24.065218 54.258449 24.065218a65.995002 65.995002 0 0 0 9.161203-0.950899 54.539096 54.539096 0 0 1 7.786583-1.091772c28.85713-7.504835 50.736602-25.973618 66.915084-55.359026a147.597288 147.597288 0 0 0 16.912568-42.739809 183.627979 183.627979 0 0 0 6.342625-37.595712c0.704369-9.161203 0.704369-25.369402 0.70437-47.214755v-25.361698c-39.463391 0-69.730361 2.818578-90.201094 8.456834-59.898906 16.912568-90.201094 54.96612-90.201094 114.161757z m430.435684 330.116985a34.135498 34.135498 0 0 1 6.201752-7.997893c17.018223-11.416286 33.543387-19.273305 49.328964-23.501722 25.827242-6.23697 51.196643-10.429068 75.719701-11.275412a62.565604 62.565604 0 0 1 19.273305 1.409839c30.548717 2.818578 49.328964 7.892238 55.071775 15.503829 2.959452 4.228417 4.228417 10.711916 4.228417 18.322407v7.043693c0 23.959562-6.589155 52.147542-19.480213 84.563938-13.072654 32.416396-31.18265 58.631041-54.296969 78.926783-3.417292 2.818578-6.589155 4.228417-9.266859 4.228417a10.956245 10.956245 0 0 1-4.228417-0.563496c-4.228417-2.07899-5.038442-5.637156-2.99467-11.275412 25.362798-59.190134 37.875259-100.659878 37.875258-124.022927 0-7.043693-1.409839-12.684151-4.087543-16.17298-6.80817-7.788784-25.830543-12.081034-57.507352-12.081034q-17.123878 0-40.87213 2.149427c-17.058944 2.114209-32.874237 4.228417-46.968226 6.342625-4.228417 0-6.941339-0.669151-8.456834-2.07899-1.409839-1.409839-1.691587-2.219864-0.950899-3.631904a7.546657 7.546657 0 0 1 0.950899-2.959452z" 83 d="M2.008553 794.830142c3.382073-5.461063 8.7738-5.813248 16.349072-1.021335q256.233047 148.673652 557.663492 148.72648 200.998387 0 396.851576-74.949297l14.798359-6.589155c6.483499-2.818578 10.993664-4.686257 13.777024-6.096097 10.606261-4.122762 18.322407-2.149427 24.665032 6.096097 5.637156 8.173986 4.228417 15.785577-5.637156 22.550824-12.015 8.914674-28.187979 19.273305-47.249974 30.724809q-87.681873 52.376462-196.611486 81.07621c-71.879788 19.061995-143.054106 28.645819-212.149434 28.645819q-159.614488 0-302.316409-55.777245A840.520599 840.520599 0 0 1 7.046995 810.829231c-4.686257-3.488829-7.043693-7.043693-7.043693-10.323413a9.795136 9.795136 0 0 1 2.075688-5.745012z m308.411405-292.13277q0-70.822133 34.918008-121.063475c23.255193-33.367295 54.96612-58.736697 95.839351-75.860575 37.384401-15.750358 82.484948-27.02577 136.817135-33.825135 18.322407-2.149427 48.518939-4.827131 90.201094-8.173986v-17.37591c0-43.692909-4.932786-73.188374-14.09399-88.087986-14.197444-20.190086-36.649216-30.548717-67.651371-30.548718h-8.562489c-22.550824 2.149427-42.105877 9.196422-58.525386 21.598825-16.454727 12.684151-27.02577 29.597819-31.710926 51.478391-2.818578 14.09399-9.685078 21.845354-20.436616 23.959562l-118.389073-14.798359c-11.666117-2.818578-17.476063-8.456834-17.476063-18.322406a28.0372 28.0372 0 0 1 1.021336-7.043693q17.389117-90.907664 85.514836-135.302742C463.72704 20.401397 516.544833 3.522947 577.149209 0h25.369401c77.516943 0 138.931344 20.401397 182.658371 60.604376 6.342626 7.043693 12.684151 14.09399 19.026776 22.550824 5.637156 7.751364 10.535824 14.763141 13.283965 21.140984a90.185686 90.185686 0 0 1 9.161204 26.77814c2.818578 11.944563 4.932786 19.731145 6.342625 23.959563 1.409839 4.897568 2.924233 14.09399 3.559266 28.892349 0.45784 14.692704 0.950899 23.149538 0.950899 25.973618v248.049156a153.121084 153.121084 0 0 0 7.751364 48.659813c4.932786 14.692704 9.865573 25.369402 14.798359 31.675708l23.959562 31.675709c4.228417 6.377844 6.377844 12.015 6.377844 16.912567 0 5.637156-2.818578 10.606261-8.456834 14.763141-56.375959 49.328964-87.385818 76.107104-92.209647 80.335521q-11.627597 9.513388-29.597818 2.114208a284.646645 284.646645 0 0 1-24.700251-23.290411l-14.549629-16.312753c-2.818578-3.487729-7.786583-9.865573-14.904014-19.731146l-14.096191-20.436615c-38.053552 41.612818-75.297079 67.651371-112.751918 78.221313-23.222176 7.043693-51.337517 10.675597-85.973777 10.675597-52.150844 0.001101-95.843752-16.102543-129.664485-48.587175-33.825135-32.416396-50.736602-78.221313-50.736602-138.12242z m176.312444-20.58079c0 26.602048 6.589155 47.919125 19.978775 64.092104 13.38962 15.958367 31.710927 24.065218 54.258449 24.065218a65.995002 65.995002 0 0 0 9.161203-0.950899 54.539096 54.539096 0 0 1 7.786583-1.091772c28.85713-7.504835 50.736602-25.973618 66.915084-55.359026a147.597288 147.597288 0 0 0 16.912568-42.739809 183.627979 183.627979 0 0 0 6.342625-37.595712c0.704369-9.161203 0.704369-25.369402 0.70437-47.214755v-25.361698c-39.463391 0-69.730361 2.818578-90.201094 8.456834-59.898906 16.912568-90.201094 54.96612-90.201094 114.161757z m430.435684 330.116985a34.135498 34.135498 0 0 1 6.201752-7.997893c17.018223-11.416286 33.543387-19.273305 49.328964-23.501722 25.827242-6.23697 51.196643-10.429068 75.719701-11.275412a62.565604 62.565604 0 0 1 19.273305 1.409839c30.548717 2.818578 49.328964 7.892238 55.071775 15.503829 2.959452 4.228417 4.228417 10.711916 4.228417 18.322407v7.043693c0 23.959562-6.589155 52.147542-19.480213 84.563938-13.072654 32.416396-31.18265 58.631041-54.296969 78.926783-3.417292 2.818578-6.589155 4.228417-9.266859 4.228417a10.956245 10.956245 0 0 1-4.228417-0.563496c-4.228417-2.07899-5.038442-5.637156-2.99467-11.275412 25.362798-59.190134 37.875259-100.659878 37.875258-124.022927 0-7.043693-1.409839-12.684151-4.087543-16.17298-6.80817-7.788784-25.830543-12.081034-57.507352-12.081034q-17.123878 0-40.87213 2.149427c-17.058944 2.114209-32.874237 4.228417-46.968226 6.342625-4.228417 0-6.941339-0.669151-8.456834-2.07899-1.409839-1.409839-1.691587-2.219864-0.950899-3.631904a7.546657 7.546657 0 0 1 0.950899-2.959452z"
86 fill="#FF9900" 84 fill="#FF9900"
87 - p-id="7891"  
88 - ></path></svg  
89 - >Amazon</a  
90 - > 85 + ></path>
  86 + </svg>
  87 + Amazon
  88 + </a>
91 </span> 89 </span>
  90 +
92 <span class="tw-mt-2"> 91 <span class="tw-mt-2">
93 <a 92 <a
94 href="https://canrd.en.alibaba.com/company_profile.html?spm=a2700.galleryofferlist.normal_offer.d_companyName.262213a0fqshG2" 93 href="https://canrd.en.alibaba.com/company_profile.html?spm=a2700.galleryofferlist.normal_offer.d_companyName.262213a0fqshG2"
95 rel="noopener noreferrer" 94 rel="noopener noreferrer"
96 style="display: flex; align-items: center; gap: 8px" 95 style="display: flex; align-items: center; gap: 8px"
97 - ><svg 96 + >
  97 + <svg
98 t="1733207242907" 98 t="1733207242907"
99 class="icon" 99 class="icon"
100 viewBox="0 0 1651 1024" 100 viewBox="0 0 1651 1024"
@@ -108,9 +108,10 @@ @@ -108,9 +108,10 @@
108 d="M972.403613 749.997419c-59.986581 4.195097-54.172903-27.912258-18.531097-74.520774 81.259355-108.378839 231.787355-255.636645 238.558968-363.22271 9.348129-139.660387-131.138065-182.899613-275.819355-182.899612-100.64929 2.576516-204.833032 30.488774-275.819355 55.824516-244.504774 86.280258-397.708387 221.745548-494.988387 374.189419-100.64929 150.627097-69.367742 295.473548 148.050581 299.668645 164.203355-6.771613 275.026581-52.422194 386.64258-109.997419 0.792774 0-310.503226 88.856774-425.653677 23.717161-12.750452-6.804645-25.335742-16.152774-28.738065-42.28129 0-53.380129 88.097032-109.171613 139.726452-126.942968v-91.43329c104.018581 36.434581 226.733419 26.293677 331.742968-51.62942 3.402323 9.381161 6.771613 21.140645 5.945806 33.891097h17.771355c4.195097-36.467613-20.314839-71.944258-60.977548-74.520774 11.792516 9.348129 20.314839 16.945548 24.509935 23.717161l-1.585548 1.618581-0.825807 0.792774c-135.300129 94.835613-266.603355 50.803613-279.188645 48.227097l75.313549-73.728-21.140646-53.380129c149.867355-52.422194 273.408-90.640516 478.901678-126.909936l-45.980903-37.128258 23.717161-14.336c121.756903 33.858065 203.875097 59.193806 199.68 123.540645-1.618581 10.96671-5.945806 23.717161-12.750452 37.260388-36.302452 71.944258-142.897548 187.920516-186.136774 237.898322-27.879226 33.06529-55.824516 63.554065-75.313548 94.042839-21.933419 31.281548-33.032258 60.151742-33.858065 86.280258 4.195097 212.595613 631.279484-99.823484 754.820129-182.106839-180.157935 77.09729-375.642839 150.82529-588.07329 164.368516z m137.083871-488.547096c4.558452 8.390194 6.639484 18.696258 6.639484 30.819096a75.379613 75.379613 0 0 0-6.606452-30.819096z" 108 d="M972.403613 749.997419c-59.986581 4.195097-54.172903-27.912258-18.531097-74.520774 81.259355-108.378839 231.787355-255.636645 238.558968-363.22271 9.348129-139.660387-131.138065-182.899613-275.819355-182.899612-100.64929 2.576516-204.833032 30.488774-275.819355 55.824516-244.504774 86.280258-397.708387 221.745548-494.988387 374.189419-100.64929 150.627097-69.367742 295.473548 148.050581 299.668645 164.203355-6.771613 275.026581-52.422194 386.64258-109.997419 0.792774 0-310.503226 88.856774-425.653677 23.717161-12.750452-6.804645-25.335742-16.152774-28.738065-42.28129 0-53.380129 88.097032-109.171613 139.726452-126.942968v-91.43329c104.018581 36.434581 226.733419 26.293677 331.742968-51.62942 3.402323 9.381161 6.771613 21.140645 5.945806 33.891097h17.771355c4.195097-36.467613-20.314839-71.944258-60.977548-74.520774 11.792516 9.348129 20.314839 16.945548 24.509935 23.717161l-1.585548 1.618581-0.825807 0.792774c-135.300129 94.835613-266.603355 50.803613-279.188645 48.227097l75.313549-73.728-21.140646-53.380129c149.867355-52.422194 273.408-90.640516 478.901678-126.909936l-45.980903-37.128258 23.717161-14.336c121.756903 33.858065 203.875097 59.193806 199.68 123.540645-1.618581 10.96671-5.945806 23.717161-12.750452 37.260388-36.302452 71.944258-142.897548 187.920516-186.136774 237.898322-27.879226 33.06529-55.824516 63.554065-75.313548 94.042839-21.933419 31.281548-33.032258 60.151742-33.858065 86.280258 4.195097 212.595613 631.279484-99.823484 754.820129-182.106839-180.157935 77.09729-375.642839 150.82529-588.07329 164.368516z m137.083871-488.547096c4.558452 8.390194 6.639484 18.696258 6.639484 30.819096a75.379613 75.379613 0 0 0-6.606452-30.819096z"
109 fill="#FF6600" 109 fill="#FF6600"
110 p-id="9033" 110 p-id="9033"
111 - ></path></svg  
112 - >Alibaba</a  
113 - > 111 + ></path>
  112 + </svg>
  113 + Alibaba
  114 + </a>
114 </span> 115 </span>
115 <span class="tw-mt-2"> 116 <span class="tw-mt-2">
116 <a 117 <a
@@ -137,34 +138,104 @@ @@ -137,34 +138,104 @@
137 d="M406.293333 644.821333l-0.064-287.722666 276.693334 144.362666-276.629334 143.36z" 138 d="M406.293333 644.821333l-0.064-287.722666 276.693334 144.362666-276.629334 143.36z"
138 fill="#FFFFFF" 139 fill="#FFFFFF"
139 p-id="14165" 140 p-id="14165"
140 - ></path></svg  
141 - >Youtube</a  
142 - > 141 + ></path>
  142 + </svg>
  143 + Youtube
  144 + </a>
143 </span> 145 </span>
144 <span class="tw-mt-2"> 146 <span class="tw-mt-2">
145 <a 147 <a
146 href="https://x.com/canrdenerge?s=11" 148 href="https://x.com/canrdenerge?s=11"
147 rel="noopener noreferrer" 149 rel="noopener noreferrer"
  150 + class="link-container"
148 style="display: flex; align-items: center; gap: 8px" 151 style="display: flex; align-items: center; gap: 8px"
149 - ><svg  
150 - t="1733207912677" 152 + >
  153 + <svg
151 class="icon" 154 class="icon"
  155 + t="1733207912677"
152 viewBox="0 0 1024 1024" 156 viewBox="0 0 1024 1024"
153 - version="1.1"  
154 xmlns="http://www.w3.org/2000/svg" 157 xmlns="http://www.w3.org/2000/svg"
155 - p-id="22952"  
156 width="20" 158 width="20"
157 height="20" 159 height="20"
158 > 160 >
159 <path 161 <path
160 d="M1024 186.368a410.325333 410.325333 0 0 1-120.618667 33.877333 214.954667 214.954667 0 0 0 92.373334-119.125333 413.781333 413.781333 0 0 1-133.504 52.181333A207.189333 207.189333 0 0 0 708.949333 85.333333c-115.968 0-210.005333 96.426667-210.005333 215.424 0 16.896 1.792 33.28 5.376 49.066667-174.592-9.002667-329.386667-94.72-433.066667-225.152a219.306667 219.306667 0 0 0-28.416 108.373333c0 74.709333 37.12 140.672 93.44 179.328a206.250667 206.250667 0 0 1-95.146666-26.88v2.645334c0 104.405333 72.405333 191.488 168.533333 211.2a200.32 200.32 0 0 1-55.296 7.594666c-13.525333 0-26.752-1.28-39.552-3.84 26.709333 85.589333 104.277333 147.882667 196.224 149.546667A414.805333 414.805333 0 0 1 0 841.941333 584.96 584.96 0 0 0 322.048 938.666667c386.474667 0 597.76-328.192 597.76-612.906667 0-9.386667-0.213333-18.730667-0.554667-27.904 41.045333-30.378667 76.672-68.266667 104.746667-111.488" 162 d="M1024 186.368a410.325333 410.325333 0 0 1-120.618667 33.877333 214.954667 214.954667 0 0 0 92.373334-119.125333 413.781333 413.781333 0 0 1-133.504 52.181333A207.189333 207.189333 0 0 0 708.949333 85.333333c-115.968 0-210.005333 96.426667-210.005333 215.424 0 16.896 1.792 33.28 5.376 49.066667-174.592-9.002667-329.386667-94.72-433.066667-225.152a219.306667 219.306667 0 0 0-28.416 108.373333c0 74.709333 37.12 140.672 93.44 179.328a206.250667 206.250667 0 0 1-95.146666-26.88v2.645334c0 104.405333 72.405333 191.488 168.533333 211.2a200.32 200.32 0 0 1-55.296 7.594666c-13.525333 0-26.752-1.28-39.552-3.84 26.709333 85.589333 104.277333 147.882667 196.224 149.546667A414.805333 414.805333 0 0 1 0 841.941333 584.96 584.96 0 0 0 322.048 938.666667c386.474667 0 597.76-328.192 597.76-612.906667 0-9.386667-0.213333-18.730667-0.554667-27.904 41.045333-30.378667 76.672-68.266667 104.746667-111.488"
161 fill="#55ACEE" 163 fill="#55ACEE"
162 - p-id="22953"  
163 - ></path></svg  
164 - >Twitter</a  
165 - > 164 + ></path>
  165 + </svg>
  166 + Twitter
  167 + </a>
166 </span> 168 </span>
167 </div> 169 </div>
  170 + <div class="text-h5 tw-mb-5 tw-mt-5">Send an email to me</div>
  171 + <form>
  172 + <v-row>
  173 + <v-col cols="8" md="6">
  174 + <v-text-field
  175 + v-model="state.firstName"
  176 + :error-messages="v$.firstName.$errors.map((e) => e.$message)"
  177 + label="FirstName"
  178 + required
  179 + @blur="v$.firstName.$touch"
  180 + @input="v$.firstName.$touch"
  181 + ></v-text-field>
  182 + </v-col>
  183 +
  184 + <v-col cols="8" md="6">
  185 + <v-text-field
  186 + v-model="state.lastName"
  187 + :error-messages="v$.lastName.$errors.map((e) => e.$message)"
  188 + label="LastName"
  189 + required
  190 + @blur="v$.lastName.$touch"
  191 + @input="v$.lastName.$touch"
  192 + ></v-text-field>
  193 + </v-col>
  194 + </v-row>
  195 + <v-text-field
  196 + v-model="state.email"
  197 + :error-messages="v$.email.$errors.map((e) => e.$message)"
  198 + label="E-mail"
  199 + required
  200 + @blur="v$.email.$touch"
  201 + @input="v$.email.$touch"
  202 + ></v-text-field>
  203 + <v-text-field
  204 + v-model="state.text"
  205 + :error-messages="v$.text.$errors.map((e) => e.$message)"
  206 + label="message"
  207 + required
  208 + @blur="v$.text.$touch"
  209 + @input="v$.text.$touch"
  210 + ></v-text-field>
  211 + <div class="recaptcha-container" style="margin-bottom: 20px">
  212 + <!-- reCAPTCHA v2 Checkbox -->
  213 + <div id="recaptcha" class="g-recaptcha"></div>
  214 + <!-- 验证成功后显示的消息 -->
  215 + <!-- <div v-if="verified" class="success-message">验证通过!</div> -->
  216 + </div>
  217 + <!-- <v-btn class="me-4" @click="v$.$validate"> submit </v-btn> -->
  218 + <v-btn class="me-4" @click="handleSubmit"> submit </v-btn>
  219 + <v-btn @click="clear"> clear </v-btn>
  220 + </form>
  221 + <v-snackbar
  222 + v-model="snackbar"
  223 + :timeout="3000"
  224 + top
  225 + :style="{ top: '300px', position: 'fixed' }"
  226 + color="success"
  227 + >
  228 + Sent successfully!
  229 + </v-snackbar>
  230 + <v-snackbar
  231 + v-model="snackbarFailed"
  232 + :timeout="3000"
  233 + top
  234 + :style="{ top: '100px', position: 'fixed' }"
  235 + color="error"
  236 + >
  237 + Failed to send!
  238 + </v-snackbar>
168 <div style="margin-bottom: 10px"> 239 <div style="margin-bottom: 10px">
169 <v-tabs 240 <v-tabs
170 class="tabs2" 241 class="tabs2"
@@ -296,6 +367,10 @@ @@ -296,6 +367,10 @@
296 </template> 367 </template>
297 368
298 <script setup lang="ts"> 369 <script setup lang="ts">
  370 +import { ref, reactive } from "vue";
  371 +import { useVuelidate } from "@vuelidate/core";
  372 +import { email, required, maxLength } from "@vuelidate/validators";
  373 +
299 const productStore = useProductListStore(); 374 const productStore = useProductListStore();
300 const categoryStore = useCategoryStore(); 375 const categoryStore = useCategoryStore();
301 const loading = ref(false); 376 const loading = ref(false);
@@ -317,6 +392,127 @@ const recommendImagesMobile = ref({}); @@ -317,6 +392,127 @@ const recommendImagesMobile = ref({});
317 const currentIndexMobile = ref(1); 392 const currentIndexMobile = ref(1);
318 const hotLoadingMobile = ref(false); 393 const hotLoadingMobile = ref(false);
319 const hotTotalMobile = ref(10); 394 const hotTotalMobile = ref(10);
  395 +const verified = ref(false); // 验证状态
  396 +const snackbar = ref(false);
  397 +const snackbarFailed = ref(false);
  398 +const initialState = {
  399 + firstName: "",
  400 + lastName: "",
  401 + email: "",
  402 + text: "",
  403 +};
  404 +
  405 +const state = reactive({
  406 + ...initialState,
  407 +});
  408 +
  409 +const handleSubmit = async () => {
  410 + if (verified.value) {
  411 + let { data } = await useAsyncData(
  412 + "sendEmail",
  413 + () =>
  414 + $fetch("/email/send", {
  415 + method: "POST",
  416 + body: {
  417 + firstName: state.firstName,
  418 + lastName: state.lastName,
  419 + email: state.email,
  420 + message: state.text,
  421 + subject: "",
  422 + },
  423 + }),
  424 + {
  425 + server: true, // 仅在服务器端获取数据
  426 + }
  427 + );
  428 + if (data.value.message == "成功") {
  429 + snackbar.value = true;
  430 + } else {
  431 + snackbarFailed.value = true;
  432 + }
  433 + } else {
  434 + snackbarFailed.value = true;
  435 + }
  436 +};
  437 +
  438 +const rules = {
  439 + name: { required },
  440 + firstName: { required },
  441 + lastName: { required },
  442 + email: { required, email },
  443 + text: { required, maxLength: maxLength(20) },
  444 +};
  445 +
  446 +const v$ = useVuelidate(rules, state);
  447 +
  448 +function clear() {
  449 + v$.value.$reset();
  450 +
  451 + for (const [key, value] of Object.entries(initialState)) {
  452 + state[key] = value;
  453 + }
  454 +}
  455 +
  456 +onMounted(() => {
  457 + // 动态加载 reCAPTCHA 脚本并初始化
  458 + loadRecaptchaScript(() => {
  459 + // 确保 grecaptcha 已经准备好
  460 + if (window.grecaptcha) {
  461 + // 强制等待一段时间,确保 #recaptcha 元素已经渲染
  462 + setTimeout(() => {
  463 + // 确保 #recaptcha 元素已经存在并且可用
  464 + const recaptchaElement = document.getElementById("recaptcha");
  465 + if (recaptchaElement) {
  466 + try {
  467 + window.grecaptcha.render("recaptcha", {
  468 + sitekey: "6Lcgd6kqAAAAAAm0mLcuLcjv3zz55hB6wu5gkZMe", // 替换为你的 Site Key
  469 + callback: onRecaptchaSuccess,
  470 + "error-callback": onRecaptchaError,
  471 + });
  472 + } catch (error) {}
  473 + } else {
  474 + }
  475 + }, 1000); // 延时 1 秒,确保 DOM 渲染完成
  476 + } else {
  477 + }
  478 + });
  479 +});
  480 +
  481 +// 验证成功回调
  482 +const onRecaptchaSuccess = (token) => {
  483 + verified.value = true; // 设置为已验证
  484 +};
  485 +
  486 +// 验证失败回调
  487 +const onRecaptchaError = () => {
  488 + verified.value = false; // 设置为未验证
  489 +};
  490 +
  491 +// 动态加载 reCAPTCHA 脚本
  492 +const loadRecaptchaScript = (callback) => {
  493 + if (document.getElementById("recaptcha-api")) {
  494 + callback(); // 如果脚本已加载,直接回调
  495 + return;
  496 + }
  497 +
  498 + const script = document.createElement("script");
  499 + script.id = "recaptcha-api";
  500 + script.src =
  501 + "https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit&hl=en";
  502 + script.async = true;
  503 + script.defer = true;
  504 +
  505 + // 脚本加载完成后的回调
  506 + script.onload = () => {
  507 + // 修改开始
  508 + callback();
  509 + }; // 修改结束
  510 +
  511 + // 脚本加载失败时的回调
  512 + script.onerror = () => {};
  513 +
  514 + document.head.appendChild(script);
  515 +};
320 const loadHotProducts = async () => { 516 const loadHotProducts = async () => {
321 hotLoading.value = true; 517 hotLoading.value = true;
322 let { data: hotProducts } = await useAsyncData( 518 let { data: hotProducts } = await useAsyncData(