Commit a96c3fee87e3ab70dbf04c014c5530cd931a7c53
1 parent
5359076c
feat: 开发邮件发送,prod2-55/56
Showing
5 changed files
with
330 additions
and
248 deletions
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 "../components/MainTitle.vue"; | @@ -579,117 +468,6 @@ import MainTitle from "../components/MainTitle.vue"; | ||
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 | |||
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( |