Commit a7b494e229505dd4eeb2a0e0e0f28d890faca4c9
Merge branch 'master' of http://39.108.227.113:8001/zhusen/canrud-nuxt-front
# Conflicts: # nuxt.config.ts
Showing
13 changed files
with
3241 additions
and
304 deletions
Too many changes to show.
To preserve performance only 13 of 18 files are displayed.
components/CategoryList.vue
... | ... | @@ -79,7 +79,9 @@ import { useProductListStore } from "@/stores/product_list"; |
79 | 79 | import type { CategoryRootType } from "@/type"; |
80 | 80 | import { computed } from "vue"; |
81 | 81 | import { useRouter, useRoute } from "vue-router"; |
82 | +import { useRouteQuery } from "@/stores/route_query"; | |
82 | 83 | |
84 | +const routeQuery = useRouteQuery(); | |
83 | 85 | const route = useRoute(); |
84 | 86 | const router = useRouter(); |
85 | 87 | |
... | ... | @@ -91,13 +93,16 @@ watchEffect(async () => { |
91 | 93 | productStore.updateKeyword(""); |
92 | 94 | const categories = route.query.categories.split(","); |
93 | 95 | const mainCategory = categories[0].trim(); // 取第一个值 |
94 | - const subCategoryName = categories[1] ? categories[1].trim() : null; // 取第二个值(如果存在) | |
96 | + const subCategoryName = ref("Not specified"); | |
97 | + subCategoryName.value = categories[1] | |
98 | + ? categories[1].trim() | |
99 | + : "Not specified"; // 取第二个值(如果存在) | |
95 | 100 | |
96 | 101 | // 2. 更新选中的主分类 |
97 | 102 | categoryStore.updateCategory(mainCategory); |
98 | - | |
103 | + routeQuery.updateCategories(mainCategory + "," + subCategoryName.value); | |
99 | 104 | // 3. 如果有子分类名称,查找其对应的 ID |
100 | - if (subCategoryName) { | |
105 | + if (subCategoryName.value) { | |
101 | 106 | const subCategoryList = computed(() => { |
102 | 107 | if (categoryStore.selectedCategory) { |
103 | 108 | const tmp = categoryStore.list.filter( |
... | ... | @@ -111,7 +116,7 @@ watchEffect(async () => { |
111 | 116 | |
112 | 117 | // 4. 查找对应的子分类 ID |
113 | 118 | const foundFuncCategory = subCategoryList.value.find( |
114 | - (func) => func.name === subCategoryName | |
119 | + (func) => func.name === subCategoryName.value | |
115 | 120 | ); |
116 | 121 | |
117 | 122 | if (foundFuncCategory) { |
... | ... | @@ -123,6 +128,7 @@ watchEffect(async () => { |
123 | 128 | // 5. 判断 query 中是否存在 function,并查找对应的 ID |
124 | 129 | if (route.query.function) { |
125 | 130 | const functionName = route.query.function.trim(); |
131 | + routeQuery.updateFunction(functionName); | |
126 | 132 | const funcCategoryList = computed(() => { |
127 | 133 | if (categoryStore.selectedCategory) { |
128 | 134 | const tmp = categoryStore.list.filter( |
... | ... | @@ -152,15 +158,30 @@ watchEffect(async () => { |
152 | 158 | // // 使用找到的 funcCategoryId |
153 | 159 | // categoryStore.updateFuncCategory(funcCategoryId); |
154 | 160 | // } |
161 | + } else if (route.query.categories.includes("Energy materials")) { | |
162 | + const defaultCategory = categoryStore.list[1]; | |
163 | + const defaultFuncCategory = defaultCategory.productFunctions[1]; | |
164 | + if (defaultFuncCategory) { | |
165 | + categoryStore.updateFuncCategory(defaultFuncCategory.id); | |
166 | + } | |
167 | + router.push({ | |
168 | + query: { | |
169 | + categories: route.query.categories, | |
170 | + function: defaultFuncCategory.name, | |
171 | + }, | |
172 | + }); | |
155 | 173 | } |
156 | - } else if (Object.keys(route.query).length === 0) { | |
174 | + } else if ( | |
175 | + Object.keys(route.query).length === 0 && | |
176 | + !route.path.includes("/products/detail") | |
177 | + ) { | |
157 | 178 | // 检查是否有默认的分类 |
158 | 179 | const defaultCategory = categoryStore.list[0]; // 假设第一个分类是默认的 |
159 | 180 | |
160 | 181 | if (defaultCategory) { |
161 | 182 | const defaultCategoryName = defaultCategory.categoryDisplayName; |
162 | - const defaultSubCategory = defaultCategory.list[0]; // 假设第一个子分类为默认子分类 | |
163 | - const defaultFuncCategory = defaultCategory.productFunctions[0]; // 假设第一个功能分类为默认功能分类 | |
183 | + const defaultSubCategory = defaultCategory.list[1]; // 假设第一个子分类为默认子分类 | |
184 | + const defaultFuncCategory = defaultCategory.productFunctions[1]; // 假设第一个功能分类为默认功能分类 | |
164 | 185 | |
165 | 186 | // 更新 store 和 URL |
166 | 187 | categoryStore.updateCategory(defaultCategoryName); | ... | ... |
components/Footer.vue
... | ... | @@ -5,7 +5,9 @@ |
5 | 5 | <v-col cols="12" lg="3" sm="12" md="6"> |
6 | 6 | <b>Solution</b> |
7 | 7 | <p><router-link to="/equipment">Lab Device</router-link></p> |
8 | - <p><router-link to="/customize">Customized BatterTesting</router-link></p> | |
8 | + <p> | |
9 | + <router-link to="/customize">Customized BatterTesting</router-link> | |
10 | + </p> | |
9 | 11 | <p><router-link to="/pack">Pack</router-link></p> |
10 | 12 | </v-col> |
11 | 13 | <v-col cols="12" lg="3" sm="12" md="6"> |
... | ... | @@ -16,6 +18,39 @@ |
16 | 18 | <v-col cols="12" lg="3" sm="12" md="6"> |
17 | 19 | <b>About</b> |
18 | 20 | <p><router-link to="/about">About us</router-link></p> |
21 | + <p> | |
22 | + <a | |
23 | + href="https://www.linkedin.com/company/canrd?originalSubdomain=cn" | |
24 | + rel="noopener noreferrer" | |
25 | + >LinkedIn</a | |
26 | + > | |
27 | + </p> | |
28 | + <p> | |
29 | + <a | |
30 | + href="https://www.amazon.com/s?me=A3A2SQ086XUS66&marketplaceID=ATVPDKIKX0DER" | |
31 | + rel="noopener noreferrer" | |
32 | + >Amazon</a | |
33 | + > | |
34 | + </p> | |
35 | + <p> | |
36 | + <a | |
37 | + href="https://canrd.en.alibaba.com/company_profile.html?spm=a2700.galleryofferlist.normal_offer.d_companyName.262213a0fqshG2" | |
38 | + rel="noopener noreferrer" | |
39 | + >Alibaba</a | |
40 | + > | |
41 | + </p> | |
42 | + <p> | |
43 | + <a | |
44 | + href="https://www.youtube.com/@Canrd_Tech" | |
45 | + rel="noopener noreferrer" | |
46 | + >Youtube</a | |
47 | + > | |
48 | + </p> | |
49 | + <p> | |
50 | + <a href="https://x.com/canrdenerge?s=11" rel="noopener noreferrer" | |
51 | + >Twitter</a | |
52 | + > | |
53 | + </p> | |
19 | 54 | </v-col> |
20 | 55 | <v-col cols="12" lg="3" sm="12" md="6"> |
21 | 56 | <div class="tw-w-[250px] tw-float-left tw-mr-[8px]"> |
... | ... | @@ -24,14 +59,20 @@ |
24 | 59 | <p>Phone: +86 19867737979</p> |
25 | 60 | <p>Wechat: contactcanrd</p> |
26 | 61 | </div> |
27 | - <img class="tw-float-left" src="/wechat.jpg" alt="canrud-wechat" width="80" /> | |
62 | + <img | |
63 | + class="tw-float-left" | |
64 | + src="/wechat.jpg" | |
65 | + alt="canrud-wechat" | |
66 | + width="80" | |
67 | + /> | |
28 | 68 | </v-col> |
29 | 69 | </v-row> |
30 | 70 | </v-container> |
31 | 71 | </div> |
32 | 72 | </template> |
33 | 73 | |
34 | -<script setup lang="ts"></script> | |
74 | +<script setup lang="ts"> | |
75 | +</script> | |
35 | 76 | |
36 | 77 | <style> |
37 | 78 | b { | ... | ... |
components/MobileCategoryList.vue
... | ... | @@ -3,7 +3,9 @@ |
3 | 3 | class="pr-4 tw-h-[48px] tw-leading-[48px] border-b tw-flex tw-justify-between tw-items-center" |
4 | 4 | > |
5 | 5 | <span class="ml-4 tw-font-bold">{{ categoryStore.selectedCategory }}</span> |
6 | - <span class="ml-1 text-grey-darken-4 text-body-2" @click="drawerVis = !drawerVis" | |
6 | + <span | |
7 | + class="ml-1 text-grey-darken-4 text-body-2" | |
8 | + @click="drawerVis = !drawerVis" | |
7 | 9 | >Filter <v-icon> mdi-filter-outline </v-icon></span |
8 | 10 | > |
9 | 11 | </div> |
... | ... | @@ -16,7 +18,11 @@ |
16 | 18 | touchless |
17 | 19 | class="!tw-h-[70%] bg-grey-lighten-4 tw-overflow-y-auto tw-overflow-x-hidden" |
18 | 20 | > |
19 | - <div class="mb-4 pa-2 tw-bg-[#fff]" v-for="(item, index) in categoryStore.list" :key="index"> | |
21 | + <div | |
22 | + class="mb-4 pa-2 tw-bg-[#fff]" | |
23 | + v-for="(item, index) in categoryStore.list" | |
24 | + :key="index" | |
25 | + > | |
20 | 26 | <div class="mb-4 tw-flex tw-items-center"> |
21 | 27 | <div> |
22 | 28 | <v-img |
... | ... | @@ -29,7 +35,9 @@ |
29 | 35 | " |
30 | 36 | ></v-img> |
31 | 37 | </div> |
32 | - <strong class="tw-m-0 tw-inline">{{ item.categoryDisplayName }}</strong> | |
38 | + <strong class="tw-m-0 tw-inline">{{ | |
39 | + item.categoryDisplayName | |
40 | + }}</strong> | |
33 | 41 | </div> |
34 | 42 | <div class="tw-flex tw-flex-wrap tw-justify-between"> |
35 | 43 | <template v-if="index !== 0"> |
... | ... | @@ -43,7 +51,7 @@ |
43 | 51 | : ' bg-grey-lighten-4 ') |
44 | 52 | " |
45 | 53 | :key="i" |
46 | - @click="handleCategoryClick(item, k.id)" | |
54 | + @click="handleCategoryClick(item, k.id, k)" | |
47 | 55 | > |
48 | 56 | {{ k.name }} |
49 | 57 | </div> |
... | ... | @@ -59,7 +67,7 @@ |
59 | 67 | ' tw-w-[48%] mb-4 py-3 px-2 tw-rounded-lg tw-whitespace-nowrap tw-overflow-hidden tw-text-ellipsis' |
60 | 68 | " |
61 | 69 | :key="i" |
62 | - @click="handleCategoryClick(item, k.id)" | |
70 | + @click="handleCategoryClick(item, k.id, k)" | |
63 | 71 | > |
64 | 72 | {{ k.name }} |
65 | 73 | </div> |
... | ... | @@ -71,62 +79,259 @@ |
71 | 79 | </template> |
72 | 80 | |
73 | 81 | <script setup lang="ts"> |
74 | -import { useCategoryStore } from '@/stores/category' | |
75 | -import { useProductListStore } from '@/stores/product_list' | |
76 | -import type { CategoryRootType } from '@/type' | |
77 | -import { computed, ref, watchEffect } from 'vue' | |
78 | -import { useRouter } from 'vue-router' | |
82 | +import { useCategoryStore } from "@/stores/category"; | |
83 | +import { useProductListStore } from "@/stores/product_list"; | |
84 | +import type { CategoryRootType } from "@/type"; | |
85 | +import { computed, ref, watchEffect } from "vue"; | |
86 | +import { useRouter, useRoute } from "vue-router"; | |
79 | 87 | |
80 | -const router = useRouter() | |
88 | +const router = useRouter(); | |
89 | +const route = useRoute(); | |
81 | 90 | |
82 | -const categoryStore = useCategoryStore() | |
83 | -const productStore = useProductListStore() | |
91 | +const categoryStore = useCategoryStore(); | |
92 | +const productStore = useProductListStore(); | |
93 | +const routeQuery = useRouteQuery(); | |
84 | 94 | |
85 | -const drawerVis = ref(false) | |
95 | +const drawerVis = ref(false); | |
86 | 96 | |
87 | 97 | const CATEGORY_IMG = [ |
88 | - { normal: '/category/1.png', selected: '/category/1.1.png', name: 'Energy materials' }, | |
89 | - { normal: '/category/2.png', selected: '/category/2.1.png', name: 'Laboratory consumables' }, | |
90 | - { normal: '/category/3.png', selected: '/category/3.1.png', name: 'Low-dimensional materials' }, | |
91 | - { normal: '/category/4.png', selected: '/category/4.1.png', name: 'Equipment' } | |
92 | -] | |
98 | + { | |
99 | + normal: "/category/1.png", | |
100 | + selected: "/category/1.1.png", | |
101 | + name: "Energy materials", | |
102 | + }, | |
103 | + { | |
104 | + normal: "/category/2.png", | |
105 | + selected: "/category/2.1.png", | |
106 | + name: "Laboratory consumables", | |
107 | + }, | |
108 | + { | |
109 | + normal: "/category/3.png", | |
110 | + selected: "/category/3.1.png", | |
111 | + name: "Low-dimensional materials", | |
112 | + }, | |
113 | + { | |
114 | + normal: "/category/4.png", | |
115 | + selected: "/category/4.1.png", | |
116 | + name: "Equipment", | |
117 | + }, | |
118 | +]; | |
119 | + | |
120 | +watchEffect(async () => { | |
121 | + if (route.query.categories) { | |
122 | + // 1. 提取 query.category 的内容 | |
123 | + productStore.updateKeyword(""); | |
124 | + const categories = route.query.categories.split(","); | |
125 | + const mainCategory = categories[0].trim(); // 取第一个值 | |
126 | + const subCategoryName = ref(""); | |
127 | + subCategoryName.value = categories[1] | |
128 | + ? categories[1].trim() | |
129 | + : "Not specified"; // 取第二个值(如果存在) | |
130 | + | |
131 | + // 2. 更新选中的主分类 | |
132 | + categoryStore.updateCategory(mainCategory); | |
133 | + routeQuery.updateCategories(mainCategory + "," + subCategoryName.value); | |
134 | + if (!route.query.categories.includes("Energy materials")) { | |
135 | + // 3. 如果有子分类名称,查找其对应的 ID | |
136 | + if (subCategoryName.value) { | |
137 | + // if (subCategoryName.value && mainCategory !== "Energy materials") { | |
138 | + const subCategoryList = computed(() => { | |
139 | + if (categoryStore.selectedCategory) { | |
140 | + const tmp = categoryStore.list.filter( | |
141 | + (item) => | |
142 | + item.categoryDisplayName === categoryStore.selectedCategory | |
143 | + ); | |
144 | + return tmp?.[0]?.list || []; | |
145 | + } | |
146 | + return []; | |
147 | + }); | |
148 | + | |
149 | + // 4. 查找对应的子分类 ID | |
150 | + const foundFuncCategory = subCategoryList.value.find( | |
151 | + (func) => func.name === subCategoryName.value | |
152 | + ); | |
153 | + | |
154 | + if (foundFuncCategory) { | |
155 | + const funcCategoryId = foundFuncCategory.id; | |
156 | + // 你可以在这里使用找到的 funcCategoryId | |
157 | + categoryStore.updateSubCategory(funcCategoryId); | |
158 | + } | |
159 | + } | |
160 | + } else { | |
161 | + // 3. 如果有子分类名称,查找其对应的 ID | |
162 | + if (subCategoryName.value) { | |
163 | + // if (subCategoryName.value && mainCategory !== "Energy materials") { | |
164 | + const subCategoryList = computed(() => { | |
165 | + if (categoryStore.selectedCategory) { | |
166 | + const tmp = categoryStore.list.filter( | |
167 | + (item) => | |
168 | + item.categoryDisplayName === categoryStore.selectedCategory | |
169 | + ); | |
170 | + return tmp?.[0]?.list || []; | |
171 | + } | |
172 | + return []; | |
173 | + }); | |
174 | + | |
175 | + // 4. 查找对应的子分类 ID | |
176 | + const foundFuncCategory = subCategoryList.value.find( | |
177 | + (func) => func.name === subCategoryName.value | |
178 | + ); | |
179 | + | |
180 | + if (foundFuncCategory) { | |
181 | + const funcCategoryId = foundFuncCategory.id; | |
182 | + // 你可以在这里使用找到的 funcCategoryId | |
183 | + categoryStore.updateSubCategory(funcCategoryId); | |
184 | + } | |
185 | + } | |
186 | + // 5. 判断 query 中是否存在 function,并查找对应的 ID | |
187 | + if (route.query.function) { | |
188 | + const functionName = route.query.function.trim(); | |
189 | + routeQuery.updateFunction(functionName); | |
190 | + const funcCategoryList = computed(() => { | |
191 | + if (categoryStore.selectedCategory) { | |
192 | + const tmp = categoryStore.list.filter( | |
193 | + (item) => | |
194 | + item.categoryDisplayName === categoryStore.selectedCategory | |
195 | + ); | |
196 | + return tmp?.[0]?.productFunctions || []; | |
197 | + } | |
198 | + return []; | |
199 | + }); | |
200 | + const foundFuncCategory = funcCategoryList.value.find( | |
201 | + (func) => func.name === functionName | |
202 | + ); | |
203 | + console.log(foundFuncCategory, "functionName"); | |
204 | + | |
205 | + if (foundFuncCategory) { | |
206 | + const funcCategoryId = foundFuncCategory.id; | |
207 | + if (typeof window !== "undefined") { | |
208 | + window.selectedFuncCategory = foundFuncCategory.id; | |
209 | + } | |
210 | + // 你可以在这里使用找到的 funcCategoryId | |
211 | + categoryStore.updateFuncCategory(funcCategoryId); | |
212 | + } | |
213 | + // // 6. 查找对应的功能分类 ID | |
214 | + // const foundFuncCategory = funcCategories.find( | |
215 | + // (func) => func.name === functionName | |
216 | + // ); | |
217 | + | |
218 | + // if (foundFuncCategory) { | |
219 | + // const funcCategoryId = foundFuncCategory.id; | |
220 | + // // 使用找到的 funcCategoryId | |
221 | + // categoryStore.updateFuncCategory(funcCategoryId); | |
222 | + // } | |
223 | + } else if (route.query.categories.includes("Energy materials")) { | |
224 | + //没有function默认为第一个 | |
225 | + const defaultCategory = categoryStore.list[0]; | |
226 | + const defaultFuncCategory = defaultCategory.productFunctions[1]; | |
227 | + if (defaultFuncCategory) { | |
228 | + categoryStore.updateFuncCategory(defaultFuncCategory.id); | |
229 | + } | |
230 | + } | |
231 | + } | |
232 | + } else if ( | |
233 | + Object.keys(route.query).length === 0 && | |
234 | + !route.path.includes("/products/detail") | |
235 | + ) { | |
236 | + // 检查是否有默认的分类 | |
237 | + const defaultCategory = categoryStore.list[0]; // 假设第一个分类是默认的 | |
238 | + | |
239 | + if (defaultCategory) { | |
240 | + const defaultCategoryName = defaultCategory.categoryDisplayName; | |
241 | + const defaultSubCategory = defaultCategory.list[1]; // 假设第一个子分类为默认子分类 | |
242 | + const defaultFuncCategory = defaultCategory.productFunctions[1]; // 假设第一个功能分类为默认功能分类 | |
243 | + | |
244 | + // 更新 store 和 URL | |
245 | + categoryStore.updateCategory(defaultCategoryName); | |
246 | + productStore.updatePageNo(1); | |
247 | + | |
248 | + if (defaultSubCategory) { | |
249 | + categoryStore.updateSubCategory(defaultSubCategory.id); | |
250 | + | |
251 | + // 如果有之前的值则使用之前的值,拼接新的子分类名 | |
252 | + const updatedCategory = | |
253 | + defaultCategoryName + "," + defaultSubCategory.name; | |
254 | + | |
255 | + // 拼接设备类型到 URL | |
256 | + router.push({ | |
257 | + query: { | |
258 | + categories: defaultCategoryName + "," + defaultSubCategory.name, | |
259 | + }, | |
260 | + }); | |
261 | + } | |
262 | + | |
263 | + if (defaultFuncCategory) { | |
264 | + categoryStore.updateFuncCategory(defaultFuncCategory.id); | |
265 | + // 拼接功能类型到 URL | |
266 | + router.push({ | |
267 | + query: { | |
268 | + categories: defaultCategoryName + "," + defaultSubCategory.name, | |
269 | + function: defaultFuncCategory.name, | |
270 | + }, | |
271 | + }); | |
272 | + } | |
273 | + } | |
274 | + } | |
275 | +}); | |
93 | 276 | |
94 | 277 | const SEO = { |
95 | - 'Energy materials': | |
96 | - 'Energy materials,Not specified,Battery accessories,Lithium-ion batteries,Capacitors,Sodium-ion batteries,Lithium-sulfur batteries,Potassium/magnesium/aluminum/zinc batteries,Air/fuel/solar,Analytical electrodes,Complete battery accessories', | |
97 | - 'Laboratory consumables': | |
98 | - 'Laboratory consumables,Not specified,Glass materials,Plastic materials,Metal materials,Ceramic materials,Paper film materials,Chemical materials,Tetrafluoro materials,Safety protection,Office supplies,Tools,Others', | |
99 | - 'Low-dimensional materials': | |
100 | - ',Low-dimensional materialsNot specified,Zero-dimensional carbon materials,One-dimensional carbon materials,Two-dimensional carbon materials,Three-dimensional carbon materials,Inorganic nanomaterials,Organic nanomaterials,Metal nanomaterials,Others', | |
278 | + "Energy materials": | |
279 | + "Energy materials,Not specified,Battery accessories,Lithium-ion batteries,Capacitors,Sodium-ion batteries,Lithium-sulfur batteries,Potassium/magnesium/aluminum/zinc batteries,Air/fuel/solar,Analytical electrodes,Complete battery accessories", | |
280 | + "Laboratory consumables": | |
281 | + "Laboratory consumables,Not specified,Glass materials,Plastic materials,Metal materials,Ceramic materials,Paper film materials,Chemical materials,Tetrafluoro materials,Safety protection,Office supplies,Tools,Others", | |
282 | + "Low-dimensional materials": | |
283 | + ",Low-dimensional materialsNot specified,Zero-dimensional carbon materials,One-dimensional carbon materials,Two-dimensional carbon materials,Three-dimensional carbon materials,Inorganic nanomaterials,Organic nanomaterials,Metal nanomaterials,Others", | |
101 | 284 | Equipment: |
102 | - 'Equipment,Not specified,Equipment,Accessories & fixtures,Fuel cell manufacturing and testing equipment' | |
103 | -} | |
285 | + "Equipment,Not specified,Equipment,Accessories & fixtures,Fuel cell manufacturing and testing equipment", | |
286 | +}; | |
104 | 287 | |
105 | 288 | const handleDrawerHide = () => { |
106 | - productStore.updatePageNo(1) | |
289 | + // productStore.updatePageNo(1); | |
107 | 290 | |
108 | - router.push({ query: { category: categoryStore.selectedCategory } }) | |
291 | + // router.push({ | |
292 | + // query: { | |
293 | + // categories: categoryStore.selectedCategory, | |
294 | + // }, | |
295 | + // }); | |
109 | 296 | |
110 | - const doc = document as any | |
111 | - const head = doc.getElementsByTagName('head') | |
112 | - const meta = doc.createElement('meta') | |
113 | - document.title = SEO[categoryStore.selectedCategory as keyof typeof SEO] | |
297 | + const doc = document as any; | |
298 | + const head = doc.getElementsByTagName("head"); | |
299 | + const meta = doc.createElement("meta"); | |
300 | + document.title = SEO[categoryStore.selectedCategory as keyof typeof SEO]; | |
114 | 301 | doc |
115 | 302 | .querySelector('meta[name="keywords"]') |
116 | - .setAttribute('content', SEO[categoryStore.selectedCategory as keyof typeof SEO]) | |
117 | - head[0].appendChild(meta) | |
118 | -} | |
303 | + .setAttribute( | |
304 | + "content", | |
305 | + SEO[categoryStore.selectedCategory as keyof typeof SEO] | |
306 | + ); | |
307 | + head[0].appendChild(meta); | |
308 | +}; | |
119 | 309 | |
120 | -const handleCategoryClick = (item: CategoryRootType, id: string) => { | |
121 | - categoryStore.updateCategory(item.categoryDisplayName) | |
310 | +const handleCategoryClick = ( | |
311 | + item: CategoryRootType, | |
312 | + id: string, | |
313 | + subItem: any | |
314 | +) => { | |
315 | + categoryStore.updateCategory(item.categoryDisplayName); | |
122 | 316 | |
123 | - if (item.categoryDisplayName === 'Energy materials') { | |
124 | - categoryStore.updateFuncCategory(id) | |
125 | - categoryStore.updateSubCategory(item.list[0].id) | |
317 | + if (item.categoryDisplayName === "Energy materials") { | |
318 | + categoryStore.updateFuncCategory(id); | |
319 | + categoryStore.updateSubCategory(item.list[0].id); | |
320 | + router.push({ | |
321 | + query: { | |
322 | + categories: categoryStore.selectedCategory + "," + item.list[0].name, | |
323 | + function: subItem.name, | |
324 | + }, | |
325 | + }); | |
126 | 326 | } else { |
127 | - categoryStore.updateSubCategory(id) | |
327 | + categoryStore.updateSubCategory(id); | |
328 | + router.push({ | |
329 | + query: { | |
330 | + categories: categoryStore.selectedCategory + "," + subItem.name, | |
331 | + }, | |
332 | + }); | |
128 | 333 | } |
129 | -} | |
334 | +}; | |
130 | 335 | </script> |
131 | 336 | |
132 | 337 | <style lang="less" scoped></style> | ... | ... |
components/MobileHeader.vue
... | ... | @@ -5,37 +5,87 @@ |
5 | 5 | <v-layout> |
6 | 6 | <v-app-bar scroll-behavior="elevate" color="white" density="default"> |
7 | 7 | <v-app-bar-title v-if="!showSearch"> |
8 | - <a href="/"><v-img src="/mobile/index-logo.png" alt="canrud" width="100" height="40" /></a> | |
8 | + <a href="/" | |
9 | + ><v-img | |
10 | + src="/mobile/index-logo.png" | |
11 | + alt="canrud" | |
12 | + width="100" | |
13 | + height="40" | |
14 | + /></a> | |
9 | 15 | </v-app-bar-title> |
10 | 16 | <template v-slot:append> |
11 | 17 | <v-btn icon v-if="!showSearch"> |
12 | 18 | <v-icon @click="showSearch = !showSearch">mdi-magnify</v-icon> |
13 | 19 | </v-btn> |
14 | 20 | |
15 | - <v-app-bar-nav-icon @click="drawer = !drawer" v-if="!drawer"></v-app-bar-nav-icon> | |
16 | - <v-app-bar-nav-icon @click="drawer = !drawer" v-if="drawer" icon="mdi-window-close"></v-app-bar-nav-icon> | |
21 | + <v-app-bar-nav-icon | |
22 | + @click="drawer = !drawer" | |
23 | + v-if="!drawer" | |
24 | + ></v-app-bar-nav-icon> | |
25 | + <v-app-bar-nav-icon | |
26 | + @click="drawer = !drawer" | |
27 | + v-if="drawer" | |
28 | + icon="mdi-window-close" | |
29 | + ></v-app-bar-nav-icon> | |
17 | 30 | </template> |
18 | 31 | |
19 | 32 | <div class="tw-h-[36px] tw-w-[300px] ml-8" v-if="showSearch"> |
20 | - <v-text-field dense density="compact" height="24" label="Search keyword" hide-details="auto" variant="solo" | |
21 | - append-inner-icon="mdi-close" @click:appendInner="handleClick" @keydown="handleKeyDown" v-model="input"> | |
33 | + <v-text-field | |
34 | + dense | |
35 | + density="compact" | |
36 | + height="24" | |
37 | + label="Search keyword" | |
38 | + hide-details="auto" | |
39 | + variant="solo" | |
40 | + append-inner-icon="mdi-close" | |
41 | + @click:appendInner="handleClick" | |
42 | + @keydown="handleKeyDown" | |
43 | + v-model="input" | |
44 | + > | |
22 | 45 | </v-text-field> |
23 | 46 | </div> |
24 | 47 | </v-app-bar> |
25 | 48 | |
26 | - <v-navigation-drawer width="512" v-model="drawer" location="right" temporary floating> | |
49 | + <v-navigation-drawer | |
50 | + width="512" | |
51 | + v-model="drawer" | |
52 | + location="right" | |
53 | + temporary | |
54 | + floating | |
55 | + > | |
27 | 56 | <v-list density="compact" nav> |
28 | - <v-list-item to="/" title="Home" value="home" @click="handleTabClick" color="blue-darken-1"> | |
57 | + <v-list-item | |
58 | + to="/" | |
59 | + title="Home" | |
60 | + value="home" | |
61 | + @click="handleTabClick" | |
62 | + color="blue-darken-1" | |
63 | + > | |
29 | 64 | </v-list-item> |
30 | 65 | <v-divider></v-divider> |
31 | - <v-list-item to="/products" title="Products" value="Products" @click="handleTabClick" | |
32 | - color="blue-darken-1"></v-list-item> | |
66 | + <v-list-item | |
67 | + to="/products" | |
68 | + title="Products" | |
69 | + value="Products" | |
70 | + @click="handleTabClick" | |
71 | + color="blue-darken-1" | |
72 | + ></v-list-item> | |
33 | 73 | <v-divider></v-divider> |
34 | - <v-list-item to="/about" title="About" value="about" @click="handleTabClick" | |
35 | - color="blue-darken-1"></v-list-item> | |
74 | + <v-list-item | |
75 | + to="/about" | |
76 | + title="About" | |
77 | + value="about" | |
78 | + @click="handleTabClick" | |
79 | + color="blue-darken-1" | |
80 | + ></v-list-item> | |
36 | 81 | <v-divider></v-divider> |
37 | - <v-list-item to="/contact" title="Contact" value="contact" @click="handleTabClick" | |
38 | - color="blue-darken-1"></v-list-item> | |
82 | + <v-list-item | |
83 | + to="/contact" | |
84 | + title="Contact" | |
85 | + value="contact" | |
86 | + @click="handleTabClick" | |
87 | + color="blue-darken-1" | |
88 | + ></v-list-item> | |
39 | 89 | <v-divider></v-divider> |
40 | 90 | <v-list-item title="Blog" value="blog" color="blue-darken-1"> |
41 | 91 | <a href="http://blog.canrud.com/" target="" title="Blog">Blog</a> |
... | ... | @@ -73,66 +123,66 @@ |
73 | 123 | </template> |
74 | 124 | |
75 | 125 | <script setup lang="ts"> |
76 | -import { ref, watchEffect } from 'vue' | |
77 | -import { useProductListStore } from '@/stores/product_list' | |
78 | -import { useRouter } from 'vue-router' | |
79 | -import { useDialogStore } from '@/stores/dialog' | |
80 | -import { useCategoryStore } from '@/stores/category' | |
81 | -import { useDisplay } from 'vuetify' | |
126 | +import { ref, watchEffect } from "vue"; | |
127 | +import { useProductListStore } from "@/stores/product_list"; | |
128 | +import { useRouter } from "vue-router"; | |
129 | +import { useDialogStore } from "@/stores/dialog"; | |
130 | +import { useCategoryStore } from "@/stores/category"; | |
131 | +import { useDisplay } from "vuetify"; | |
82 | 132 | |
83 | -const drawer = ref(false) | |
84 | -const showSearch = ref(false) | |
133 | +const drawer = ref(false); | |
134 | +const showSearch = ref(false); | |
85 | 135 | |
86 | -const productStore = useProductListStore() | |
87 | -const categoryStore = useCategoryStore() | |
136 | +const productStore = useProductListStore(); | |
137 | +const categoryStore = useCategoryStore(); | |
88 | 138 | |
89 | -const input = ref() | |
139 | +const input = ref(); | |
90 | 140 | |
91 | -const router = useRouter() | |
141 | +const router = useRouter(); | |
92 | 142 | |
93 | 143 | const handleKeyDown = (e: any) => { |
94 | 144 | if (e.keyCode == 13) { |
95 | - categoryStore.updateDisplay(!input.value) | |
96 | - productStore.updateKeyword(input.value) | |
97 | - productStore.updatePageNo(1) | |
98 | - router.push('/products') | |
145 | + categoryStore.updateDisplay(!input.value); | |
146 | + productStore.updateKeyword(input.value); | |
147 | + productStore.updatePageNo(1); | |
148 | + router.push("/products"); | |
99 | 149 | } |
100 | -} | |
150 | +}; | |
101 | 151 | |
102 | 152 | const handleClick = () => { |
103 | - showSearch.value = false | |
104 | -} | |
153 | + showSearch.value = false; | |
154 | +}; | |
105 | 155 | |
106 | 156 | const handleTabClick = () => { |
107 | - categoryStore.updateDisplay(true) | |
108 | - productStore.updateKeyword('') | |
109 | -} | |
157 | + categoryStore.updateDisplay(true); | |
158 | + productStore.updateKeyword(""); | |
159 | +}; | |
110 | 160 | |
111 | 161 | watchEffect(() => { |
112 | - input.value = productStore.keyword | |
113 | -}) | |
162 | + input.value = productStore.keyword; | |
163 | +}); | |
114 | 164 | |
115 | 165 | onMounted(() => { |
116 | 166 | // 获取url的参数 |
117 | - const url = window.location.href | |
118 | - const index = url.indexOf('?') | |
167 | + const url = window.location.href; | |
168 | + const index = url.indexOf("?"); | |
119 | 169 | if (index !== -1) { |
120 | - const params = url.slice(index + 1).split('&') | |
121 | - const obj: any = {} | |
170 | + const params = url.slice(index + 1).split("&"); | |
171 | + const obj: any = {}; | |
122 | 172 | params.forEach((item) => { |
123 | - const arr = item.split('=') | |
124 | - obj[arr[0]] = arr[1] | |
125 | - }) | |
173 | + const arr = item.split("="); | |
174 | + obj[arr[0]] = arr[1]; | |
175 | + }); | |
126 | 176 | // 获取dialog的状态 |
127 | 177 | if (obj.flag) { |
128 | - dialog.updateDialog(true) | |
178 | + dialog.updateDialog(true); | |
129 | 179 | } |
130 | 180 | |
131 | 181 | if (obj.keyword) { |
132 | - productStore.updateKeyword(obj.keyword) | |
182 | + productStore.updateKeyword(obj.keyword); | |
133 | 183 | } |
134 | 184 | } |
135 | -}) | |
185 | +}); | |
136 | 186 | </script> |
137 | 187 | |
138 | 188 | <style lang="scss" scoped> | ... | ... |
components/MobileProductDetail.vue
1 | 1 | <template> |
2 | + <div> | |
3 | + <!-- <v-breadcrumbs | |
4 | + divider="/" | |
5 | + dense | |
6 | + style=" | |
7 | + padding-top: 8px; | |
8 | + font-size: 14px; | |
9 | + display: flex; | |
10 | + flex-wrap: wrap; | |
11 | + align-items: center; | |
12 | + " | |
13 | + > | |
14 | + <template v-for="(item, index) in items" :key="index"> | |
15 | + <v-breadcrumbs-item | |
16 | + :disabled="item.disabled" | |
17 | + :href="item.href" | |
18 | + :class="{ | |
19 | + 'breadcrumb-last': index === items.length - 1, | |
20 | + }" | |
21 | + :style=" | |
22 | + index === items.length - 1 | |
23 | + ? 'color: black; font-weight: bold;' | |
24 | + : 'color:black' | |
25 | + " | |
26 | + > | |
27 | + {{ item.title }} | |
28 | + </v-breadcrumbs-item> | |
29 | + <span | |
30 | + v-if="index < items.length - 1" | |
31 | + class="breadcrumb-divider" | |
32 | + style="margin: 0 4px; font-size: 14px; color: gray" | |
33 | + >/</span | |
34 | + > | |
35 | + </template> | |
36 | + </v-breadcrumbs> --> | |
37 | + <v-breadcrumbs | |
38 | + divider="/" | |
39 | + dense | |
40 | + style=" | |
41 | + padding-top: 8px; | |
42 | + font-size: 14px; | |
43 | + display: flex; | |
44 | + flex-wrap: wrap; | |
45 | + align-items: center; | |
46 | + " | |
47 | + > | |
48 | + <template v-for="(item, index) in items" :key="index"> | |
49 | + <v-breadcrumbs-item | |
50 | + :disabled="item.disabled" | |
51 | + :href="item.href" | |
52 | + style="color: #1e88e5" | |
53 | + > | |
54 | + {{ item.title }} | |
55 | + </v-breadcrumbs-item> | |
56 | + <span | |
57 | + v-if="index < items.length - 1" | |
58 | + class="breadcrumb-divider" | |
59 | + style="margin: 0 4px; font-size: 14px; color: gray" | |
60 | + >/</span | |
61 | + > | |
62 | + </template> | |
63 | + </v-breadcrumbs> | |
64 | + </div> | |
65 | + | |
2 | 66 | <v-container class="ma-0 pa-0 bg-grey-lighten-5"> |
3 | - <v-carousel class="tw-float-left" height="450" v-model="slide" hide-delimiter-background> | |
4 | - <v-carousel-item cover v-for="(slide, i) in info.productimageliststore" :src="slide.url" :key="i" | |
5 | - :alt="info.name"> | |
67 | + <v-carousel | |
68 | + class="tw-float-left" | |
69 | + height="450" | |
70 | + v-model="slide" | |
71 | + hide-delimiter-background | |
72 | + > | |
73 | + <v-carousel-item | |
74 | + cover | |
75 | + v-for="(slide, i) in info.productimageliststore" | |
76 | + :src="slide.url" | |
77 | + :key="i" | |
78 | + :alt="info.name" | |
79 | + > | |
6 | 80 | </v-carousel-item> |
7 | 81 | </v-carousel> |
8 | - <div class="mb-3 bg-white mb-sm-10 text-h4 font-weight-medium pa-4"> | |
82 | + <h1 class="mb-3 bg-white mb-sm-10 text-h4 font-weight-medium pa-4"> | |
9 | 83 | {{ info.name }} |
10 | - </div> | |
84 | + </h1> | |
11 | 85 | <v-row class="mx-4 mt-0 mb-4 bg-white rounded-lg"> |
12 | 86 | <v-col cols="6"> |
13 | 87 | <div class="text-body-1 text-grey-darken-3">Brand:</div> |
... | ... | @@ -30,10 +104,21 @@ |
30 | 104 | <div class="text-h6">{{ info.basecore3 }}</div> |
31 | 105 | </v-col> |
32 | 106 | </v-row> |
33 | - <div v-if="info.ticketTypes?.length" class="py-4 mx-4 bg-white rounded-lg tw-flex tw-flex-wrap tw-justify-around"> | |
34 | - <v-sheet v-for="item in info.ticketTypes || []" :key="item.rank" rounded="lg" border class="tw-w-[40%] mb-4"> | |
107 | + <div | |
108 | + ref="ticketDiv" | |
109 | + v-if="info.ticketTypes?.length" | |
110 | + class="py-0 mx-4 rounded-lg tw-flex tw-flex-wrap tw-justify-around" | |
111 | + > | |
112 | + <!-- <v-sheet | |
113 | + v-for="item in info.ticketTypes || []" | |
114 | + :key="item.rank" | |
115 | + rounded="lg" | |
116 | + border | |
117 | + class="tw-w-[40%] mb-4" | |
118 | + > | |
35 | 119 | <div |
36 | - class="tw-bg-[#dcecfa] tw-h-[64px] !tw-leading-[64px] rounded-lg rounded-b-0 pa-2 tw-text-center text-h6 tw-overflow-hidden tw-text-ellipsis"> | |
120 | + class="tw-bg-[#dcecfa] tw-h-[64px] !tw-leading-[64px] rounded-lg rounded-b-0 pa-2 tw-text-center text-h6 tw-overflow-hidden tw-text-ellipsis" | |
121 | + > | |
37 | 122 | {{ item.typeName }} |
38 | 123 | </div> |
39 | 124 | <div class="tw-h-[82px] pa-2"> |
... | ... | @@ -42,17 +127,91 @@ |
42 | 127 | {{ item.rank }} |
43 | 128 | </div> |
44 | 129 | </div> |
45 | - </v-sheet> | |
46 | - <v-btn size="large" color="blue-darken-1" @click="router.push('/contact')"> | |
130 | + </v-sheet> --> | |
131 | + <v-table | |
132 | + density="comfortable" | |
133 | + style="width: 100%" | |
134 | + class="table1 tw-mt-[0px] tw-overflow-x-auto" | |
135 | + v-if="info.ticketTypes?.length" | |
136 | + > | |
137 | + <thead> | |
138 | + <tr class="bg-grey-lighten-3"> | |
139 | + <th class="text-left headerBorder text-grey-darken-1">Name/Code</th> | |
140 | + <th class="text-left headerBorder text-grey-darken-1"> | |
141 | + Specification | |
142 | + </th> | |
143 | + <th | |
144 | + v-if="info.priceShow !== undefined && info.priceShow" | |
145 | + class="text-left headerBorder text-grey-darken-1" | |
146 | + > | |
147 | + Price | |
148 | + </th> | |
149 | + <!-- <th class="text-left headerBorder text-grey-darken-1">Actions</th> --> | |
150 | + </tr> | |
151 | + </thead> | |
152 | + <tbody> | |
153 | + <tr | |
154 | + class="tr" | |
155 | + v-for="item in info.ticketTypes || []" | |
156 | + :key="item.rank" | |
157 | + > | |
158 | + <td class="td text-grey-darken-4 font-weight-medium"> | |
159 | + {{ item.rank }} | |
160 | + </td> | |
161 | + <td class="td text-grey-darken-4 font-weight-medium"> | |
162 | + {{ item.typeName }} | |
163 | + </td> | |
164 | + <td v-if="item.priceShow" class="td">$ {{ item.price }}</td> | |
165 | + <!-- <td class="td"> | |
166 | + <v-btn | |
167 | + size="small" | |
168 | + color="blue-darken-1" | |
169 | + @click="router.push('/contact')" | |
170 | + > | |
171 | + Quote | |
172 | + </v-btn> | |
173 | + </td> --> | |
174 | + </tr> | |
175 | + </tbody> | |
176 | + </v-table> | |
177 | + <!-- <div v-for="item in info.ticketTypes || []" | |
178 | + :key="item.rank"> | |
47 | 179 | |
180 | + </div> --> | |
181 | + <!-- <v-btn | |
182 | + style="margin-top: 6px" | |
183 | + color="blue-darken-1" | |
184 | + @click="router.push('/contact')" | |
185 | + > | |
186 | + Quotation Inquiry | |
187 | + </v-btn> --> | |
188 | + <!-- <div | |
189 | + v-if="showBottomButton" | |
190 | + ref="bottomButton" | |
191 | + :class="['button-container', { fixed: isFixed }]" | |
192 | + > --> | |
193 | + <v-btn | |
194 | + style="margin-top: 12px" | |
195 | + color="blue-darken-1 tw-sticky" | |
196 | + @click="router.push('/contact')" | |
197 | + > | |
48 | 198 | Quotation Inquiry |
49 | 199 | </v-btn> |
200 | + <!-- </div> --> | |
50 | 201 | </div> |
51 | 202 | <!-- <v-dialog v-model="dialog" activator="parent" width="auto"> |
52 | 203 | <v-card> Contact us Email: contact@canrd.com QQ: 3003597584 / 2902385824 </v-card> |
53 | 204 | </v-dialog> --> |
54 | 205 | <div class="tw-pb-[64px] ma-4 rounded-lg"> |
55 | - <v-tabs class="tabs" v-model="tab" bg-color="#fff" slider-color="#1d89e4" selected-class="active"> | |
206 | + <v-tabs | |
207 | + class="tabs" | |
208 | + v-model="tab" | |
209 | + style="margin-top: 25px; margin-bottom: 20px" | |
210 | + color="white" | |
211 | + bg-color="#eeeeee" | |
212 | + slider-color="blue-lighten-1" | |
213 | + selected-class="active" | |
214 | + > | |
56 | 215 | <v-tab :value="1">Product Details</v-tab> |
57 | 216 | <v-tab :value="2">Specification</v-tab> |
58 | 217 | <!-- <v-tab :value="3">商品百科</v-tab> --> |
... | ... | @@ -69,7 +228,7 @@ |
69 | 228 | <v-divider class="tw-mb-[12px]"></v-divider> |
70 | 229 | <div v-html="info.physicalproperty"></div> |
71 | 230 | </div> |
72 | - <div v-if="info.advantage" class="tw-mb-[24px]"> | |
231 | + <div v-if="info.storage" class="tw-mb-[24px]"> | |
73 | 232 | <div class="py-2 pl-2 text-h6">Storage</div> |
74 | 233 | <v-divider class="tw-mb-[12px]"></v-divider> |
75 | 234 | <div v-html="info.storage"></div> |
... | ... | @@ -79,7 +238,7 @@ |
79 | 238 | <v-divider class="tw-mb-[12px]"></v-divider> |
80 | 239 | <div v-html="info.introduction"></div> |
81 | 240 | </div> |
82 | - <div v-if="info.advantage" class="tw-mb-[24px]"> | |
241 | + <div v-if="info.description" class="tw-mb-[24px]"> | |
83 | 242 | <div class="py-2 pl-2 text-h6">Description</div> |
84 | 243 | <v-divider class="tw-mb-[12px]"></v-divider> |
85 | 244 | <div v-html="info.description"></div> |
... | ... | @@ -88,8 +247,12 @@ |
88 | 247 | <v-window-item key="2" :value="2"> |
89 | 248 | <v-table density="compact" class="table2"> |
90 | 249 | <tbody> |
91 | - <tr class="tr" v-for="item in info.productAttributeList || []" :key="item.name"> | |
92 | - <td class="td tw-w-[400px]">{{ item.name }}</td> | |
250 | + <tr | |
251 | + class="tr" | |
252 | + v-for="item in info.productAttributeList || []" | |
253 | + :key="item.name" | |
254 | + > | |
255 | + <td class="td tw-w-[350px]">{{ item.name }}</td> | |
93 | 256 | <td class="td">{{ item.value }}</td> |
94 | 257 | </tr> |
95 | 258 | </tbody> |
... | ... | @@ -98,33 +261,553 @@ |
98 | 261 | <!-- <v-window-item key="3" :value="3"> 2 </v-window-item> --> |
99 | 262 | </v-window> |
100 | 263 | </div> |
264 | + <div class="tw-pb-[64px] ma-4 rounded-lg" style="padding-bottom: 30px"> | |
265 | + <v-tabs | |
266 | + class="tabs" | |
267 | + v-model="tabProduct" | |
268 | + color="white" | |
269 | + bg-color="#eeeeee" | |
270 | + slider-color="blue-lighten-1" | |
271 | + selected-class="active" | |
272 | + style="margin-bottom: 20px" | |
273 | + > | |
274 | + <v-tab :value="1">Best Sellers</v-tab> | |
275 | + <v-tab :value="2">Related Products</v-tab> | |
276 | + <!-- <v-tab :value="3">商品百科</v-tab> --> | |
277 | + </v-tabs> | |
278 | + <v-window v-model="tabProduct"> | |
279 | + <v-window-item key="1" :value="1"> | |
280 | + <div class="tw-text-center" v-if="hotLoading && isMobile()"> | |
281 | + <v-progress-circular | |
282 | + color="blue-lighten-2" | |
283 | + indeterminate | |
284 | + size="64" | |
285 | + class="tw-m-auto" | |
286 | + ></v-progress-circular> | |
287 | + </div> | |
288 | + <v-item-group multiple v-if="isMobile()"> | |
289 | + <v-row v-if="!hotLoading"> | |
290 | + <v-col | |
291 | + v-for="(item, i) in recommendImagesHot" | |
292 | + :key="i" | |
293 | + cols="6" | |
294 | + lg="3" | |
295 | + md="4" | |
296 | + sm="6" | |
297 | + > | |
298 | + <div v-if="item !== null"> | |
299 | + <v-card | |
300 | + :elevation="4" | |
301 | + class="mx-auto" | |
302 | + :href="item[0].productUrl" | |
303 | + > | |
304 | + <!-- 设置 eager 属性,确保图片直接加载 --> | |
305 | + <v-img | |
306 | + :src="item[0].url" | |
307 | + :alt="item[0].name" | |
308 | + eager | |
309 | + class="d-block" | |
310 | + /> | |
311 | + <v-card-text class="tw-text-left font-weight-medium title"> | |
312 | + <h4>{{ item[0].name }}</h4> | |
313 | + </v-card-text> | |
314 | + </v-card> | |
315 | + </div> | |
316 | + </v-col> | |
317 | + </v-row> | |
318 | + <!-- <div | |
319 | + v-if="!hotTotal" | |
320 | + class="text-medium-emphasis text-body-1 tw-text-center tw-m-[64px]" | |
321 | + > | |
322 | + no data | |
323 | + </div> --> | |
324 | + </v-item-group> | |
325 | + <v-row v-if="isMobile()"> | |
326 | + <v-col> | |
327 | + <v-pagination | |
328 | + :size="isMobile() ? 'small' : 'default'" | |
329 | + v-if="hotTotal" | |
330 | + v-model="currentIndexHot" | |
331 | + @update:modelValue="toggleRowMobileHot" | |
332 | + :length="hotLength" | |
333 | + rounded="0" | |
334 | + class="tw-float-right tw-mt-[32px]" | |
335 | + total-visible="5" | |
336 | + ></v-pagination></v-col | |
337 | + ></v-row> | |
338 | + </v-window-item> | |
339 | + <!-- best sellers --> | |
340 | + <v-window-item key="2" :value="2"> | |
341 | + <div class="tw-text-center" v-if="loading && isMobile()"> | |
342 | + <v-progress-circular | |
343 | + color="blue-lighten-2" | |
344 | + indeterminate | |
345 | + size="64" | |
346 | + class="tw-m-auto" | |
347 | + ></v-progress-circular> | |
348 | + </div> | |
349 | + <v-item-group multiple v-if="isMobile()"> | |
350 | + <v-row v-if="!loading"> | |
351 | + <v-col | |
352 | + v-for="(item, i) in recommendImages" | |
353 | + :key="i" | |
354 | + cols="6" | |
355 | + lg="3" | |
356 | + md="4" | |
357 | + sm="6" | |
358 | + > | |
359 | + <div v-if="item !== null"> | |
360 | + <v-card | |
361 | + :elevation="4" | |
362 | + class="mx-auto" | |
363 | + :href="item[0].productUrl" | |
364 | + > | |
365 | + <!-- 设置 eager 属性,确保图片直接加载 --> | |
366 | + <v-img | |
367 | + :src="item[0].url" | |
368 | + :alt="item[0].name" | |
369 | + eager | |
370 | + class="d-block" | |
371 | + /> | |
372 | + <v-card-text class="tw-text-left font-weight-medium title"> | |
373 | + <h4>{{ item[0].name }}</h4> | |
374 | + </v-card-text> | |
375 | + </v-card> | |
376 | + </div> | |
377 | + </v-col> | |
378 | + </v-row> | |
379 | + <!-- <div | |
380 | + v-if="!hotTotal" | |
381 | + class="text-medium-emphasis text-body-1 tw-text-center tw-m-[64px]" | |
382 | + > | |
383 | + no data | |
384 | + </div> --> | |
385 | + </v-item-group> | |
386 | + <v-row v-if="isMobile()"> | |
387 | + <v-col> | |
388 | + <v-pagination | |
389 | + :size="isMobile() ? 'small' : 'default'" | |
390 | + v-if="total" | |
391 | + v-model="currentIndex" | |
392 | + @update:modelValue="toggleRowMobile" | |
393 | + :length="length" | |
394 | + rounded="0" | |
395 | + class="tw-float-right tw-mt-[32px]" | |
396 | + total-visible="5" | |
397 | + ></v-pagination></v-col | |
398 | + ></v-row> | |
399 | + </v-window-item> | |
400 | + <!-- <v-window-item key="3" :value="3"> 2 </v-window-item> --> | |
401 | + </v-window> | |
402 | + </div> | |
403 | + <div class="tw-pb-[64px] ma-4 rounded-lg"> | |
404 | + <v-tabs | |
405 | + class="tabs" | |
406 | + v-model="tabJournal" | |
407 | + color="white" | |
408 | + bg-color="#eeeeee" | |
409 | + slider-color="blue-lighten-1" | |
410 | + selected-class="active" | |
411 | + > | |
412 | + <v-tab :value="1">Product-related Journals</v-tab> | |
413 | + </v-tabs> | |
414 | + <v-window v-model="tabJournal"> | |
415 | + <v-window-item key="1" :value="1"> | |
416 | + <v-list> | |
417 | + <v-list-item | |
418 | + v-for="item in info.journals" | |
419 | + :key="item.id" | |
420 | + @click="navigateToUrl(item.link)" | |
421 | + @mouseenter="hoveredItem = item.id" | |
422 | + @mouseleave="hoveredItem = null" | |
423 | + > | |
424 | + <v-list-item-title> | |
425 | + <span | |
426 | + :class="['title', { 'full-title': hoveredItem === item.id }]" | |
427 | + > | |
428 | + {{ item.title }} | |
429 | + </span> | |
430 | + </v-list-item-title> | |
431 | + </v-list-item> | |
432 | + </v-list> | |
433 | + </v-window-item> | |
434 | + </v-window> | |
435 | + </div> | |
436 | + <!-- Basic use --> | |
437 | + <div class="social-share-container"> | |
438 | + <SocialShare | |
439 | + network="facebook" | |
440 | + :styled="true" | |
441 | + :label="true" | |
442 | + :title="info.name" | |
443 | + :url="currentUrl" | |
444 | + style="color: #1e88e5; width: 140px" | |
445 | + /> | |
446 | + | |
447 | + <SocialShare | |
448 | + network="twitter" | |
449 | + :styled="true" | |
450 | + :label="true" | |
451 | + :title="info.name" | |
452 | + :url="currentUrl" | |
453 | + style="color: #1e88e5; width: 140px" | |
454 | + /> | |
455 | + | |
456 | + <SocialShare | |
457 | + network="linkedin" | |
458 | + :styled="true" | |
459 | + :label="true" | |
460 | + :title="info.name" | |
461 | + :url="currentUrl" | |
462 | + style="color: #1e88e5; width: 140px" | |
463 | + /> | |
464 | + </div> | |
101 | 465 | </v-container> |
102 | 466 | </template> |
103 | 467 | |
104 | 468 | <script setup lang="ts"> |
105 | -import type { Product } from '~/type' | |
106 | -import { onMounted, ref } from 'vue' | |
107 | -import { useDialogStore } from '~/stores/dialog' | |
108 | -const dialogStore = useDialogStore() | |
469 | +import type { Product } from "~/type"; | |
470 | +import { isMobile } from "~/utils"; | |
471 | +import { defineProps, ref } from "vue"; | |
472 | +import { useDialogStore } from "~/stores/dialog"; | |
473 | +const dialogStore = useDialogStore(); | |
109 | 474 | |
110 | 475 | const props = defineProps<{ |
111 | - info: Product | |
112 | -}>() | |
113 | -const info = props.info | |
476 | + info: Product; | |
477 | +}>(); | |
478 | +const info = props.info; | |
114 | 479 | // onMounted(() => { |
115 | 480 | // dialogStore.updateDialog(true) |
116 | 481 | // }) |
117 | -const tab = ref(0) | |
118 | -const slide = ref(0) | |
119 | -const router = useRouter() | |
482 | +const tab = ref(); | |
483 | +const tabProduct = ref(); | |
484 | +const tabJournal = ref(); | |
485 | +const slide = ref(0); | |
486 | +const router = useRouter(); | |
487 | +const href1 = ref("/products"); | |
488 | +const href2 = ref("/products"); | |
489 | +const routeQuery = useRouteQuery(); | |
490 | +const productStore = useProductListStore(); | |
491 | +const currentUrl = ref("https://www.canrud.com/products"); | |
492 | +const hotLoading = ref(false); | |
493 | +const loading = ref(false); | |
494 | +const maxPage = ref(1); | |
495 | +const maxPageRe = ref(1); | |
496 | +const tabRecom = ref(); | |
497 | +const recommendList = ref(); | |
498 | +const recommendListHot = ref(); | |
499 | +const recommendImages = ref(); | |
500 | +const recommendImagesHot = ref(); | |
501 | +const currentIndex = ref(1); | |
502 | +const currentIndexHot = ref(1); | |
503 | +const total = ref(3); | |
504 | +const hotTotal = ref(10); | |
505 | +const isOrNotMobile = isMobile(); | |
506 | +const hoveredItem = ref(null); | |
507 | + | |
508 | +const navigateToUrl = (url) => { | |
509 | + window.open(url); // 在新标签页中打开链接 | |
510 | +}; | |
511 | +const loadProducts = async () => { | |
512 | + const pageSize = 4; | |
513 | + loading.value = true; | |
514 | + | |
515 | + let { data: resData } = await useAsyncData( | |
516 | + "list", | |
517 | + () => | |
518 | + $fetch("/shop/product/list", { | |
519 | + method: "GET", | |
520 | + params: { | |
521 | + pageNo: currentIndex.value, | |
522 | + pageSize: pageSize, | |
523 | + ids: info.relatedProductIds, | |
524 | + }, | |
525 | + }), | |
526 | + { | |
527 | + server: true, // 仅在服务器端获取数据 | |
528 | + } | |
529 | + ); | |
530 | + total.value = resData.value.data.total; | |
531 | + if (total.value > 50) { | |
532 | + total.value = 50; | |
533 | + } | |
534 | + recommendList.value = resData.value.data.records; | |
535 | + maxPageRe.value = resData.value.data.pages; | |
536 | + // recommendImages.value = recommendList.value.slice(0, 10).map((item) => { | |
537 | + recommendImages.value = Array.from({ length: 4 }).map((_, index) => { | |
538 | + const item = recommendList.value[index]; | |
539 | + if (!item) { | |
540 | + return null; | |
541 | + } | |
542 | + // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析 | |
543 | + if (typeof item.productimageliststore === "string") { | |
544 | + try { | |
545 | + item.productimageliststore = JSON.parse(item.productimageliststore); | |
546 | + } catch (error) { | |
547 | + item.productimageliststore = []; // 解析失败时,设置为空数组 | |
548 | + } | |
549 | + } | |
550 | + const ree = (item.productimageliststore = item?.productimageliststore.map( | |
551 | + (productItem: ProductImage) => ({ | |
552 | + ...productItem, | |
553 | + // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`, | |
554 | + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`, | |
555 | + name: item.name, | |
556 | + productUrl: `https://www.canrud.com/products/detail/${item.id}`, | |
557 | + }) | |
558 | + )); | |
559 | + return ree; | |
560 | + }); | |
561 | + loading.value = false; | |
562 | +}; | |
563 | +const loadHotProducts = async () => { | |
564 | + const pageSizeHot = ref(5); | |
565 | + if (isOrNotMobile) { | |
566 | + pageSizeHot.value = 4; | |
567 | + } | |
568 | + hotLoading.value = true; | |
569 | + | |
570 | + let { data: hotProducts } = await useAsyncData( | |
571 | + "hotProducts", | |
572 | + () => | |
573 | + $fetch("/shop/product/hotProducts", { | |
574 | + method: "GET", | |
575 | + params: { | |
576 | + pageNo: currentIndexHot.value, | |
577 | + pageSize: pageSizeHot.value, | |
578 | + }, | |
579 | + }), | |
580 | + { | |
581 | + server: true, // 仅在服务器端获取数据 | |
582 | + } | |
583 | + ); | |
584 | + hotTotal.value = hotProducts.value.data.total; | |
585 | + recommendListHot.value = hotProducts.value.data.records; | |
586 | + maxPage.value = hotProducts.value.data.pages; | |
587 | + // recommendImages.value = recommendList.value.slice(0, 10).map((item) => { | |
588 | + recommendImagesHot.value = Array.from({ length: pageSizeHot.value }).map( | |
589 | + (_, index) => { | |
590 | + const item = recommendListHot.value[index]; | |
591 | + if (!item) { | |
592 | + return null; | |
593 | + } | |
594 | + // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析 | |
595 | + if (typeof item.productimageliststore === "string") { | |
596 | + try { | |
597 | + item.productimageliststore = JSON.parse(item.productimageliststore); | |
598 | + } catch (error) { | |
599 | + item.productimageliststore = []; // 解析失败时,设置为空数组 | |
600 | + } | |
601 | + } | |
602 | + const ree = (item.productimageliststore = item?.productimageliststore.map( | |
603 | + (productItem: ProductImage) => ({ | |
604 | + ...productItem, | |
605 | + // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`, | |
606 | + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`, | |
607 | + name: item.name, | |
608 | + productUrl: `https://www.canrud.com/products/detail/${item.id}`, | |
609 | + }) | |
610 | + )); | |
611 | + return ree; | |
612 | + } | |
613 | + ); | |
614 | + hotLoading.value = false; | |
615 | +}; | |
616 | + | |
617 | +const toggleRowMobile = (value: number) => { | |
618 | + currentIndex.value = value; | |
619 | +}; | |
620 | +const toggleRowMobileHot = (value: number) => { | |
621 | + currentIndexHot.value = value; | |
622 | +}; | |
623 | + | |
624 | +watch(currentIndexHot, (newIndex) => { | |
625 | + loadHotProducts(); // Call loadHotProducts when currentIndex changes | |
626 | +}); | |
627 | +watch(currentIndex, (newIndex) => { | |
628 | + loadProducts(); // Call loadHotProducts when currentIndex changes | |
629 | +}); | |
630 | +// Initial load of hot products | |
631 | +await loadHotProducts(); // Load hot products the first time | |
632 | +await loadProducts(); // Load hot products the first time | |
633 | + | |
634 | +interface BreadcrumbItem { | |
635 | + title: string; // 标题 | |
636 | + disabled: boolean; // 是否禁用 | |
637 | + href: string; // 链接地址 | |
638 | +} | |
639 | + | |
640 | +// 示例数据 | |
641 | +const items = ref<BreadcrumbItem[]>([ | |
642 | + { | |
643 | + title: "Products", | |
644 | + disabled: false, | |
645 | + href: "https://www.canrud.com/products", | |
646 | + }, | |
647 | + { | |
648 | + title: "CATEGORY", | |
649 | + disabled: false, | |
650 | + href: href1.value, | |
651 | + }, | |
652 | + { | |
653 | + title: "Not specified", | |
654 | + disabled: false, | |
655 | + href: href2.value, | |
656 | + }, | |
657 | + { | |
658 | + title: "Not specified", | |
659 | + disabled: false, | |
660 | + href: "/products", | |
661 | + }, | |
662 | +]); | |
663 | +watchEffect(() => { | |
664 | + currentUrl.value = "https://www.canrud.com/products/detail/" + info.id; | |
665 | + if (info?.productCrumbsVO?.category1 && productStore.keyword) { | |
666 | + items.value[1].title = info.productCrumbsVO.category1; | |
667 | + items.value[1].href = | |
668 | + "https://www.canrud.com/products?categories=" + | |
669 | + info.productCrumbsVO.category1; | |
670 | + href1.value = | |
671 | + "https://www.canrud.com/products?categories=" + | |
672 | + info.productCrumbsVO.category1; | |
673 | + if (info?.productCrumbsVO?.category2) { | |
674 | + items.value[2].title = info.productCrumbsVO.category2; | |
675 | + href2.value = href1.value + "," + info.productCrumbsVO.category2; | |
676 | + items.value[2].href = href1.value + "," + info.productCrumbsVO.category2; | |
677 | + } | |
678 | + if (info?.productCrumbsVO?.function) { | |
679 | + // items.value.push({ | |
680 | + // title: info.productCrumbsVO.function, | |
681 | + // disabled: false, | |
682 | + // href: href2.value + "&function=" + info.productCrumbsVO.function, | |
683 | + // }); | |
684 | + items.value[3].title = info.productCrumbsVO.function; | |
685 | + items.value[3].href = | |
686 | + href2.value + "&function=" + info.productCrumbsVO.function; | |
687 | + } | |
688 | + } else if (routeQuery.categories) { | |
689 | + if (!routeQuery.categories.includes("Energy materials")) { | |
690 | + routeQuery.updateFunction("Not specified"); | |
691 | + } | |
692 | + const categories = routeQuery.categories.split(","); | |
693 | + const mainCategory = categories[0].trim(); // 取第一个值 | |
694 | + const subCategoryName = ref( | |
695 | + categories[1] ? categories[1].trim() : "Not specified" | |
696 | + ); // 取第二个值(如果存在) | |
697 | + if (subCategoryName.value == "Accessories & fixtures") { | |
698 | + subCategoryName.value = encodeURIComponent("Accessories & fixtures"); | |
699 | + items.value[2].title = "Accessories & fixtures"; | |
700 | + } else { | |
701 | + items.value[2].title = subCategoryName.value; | |
702 | + } | |
703 | + items.value[1].title = mainCategory; | |
704 | + items.value[1].href = | |
705 | + "https://www.canrud.com/products?categories=" + mainCategory; | |
706 | + href1.value = "https://www.canrud.com/products?categories=" + mainCategory; | |
707 | + // items.value[1].title = subCategoryName.value; | |
708 | + href2.value = href1.value + "," + subCategoryName.value; | |
709 | + items.value[2].href = href1.value + "," + subCategoryName.value; | |
710 | + if (routeQuery?.selectedFunction) { | |
711 | + // items.value.push({ | |
712 | + // title: routeQuery.selectedFunction, | |
713 | + // disabled: false, | |
714 | + // href: href2.value + "&function=" + routeQuery.selectedFunction, | |
715 | + // }); | |
716 | + items.value[3].title = routeQuery.selectedFunction; | |
717 | + items.value[3].href = | |
718 | + href2.value + "&function=" + routeQuery.selectedFunction; | |
719 | + // routeQuery.updateFunction(null); | |
720 | + } | |
721 | + } else if (info?.productCrumbsVO?.category1) { | |
722 | + items.value[1].title = info.productCrumbsVO.category1; | |
723 | + items.value[1].href = | |
724 | + "https://www.canrud.com/products?categories=" + | |
725 | + info.productCrumbsVO.category1; | |
726 | + href1.value = | |
727 | + "https://www.canrud.com/products?categories=" + | |
728 | + info.productCrumbsVO.category1; | |
729 | + if (info?.productCrumbsVO?.category2) { | |
730 | + items.value[2].title = info.productCrumbsVO.category2; | |
731 | + href2.value = href1.value + "," + info.productCrumbsVO.category2; | |
732 | + items.value[2].href = href1.value + "," + info.productCrumbsVO.category2; | |
733 | + } | |
734 | + if (info?.productCrumbsVO?.function) { | |
735 | + // items.value.push({ | |
736 | + // title: info.productCrumbsVO.function, | |
737 | + // disabled: false, | |
738 | + // href: href2.value + "&function=" + info.productCrumbsVO.function, | |
739 | + // }); | |
740 | + items.value[3].title = info.productCrumbsVO.function; | |
741 | + items.value[3].href = | |
742 | + href2.value + "&function=" + info.productCrumbsVO.function; | |
743 | + } | |
744 | + } | |
745 | +}); | |
746 | +const length = computed(() => (total.value ? Math.ceil(total.value / 4) : 0)); | |
747 | +const hotLength = computed(() => | |
748 | + hotTotal.value ? Math.ceil(hotTotal.value / 4) : 0 | |
749 | +); | |
120 | 750 | </script> |
121 | 751 | |
122 | 752 | <style lang="scss" scoped> |
753 | +// .tabs { | |
754 | +// border-bottom: 2px solid #cbd9e4; | |
755 | +// } | |
756 | + | |
757 | +// .active { | |
758 | +// background-color: #fff; | |
759 | +// } | |
760 | + | |
123 | 761 | .tabs { |
124 | - border-bottom: 2px solid #cbd9e4; | |
762 | + border-bottom: 2px solid #1f88e5; | |
125 | 763 | } |
126 | 764 | |
127 | 765 | .active { |
128 | - background-color: #fff; | |
766 | + background-color: #1086e8; | |
767 | +} | |
768 | + | |
769 | +.tw-sticky { | |
770 | + position: sticky; | |
771 | +} | |
772 | + | |
773 | +// .button-container { | |
774 | +// margin-top: 6px; | |
775 | +// display: flex; | |
776 | +// justify-content: center; | |
777 | +// position: absolute; | |
778 | +// width: 100%; | |
779 | +// bottom: auto; /* 默认情况下不固定 */ | |
780 | +// } | |
781 | + | |
782 | +// .button-container.fixed { | |
783 | +// position: fixed; | |
784 | +// bottom: 16px; /* 固定在页面底部 */ | |
785 | +// left: 0; | |
786 | +// z-index: 1000; | |
787 | +// background-color: rgba(255, 255, 255, 0.9); | |
788 | +// box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.1); | |
789 | +// padding: 8px 0; | |
790 | +// } | |
791 | +.social-share-container { | |
792 | + display: flex; | |
793 | + flex-direction: row; /* 父容器横向排列 */ | |
794 | + gap: 1rem; /* 设置每个分享项之间的间距 */ | |
795 | + margin-left: 10px; | |
796 | + margin-bottom: 10px; | |
797 | +} | |
798 | + | |
799 | +.social-share-item { | |
800 | + display: flex; | |
801 | + flex-direction: row; /* 每个分享项横向排列 */ | |
802 | + align-items: center; /* 垂直居中对齐 */ | |
803 | + gap: 0.5rem; /* 标题与按钮之间的间距 */ | |
804 | +} | |
805 | + | |
806 | +.social-share-title { | |
807 | + font-size: 1rem; | |
808 | + font-weight: bold; | |
809 | +} | |
810 | +.v-card { | |
811 | + transition: all 0.3s ease-in-out; | |
129 | 812 | } |
130 | 813 | </style> | ... | ... |
components/ProductDetail.vue
1 | 1 | <template> |
2 | 2 | <v-container> |
3 | + <div> | |
4 | + <!-- <v-breadcrumbs divider="/" style="padding-top: 0"> | |
5 | + <template v-for="(item, index) in items" :key="index"> | |
6 | + <v-breadcrumbs-item | |
7 | + :disabled="item.disabled" | |
8 | + :href="item.href" | |
9 | + :class="{ | |
10 | + 'breadcrumb-disabled': item.disabled, | |
11 | + 'breadcrumb-active': !item.disabled, | |
12 | + 'breadcrumb-last': index === items.length - 1, | |
13 | + }" | |
14 | + :style=" | |
15 | + index === items.length - 1 | |
16 | + ? 'color: black;font-size:15px' | |
17 | + : 'font-size:15px;' | |
18 | + " | |
19 | + > | |
20 | + {{ item.title }} | |
21 | + </v-breadcrumbs-item> | |
22 | + <span v-if="index < items.length - 1" class="breadcrumb-divider" | |
23 | + >/</span | |
24 | + > | |
25 | + </template> | |
26 | + </v-breadcrumbs> --> | |
27 | + <v-breadcrumbs divider="/" style="padding-top: 0"> | |
28 | + <template v-for="(item, index) in items" :key="index"> | |
29 | + <v-breadcrumbs-item | |
30 | + :disabled="item.disabled" | |
31 | + :href="item.href" | |
32 | + :class="{ | |
33 | + 'breadcrumb-disabled': item.disabled, | |
34 | + 'breadcrumb-active': !item.disabled, | |
35 | + }" | |
36 | + style="font-size: 14px" | |
37 | + > | |
38 | + {{ item.title }} | |
39 | + </v-breadcrumbs-item> | |
40 | + <!-- 添加分隔符,排除最后一个 item --> | |
41 | + <span | |
42 | + v-if="index < items.length - 1" | |
43 | + class="breadcrumb-divider" | |
44 | + style="color: gray" | |
45 | + >/</span | |
46 | + > | |
47 | + </template> | |
48 | + </v-breadcrumbs> | |
49 | + </div> | |
3 | 50 | <v-row class="mb-16 ma-0"> |
4 | - <v-col cols="12" sm="5"> | |
51 | + <v-col cols="12" sm="5" class="carousel-container"> | |
5 | 52 | <v-carousel |
6 | - class="tw-float-left" | |
53 | + class="tw-float-left tw-sticky tw-top-[16px]" | |
7 | 54 | height="450" |
8 | 55 | v-model="slide" |
9 | 56 | hide-delimiter-background |
57 | + style="top: 16px" | |
10 | 58 | > |
11 | 59 | <v-carousel-item |
12 | 60 | cover |
... | ... | @@ -18,33 +66,34 @@ |
18 | 66 | </v-carousel-item> |
19 | 67 | </v-carousel> |
20 | 68 | </v-col> |
21 | - <v-col cols="12" sm="7"> | |
22 | - <v-row class="bg-white mb-sm-10 text-h4 font-weight-medium"> | |
69 | + <v-col cols="12" sm="7" class="table-container"> | |
70 | + <!-- <v-row class="bg-white mb-sm-10 text-h4 font-weight-medium"> | |
23 | 71 | <v-col>{{ info.name }}</v-col> |
24 | - </v-row> | |
72 | + </v-row> --> | |
73 | + <h1 class="tw-m-[12px] tw-mb-[36px]">{{ info.name }}</h1> | |
25 | 74 | <div class="tw-flex tw-flex-wrap"> |
26 | - <div class="tw-w-1/2"> | |
27 | - <span class="tw-leading-[10px] tw-m-[16px]"> | |
75 | + <div class="tw-w-1/2 tw-mb-[12px]"> | |
76 | + <span class="tw-leading-[10px] tw-m-[12px]"> | |
28 | 77 | Brand:{{ info.brandName }} |
29 | 78 | </span> |
30 | 79 | </div> |
31 | 80 | <div class="tw-w-1/2 tw-mb-[12px]"> |
32 | - <span class="tw-leading-[10px] tw-m-[16px]" | |
81 | + <span class="tw-leading-[10px] tw-m-[12px]" | |
33 | 82 | >Product Model:{{ info.model }}</span |
34 | 83 | > |
35 | 84 | </div> |
36 | 85 | <div class="tw-w-1/2 tw-mb-[12px]" v-if="info.basename1"> |
37 | - <span class="tw-leading-[10px] tw-m-[16px]" | |
86 | + <span class="tw-leading-[10px] tw-m-[12px]" | |
38 | 87 | >{{ info.basename1 }}:{{ info.basecore1 }}</span |
39 | 88 | > |
40 | 89 | </div> |
41 | 90 | <div class="tw-w-1/2 tw-mb-[12px]" v-if="info.basename2"> |
42 | - <span class="tw-leading-[10px] tw-m-[16px]" | |
91 | + <span class="tw-leading-[10px] tw-m-[12px]" | |
43 | 92 | >{{ info.basename2 }}:{{ info.basecore2 }}</span |
44 | 93 | > |
45 | 94 | </div> |
46 | 95 | <div class="tw-w-1/2 tw-mb-[12px]" v-if="info.basename3"> |
47 | - <span class="tw-leading-[10px] tw-m-[16px]" | |
96 | + <span class="tw-leading-[10px] tw-m-[12px]" | |
48 | 97 | >{{ info.basename3 }}:{{ info.basecore3 }}</span |
49 | 98 | > |
50 | 99 | </div> |
... | ... | @@ -60,15 +109,21 @@ |
60 | 109 | :key="index" |
61 | 110 | class="tw-w-1/2 tw-mb-[12px]" |
62 | 111 | > |
63 | - <span class="tw-leading-[10px] tw-m-[16px]" | |
112 | + <span | |
113 | + class="tw-leading-normal tw-m-0 tw-block tw-max-w-full tw-break-words tw-ml-[12px]" | |
64 | 114 | >{{ attribute.name }}:{{ attribute.value }}</span |
65 | 115 | > |
66 | 116 | </div> |
67 | 117 | </div> |
68 | - <v-table | |
118 | + <!-- <v-table | |
69 | 119 | density="comfortable" |
70 | 120 | class="table1 tw-mt-[32px]" |
71 | 121 | v-if="info.ticketTypes?.length" |
122 | + > --> | |
123 | + <v-table | |
124 | + density="comfortable" | |
125 | + class="table1 tw-mt-[32px] tw-overflow-x-auto" | |
126 | + v-if="info.ticketTypes?.length" | |
72 | 127 | > |
73 | 128 | <thead> |
74 | 129 | <tr class="bg-grey-lighten-3"> |
... | ... | @@ -120,63 +175,326 @@ |
120 | 175 | </v-dialog> --> |
121 | 176 | </v-col> |
122 | 177 | </v-row> |
123 | - <div class="tw-pb-[64px]"> | |
124 | - <v-tabs | |
125 | - class="tabs" | |
126 | - v-model="tab" | |
127 | - color="white" | |
128 | - bg-color="#eeeeee" | |
129 | - slider-color="blue-lighten-1" | |
130 | - selected-class="active" | |
178 | + <v-tabs | |
179 | + class="tabs" | |
180 | + v-model="tabRecomHot" | |
181 | + style="margin-top: 25px" | |
182 | + color="white" | |
183 | + bg-color="#eeeeee" | |
184 | + slider-color="blue-lighten-1" | |
185 | + selected-class="active" | |
186 | + v-if="recommendImagesHot[0] !== null" | |
187 | + > | |
188 | + <v-tab :value="1">Best Sellers</v-tab> | |
189 | + <!-- <v-tab :value="3">商品百科</v-tab> --> | |
190 | + </v-tabs> | |
191 | + <div id="image-container" v-if="recommendImagesHot[0] !== null"> | |
192 | + <div class="recommend-left-box"> | |
193 | + <v-img | |
194 | + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/76c987e13a334be481a346c19c2284f3qy4tjnxps7.png" | |
195 | + alt="往左移" | |
196 | + class="recommend-img-left" | |
197 | + @click="toggleRowLeft" | |
198 | + /> | |
199 | + </div> | |
200 | + <div class="image-row" id="row1"> | |
201 | + <!-- <img | |
202 | + v-for="(imageObj, index) in recommendImages.slice(0, 5)" | |
203 | + :key="'row1-' + index" | |
204 | + :src="imageObj[0]?.url" | |
205 | + :alt="'Image ' + (index + 1)" | |
206 | + style="margin: 0 5px; width: 200px; height: 200px" | |
207 | + /> --> | |
208 | + <div | |
209 | + v-for="(imageObj, index) in recommendImagesHot" | |
210 | + :key="'row1-' + index" | |
211 | + class="imageTotal" | |
212 | + > | |
213 | + <a v-if="imageObj" :href="imageObj[0]?.productUrl" target="_blank"> | |
214 | + <img | |
215 | + :src="imageObj[0]?.url" | |
216 | + :alt="'Image ' + (index + 1)" | |
217 | + class="item-imgHot" | |
218 | + /> | |
219 | + <span class="image-name"> | |
220 | + {{ imageObj[0]?.name }} | |
221 | + </span> | |
222 | + </a> | |
223 | + <div v-else style="width: 200px; height: 200px"></div> | |
224 | + </div> | |
225 | + </div> | |
226 | + <div class="recommend-right-box"> | |
227 | + <v-img | |
228 | + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/b5daa0a8f2f140f5b406e984c730a453iznzlekysg.png" | |
229 | + alt="往右移" | |
230 | + class="recommend-img-right" | |
231 | + @click="toggleRowRight" | |
232 | + /> | |
233 | + </div> | |
234 | + </div> | |
235 | + | |
236 | + <div style="display: flex; width: 100%; box-sizing: border-box"> | |
237 | + <div | |
238 | + class="tw-pb-[64px]" | |
239 | + style=" | |
240 | + width: 70%; | |
241 | + margin-right: 10px; | |
242 | + display: flex; | |
243 | + flex-direction: column; | |
244 | + flex-wrap: wrap; | |
245 | + overflow: auto; | |
246 | + " | |
131 | 247 | > |
132 | - <v-tab :value="1">Product Details</v-tab> | |
133 | - <v-tab :value="2">Specification</v-tab> | |
134 | - <!-- <v-tab :value="3">商品百科</v-tab> --> | |
135 | - </v-tabs> | |
136 | - <v-window v-model="tab" class="tw-p-[24px]"> | |
137 | - <v-window-item key="1" :value="1"> | |
138 | - <div v-if="info.advantage" class="tw-mb-[24px]"> | |
139 | - <div class="text-h6">Advantage</div> | |
140 | - <v-divider class="tw-mb-[12px]"></v-divider> | |
141 | - <div v-html="info.advantage"></div> | |
142 | - </div> | |
143 | - <div v-if="info.physicalproperty" class="tw-mb-[24px]"> | |
144 | - <div class="text-h6">Physical Property</div> | |
145 | - <v-divider class="tw-mb-[12px]"></v-divider> | |
146 | - <div v-html="info.physicalproperty"></div> | |
147 | - </div> | |
148 | - <div v-if="info.advantage" class="tw-mb-[24px]"> | |
149 | - <div class="text-h6">Storage</div> | |
150 | - <v-divider class="tw-mb-[12px]"></v-divider> | |
151 | - <div v-html="info.storage"></div> | |
152 | - </div> | |
153 | - <div v-if="info.introduction" class="tw-mb-[24px]"> | |
154 | - <div class="text-h6">Introduction</div> | |
155 | - <v-divider class="tw-mb-[12px]"></v-divider> | |
156 | - <div v-html="info.introduction"></div> | |
157 | - </div> | |
158 | - <div v-if="info.advantage" class="tw-mb-[24px]"> | |
159 | - <div class="text-h6">Description</div> | |
160 | - <v-divider class="tw-mb-[12px]"></v-divider> | |
161 | - <div v-html="info.description"></div> | |
162 | - </div> | |
163 | - </v-window-item> | |
164 | - <v-window-item key="2" :value="2"> | |
165 | - <v-table density="compact" class="table2"> | |
166 | - <tbody> | |
167 | - <tr | |
168 | - class="tr" | |
169 | - v-for="item in info.productAttributeList || []" | |
170 | - :key="item.name" | |
248 | + <v-tabs | |
249 | + class="tabs" | |
250 | + v-model="tab" | |
251 | + color="white" | |
252 | + bg-color="#eeeeee" | |
253 | + slider-color="blue-lighten-1" | |
254 | + selected-class="active" | |
255 | + style="width: 100%" | |
256 | + > | |
257 | + <v-tab :value="1">Product Details</v-tab> | |
258 | + <v-tab :value="2">Specification</v-tab> | |
259 | + <!-- <v-tab :value="3">商品百科</v-tab> --> | |
260 | + </v-tabs> | |
261 | + <v-window | |
262 | + v-model="tab" | |
263 | + class="tw-p-[24px]" | |
264 | + style=" | |
265 | + min-height: 500px; | |
266 | + height: auto; | |
267 | + overflow-wrap: break-word; | |
268 | + word-wrap: break-word; | |
269 | + white-space: pre-wrap; | |
270 | + " | |
271 | + > | |
272 | + <v-window-item key="1" :value="1"> | |
273 | + <!-- 修改位置 1: 使用 clearSpanStyle 方法清理 info.advantage --> | |
274 | + <div v-if="info.advantage" class="tw-mb-[24px]"> | |
275 | + <div class="text-h6">Advantage</div> | |
276 | + <v-divider class="tw-mb-[12px]"></v-divider> | |
277 | + <div v-html="clearSpanStyle(info.advantage)"></div> | |
278 | + </div> | |
279 | + <!-- 修改位置 2: 使用 clearSpanStyle 方法清理 info.physicalproperty --> | |
280 | + <div v-if="info.physicalproperty" class="tw-mb-[24px]"> | |
281 | + <div class="text-h6">Physical Property</div> | |
282 | + <v-divider class="tw-mb-[12px] html-content"></v-divider> | |
283 | + <div v-html="clearSpanStyle(info.physicalproperty)"></div> | |
284 | + </div> | |
285 | + <!-- 修改位置 3: 使用 clearSpanStyle 方法清理 info.storage --> | |
286 | + <div v-if="info.storage" class="tw-mb-[24px]"> | |
287 | + <div class="text-h6">Storage</div> | |
288 | + <v-divider class="tw-mb-[12px]"></v-divider> | |
289 | + <div v-html="clearSpanStyle(info.storage)"></div> | |
290 | + </div> | |
291 | + <!-- 修改位置 4: 使用 clearSpanStyle 方法清理 info.introduction --> | |
292 | + <div v-if="info.introduction" class="tw-mb-[24px]"> | |
293 | + <div class="text-h6">Introduction</div> | |
294 | + <v-divider class="tw-mb-[12px]"></v-divider> | |
295 | + <div v-html="clearSpanStyle(info.introduction)"></div> | |
296 | + </div> | |
297 | + <!-- 修改位置 5: 使用 clearSpanStyle 方法清理 info.description --> | |
298 | + <div v-if="info.description" class="tw-mb-[24px]"> | |
299 | + <div class="text-h6">Description</div> | |
300 | + <v-divider class="tw-mb-[12px]"></v-divider> | |
301 | + <div v-html="clearSpanStyle(info.description)"></div> | |
302 | + </div> | |
303 | + </v-window-item> | |
304 | + <!-- <div v-if="info.advantage" class="tw-mb-[24px]"> | |
305 | + <div class="text-h6">Advantage</div> | |
306 | + <v-divider class="tw-mb-[12px]"></v-divider> | |
307 | + <div v-html="info.advantage"></div> | |
308 | + </div> | |
309 | + <div v-if="info.physicalproperty" class="tw-mb-[24px]"> | |
310 | + <div class="text-h6">Physical Property</div> | |
311 | + <v-divider class="tw-mb-[12px] html-content"></v-divider> | |
312 | + <div | |
313 | + v-html="info.physicalproperty" | |
314 | + style=" | |
315 | + overflow-wrap: break-word; | |
316 | + word-wrap: break-word; | |
317 | + white-space: pre-wrap; | |
318 | + line-height: 1.5; | |
319 | + " | |
320 | + ></div> | |
321 | + </div> | |
322 | + <div v-if="info.storage" class="tw-mb-[24px]"> | |
323 | + <div class="text-h6">Storage</div> | |
324 | + <v-divider class="tw-mb-[12px]"></v-divider> | |
325 | + <div v-html="info.storage"></div> | |
326 | + </div> | |
327 | + <div v-if="info.introduction" class="tw-mb-[24px]"> | |
328 | + <div class="text-h6">Introduction</div> | |
329 | + <v-divider class="tw-mb-[12px]"></v-divider> | |
330 | + <div v-html="info.introduction"></div> | |
331 | + </div> | |
332 | + <div v-if="info.description" class="tw-mb-[24px]"> | |
333 | + <div class="text-h6">Description</div> | |
334 | + <v-divider class="tw-mb-[12px]"></v-divider> | |
335 | + <div v-html="info.description"></div> | |
336 | + </div> | |
337 | + </v-window-item> --> | |
338 | + <v-window-item key="2" :value="2"> | |
339 | + <v-table density="compact" class="table2"> | |
340 | + <tbody> | |
341 | + <tr | |
342 | + class="tr" | |
343 | + v-for="item in info.productAttributeList || []" | |
344 | + :key="item.name" | |
345 | + > | |
346 | + <td class="td tw-w-[400px]">{{ item.name }}</td> | |
347 | + <td class="td">{{ item.value }}</td> | |
348 | + </tr> | |
349 | + </tbody> | |
350 | + </v-table> | |
351 | + </v-window-item> | |
352 | + <!-- <v-window-item key="3" :value="3"> 2 </v-window-item> --> | |
353 | + </v-window> | |
354 | + </div> | |
355 | + <div style="width: 30%"> | |
356 | + <v-tabs | |
357 | + v-model="tabPeriodical" | |
358 | + color="white" | |
359 | + bg-color="#eeeeee" | |
360 | + slider-color="blue-lighten-1" | |
361 | + selected-class="active" | |
362 | + grow | |
363 | + > | |
364 | + <v-tab value="one">Product-related Journals</v-tab> | |
365 | + </v-tabs> | |
366 | + <v-list> | |
367 | + <v-list-item | |
368 | + v-for="item in info.journals" | |
369 | + :key="item.id" | |
370 | + @click="navigateToUrl(item.link)" | |
371 | + @mouseenter="hoveredItem = item.id" | |
372 | + @mouseleave="hoveredItem = null" | |
373 | + > | |
374 | + <v-list-item-title> | |
375 | + <span | |
376 | + :class="['title', { 'full-title': hoveredItem === item.id }]" | |
171 | 377 | > |
172 | - <td class="td tw-w-[400px]">{{ item.name }}</td> | |
173 | - <td class="td">{{ item.value }}</td> | |
174 | - </tr> | |
175 | - </tbody> | |
176 | - </v-table> | |
177 | - </v-window-item> | |
178 | - <!-- <v-window-item key="3" :value="3"> 2 </v-window-item> --> | |
179 | - </v-window> | |
378 | + {{ item.title }} | |
379 | + </span> | |
380 | + </v-list-item-title> | |
381 | + </v-list-item> | |
382 | + </v-list> | |
383 | + </div> | |
384 | + </div> | |
385 | + <v-tabs | |
386 | + class="tabs" | |
387 | + v-model="tabRecom" | |
388 | + color="white" | |
389 | + bg-color="#eeeeee" | |
390 | + slider-color="blue-lighten-1" | |
391 | + selected-class="active" | |
392 | + v-if="recommendImages[0] !== null" | |
393 | + > | |
394 | + <v-tab :value="1">Related Products</v-tab> | |
395 | + <!-- <v-tab :value="3">商品百科</v-tab> --> | |
396 | + </v-tabs> | |
397 | + <div id="image-container" v-if="recommendImages[0] !== null"> | |
398 | + <div class="recommend-left-box"> | |
399 | + <v-img | |
400 | + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/76c987e13a334be481a346c19c2284f3qy4tjnxps7.png" | |
401 | + alt="往左移" | |
402 | + class="recommend-img-left" | |
403 | + @click="toggleRow" | |
404 | + /> | |
405 | + </div> | |
406 | + <div v-if="currentIndex === 0" class="image-row" id="row1"> | |
407 | + <!-- <img | |
408 | + v-for="(imageObj, index) in recommendImages.slice(0, 5)" | |
409 | + :key="'row1-' + index" | |
410 | + :src="imageObj[0]?.url" | |
411 | + :alt="'Image ' + (index + 1)" | |
412 | + style="margin: 0 5px; width: 200px; height: 200px" | |
413 | + /> --> | |
414 | + <div | |
415 | + v-for="(imageObj, index) in recommendImages.slice(0, 5)" | |
416 | + :key="'row1-' + index" | |
417 | + class="imageTotal" | |
418 | + > | |
419 | + <a v-if="imageObj" :href="imageObj[0]?.productUrl" target="_blank"> | |
420 | + <img | |
421 | + :src="imageObj[0]?.url" | |
422 | + :alt="'Image ' + (index + 1)" | |
423 | + class="item-imgHot" | |
424 | + /> | |
425 | + <span class="image-name"> | |
426 | + {{ imageObj[0]?.name }} | |
427 | + </span> | |
428 | + </a> | |
429 | + <div v-else style="width: 200px; height: 200px"></div> | |
430 | + </div> | |
431 | + </div> | |
432 | + <div v-else class="image-row" id="row2"> | |
433 | + <!-- <img | |
434 | + v-for="(imageObj, index) in recommendImages.slice(5, 10)" | |
435 | + :key="'row2-' + index" | |
436 | + :src="imageObj[0]?.url" | |
437 | + :alt="'Image ' + (index + 6)" | |
438 | + style="margin: 0 5px; width: 200px; height: 200px" | |
439 | + /> --> | |
440 | + <div | |
441 | + v-for="(imageObj, index) in recommendImages.slice(5, 10)" | |
442 | + :key="'row2-' + index" | |
443 | + class="imageTotal" | |
444 | + > | |
445 | + <a v-if="imageObj" :href="imageObj[0]?.productUrl" target="_blank"> | |
446 | + <img | |
447 | + :src="imageObj[0]?.url" | |
448 | + :alt="'Image ' + (index + 6)" | |
449 | + class="item-imgHot" | |
450 | + /> | |
451 | + <span class="image-name"> | |
452 | + {{ imageObj[0]?.name }} | |
453 | + </span> | |
454 | + </a> | |
455 | + <div | |
456 | + v-else | |
457 | + style="width: 200px; height: 200px; margin-right: 10px" | |
458 | + ></div> | |
459 | + </div> | |
460 | + </div> | |
461 | + <div class="recommend-right-box"> | |
462 | + <v-img | |
463 | + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/b5daa0a8f2f140f5b406e984c730a453iznzlekysg.png" | |
464 | + alt="往右移" | |
465 | + class="recommend-img-right" | |
466 | + @click="toggleRow" | |
467 | + /> | |
468 | + </div> | |
469 | + </div> | |
470 | + | |
471 | + <div class="social-share-container"> | |
472 | + <SocialShare | |
473 | + network="facebook" | |
474 | + :styled="true" | |
475 | + :label="true" | |
476 | + :title="info.name" | |
477 | + :url="currentUrl" | |
478 | + style="color: #1e88e5; width: 140px" | |
479 | + /> | |
480 | + | |
481 | + <SocialShare | |
482 | + network="twitter" | |
483 | + :styled="true" | |
484 | + :label="true" | |
485 | + :title="info.name" | |
486 | + :url="currentUrl" | |
487 | + style="color: #1e88e5; width: 140px" | |
488 | + /> | |
489 | + | |
490 | + <SocialShare | |
491 | + network="linkedin" | |
492 | + :styled="true" | |
493 | + :label="true" | |
494 | + :title="info.name" | |
495 | + :url="currentUrl" | |
496 | + style="color: #1e88e5; width: 140px" | |
497 | + /> | |
180 | 498 | </div> |
181 | 499 | </v-container> |
182 | 500 | </template> |
... | ... | @@ -185,20 +503,291 @@ |
185 | 503 | import type { Product, ProductImage } from "~/type"; |
186 | 504 | import { onMounted, ref } from "vue"; |
187 | 505 | import { useDialogStore } from "~/stores/dialog"; |
506 | +import { useRouter, useRoute } from "vue-router"; | |
507 | +import { useRouteQuery } from "@/stores/route_query"; | |
508 | + | |
509 | +const recommendList = ref(); | |
510 | +const recommendImages = ref(); | |
511 | +const selectedItem = ref(); | |
512 | +const tabRecom = ref(0); | |
513 | +const tabRecommend = ref(0); | |
514 | +const tabPeriodical = ref(0); | |
515 | +const route = useRoute(); | |
516 | +const router = useRouter(); | |
517 | +const routeQuery = useRouteQuery(); | |
188 | 518 | const dialogStore = useDialogStore(); |
519 | +const href1 = ref("/products"); | |
520 | +const href2 = ref("/products"); | |
521 | +const idHref = ref("/products"); | |
522 | +const hoveredItem = ref(null); | |
523 | +const productStore = useProductListStore(); | |
524 | +const maxPage = ref(1); | |
525 | +const tabRecomHot = ref(); | |
526 | +const recommendListHot = ref(); | |
527 | +const recommendImagesHot = ref(); | |
528 | +const currentIndexHot = ref(1); | |
529 | +const currentUrl = ref("https://www.canrud.com/products"); | |
530 | +// 定义单个 item 的接口 | |
531 | +interface BreadcrumbItem { | |
532 | + title: string; // 标题 | |
533 | + disabled: boolean; // 是否禁用 | |
534 | + href: string; // 链接地址 | |
535 | +} | |
536 | +// 示例数据 | |
537 | +const items = ref<BreadcrumbItem[]>([ | |
538 | + { | |
539 | + title: "Products", | |
540 | + disabled: false, | |
541 | + href: "https://www.canrud.com/products", | |
542 | + }, | |
543 | + { | |
544 | + title: "CATEGORY", | |
545 | + disabled: false, | |
546 | + href: href1.value, | |
547 | + }, | |
548 | + { | |
549 | + title: "Not specified", | |
550 | + disabled: false, | |
551 | + href: href2.value, | |
552 | + }, | |
553 | + { | |
554 | + title: "Not specified", | |
555 | + disabled: false, | |
556 | + href: "/products", | |
557 | + }, | |
558 | +]); | |
189 | 559 | |
190 | 560 | const props = defineProps<{ |
191 | 561 | info: Product; |
192 | 562 | }>(); |
193 | 563 | const info = props.info; |
564 | +let { data: resData } = await useAsyncData( | |
565 | + "list", | |
566 | + () => | |
567 | + $fetch("/shop/product/list", { | |
568 | + method: "GET", | |
569 | + params: { | |
570 | + pageNo: 1, | |
571 | + pageSize: 20, | |
572 | + ids: info.relatedProductIds, | |
573 | + }, | |
574 | + }), | |
575 | + { | |
576 | + server: true, // 仅在服务器端获取数据 | |
577 | + } | |
578 | +); | |
579 | +const loadHotProducts = async () => { | |
580 | + let { data: hotProducts } = await useAsyncData( | |
581 | + "hotProducts", | |
582 | + () => | |
583 | + $fetch("/shop/product/hotProducts", { | |
584 | + method: "GET", | |
585 | + params: { | |
586 | + pageNo: currentIndexHot.value, | |
587 | + pageSize: 5, | |
588 | + }, | |
589 | + }), | |
590 | + { | |
591 | + server: true, // 仅在服务器端获取数据 | |
592 | + } | |
593 | + ); | |
594 | + recommendListHot.value = hotProducts.value.data.records; | |
595 | + maxPage.value = hotProducts.value.data.pages; | |
596 | + // recommendImages.value = recommendList.value.slice(0, 10).map((item) => { | |
597 | + recommendImagesHot.value = Array.from({ length: 5 }).map((_, index) => { | |
598 | + const item = recommendListHot.value[index]; | |
599 | + if (!item) { | |
600 | + return null; | |
601 | + } | |
602 | + // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析 | |
603 | + if (typeof item.productimageliststore === "string") { | |
604 | + try { | |
605 | + item.productimageliststore = JSON.parse(item.productimageliststore); | |
606 | + } catch (error) { | |
607 | + item.productimageliststore = []; // 解析失败时,设置为空数组 | |
608 | + } | |
609 | + } | |
610 | + const ree = (item.productimageliststore = item?.productimageliststore.map( | |
611 | + (productItem: ProductImage) => ({ | |
612 | + ...productItem, | |
613 | + // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`, | |
614 | + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`, | |
615 | + name: item.name, | |
616 | + productUrl: `https://www.canrud.com/products/detail/${item.id}`, | |
617 | + }) | |
618 | + )); | |
619 | + return ree; | |
620 | + }); | |
621 | +}; | |
622 | +const clearSpanStyle = (htmlContent) => { | |
623 | + if (!htmlContent) return ""; // 检查是否为空 | |
624 | + // 创建一个 DOM 解析器 | |
625 | + const parser = new DOMParser(); | |
626 | + const doc = parser.parseFromString(htmlContent, "text/html"); | |
627 | + | |
628 | + // 清理 span 的 style 属性 | |
629 | + const spans = doc.querySelectorAll("span"); | |
630 | + spans.forEach((span) => { | |
631 | + span.removeAttribute("style"); // 清空 style 属性 | |
632 | + }); | |
633 | + | |
634 | + // 修改 img 的 style 属性,追加 width: 100% | |
635 | + const imgs = doc.querySelectorAll("img"); | |
636 | + imgs.forEach((img) => { | |
637 | + const style = img.getAttribute("style") || ""; // 获取现有 style | |
638 | + if (!style.includes("width: 100%")) { | |
639 | + img.setAttribute("style", `${style} width: 100%;`.trim()); // 添加 width: 100% | |
640 | + } | |
641 | + }); | |
642 | + | |
643 | + // 返回处理后的 HTML | |
644 | + return doc.body.innerHTML; | |
645 | +}; | |
646 | +const toggleRowLeft = () => { | |
647 | + if (currentIndexHot.value !== 1) { | |
648 | + currentIndexHot.value--; | |
649 | + } else if (currentIndexHot.value == 1) { | |
650 | + currentIndexHot.value = maxPage.value; | |
651 | + } | |
652 | +}; | |
653 | +const toggleRowRight = () => { | |
654 | + if (currentIndexHot.value < maxPage.value) { | |
655 | + currentIndexHot.value++; | |
656 | + } else if (currentIndexHot.value == maxPage.value) { | |
657 | + currentIndexHot.value = 1; | |
658 | + } | |
659 | +}; | |
660 | +watch(currentIndexHot, (newIndex) => { | |
661 | + loadHotProducts(); // Call loadHotProducts when currentIndex changes | |
662 | +}); | |
663 | + | |
664 | +// Initial load of hot products | |
665 | +await loadHotProducts(); // Load hot products the first time | |
666 | + | |
667 | +watchEffect(() => { | |
668 | + console.log(info, "5656info"); | |
669 | + currentUrl.value = "https://www.canrud.com/products/detail/" + info.id; | |
670 | + if (info?.productCrumbsVO?.category1 && productStore.keyword) { | |
671 | + items.value[1].title = info.productCrumbsVO.category1; | |
672 | + items.value[1].href = | |
673 | + "https://www.canrud.com/products?categories=" + | |
674 | + info.productCrumbsVO.category1; | |
675 | + href1.value = | |
676 | + "https://www.canrud.com/products?categories=" + | |
677 | + info.productCrumbsVO.category1; | |
678 | + if (info?.productCrumbsVO?.category2) { | |
679 | + items.value[2].title = info.productCrumbsVO.category2; | |
680 | + href2.value = href1.value + "," + info.productCrumbsVO.category2; | |
681 | + items.value[2].href = href1.value + "," + info.productCrumbsVO.category2; | |
682 | + } | |
683 | + if (info?.productCrumbsVO?.function) { | |
684 | + // items.value.push({ | |
685 | + // title: info.productCrumbsVO.function, | |
686 | + // disabled: false, | |
687 | + // href: href2.value + "&function=" + info.productCrumbsVO.function, | |
688 | + // }); | |
689 | + items.value[3].title = info.productCrumbsVO.function; | |
690 | + items.value[3].href = | |
691 | + href2.value + "&function=" + info.productCrumbsVO.function; | |
692 | + } | |
693 | + } else if (routeQuery.categories) { | |
694 | + if (!routeQuery.categories.includes("Energy materials")) { | |
695 | + routeQuery.updateFunction("Not specified"); | |
696 | + } | |
697 | + const categories = routeQuery.categories.split(","); | |
698 | + const mainCategory = categories[0].trim(); // 取第一个值 | |
699 | + const subCategoryName = ref( | |
700 | + categories[1] ? categories[1].trim() : "Not specified" | |
701 | + ); // 取第二个值(如果存在) | |
702 | + if (subCategoryName.value == "Accessories & fixtures") { | |
703 | + subCategoryName.value = encodeURIComponent("Accessories & fixtures"); | |
704 | + items.value[2].title = "Accessories & fixtures"; | |
705 | + } else { | |
706 | + items.value[2].title = subCategoryName.value; | |
707 | + } | |
708 | + items.value[1].title = mainCategory; | |
709 | + items.value[1].href = | |
710 | + "https://www.canrud.com/products?categories=" + mainCategory; | |
711 | + href1.value = "https://www.canrud.com/products?categories=" + mainCategory; | |
712 | + // items.value[1].title = subCategoryName.value; | |
713 | + href2.value = href1.value + "," + subCategoryName.value; | |
714 | + items.value[2].href = href1.value + "," + subCategoryName.value; | |
715 | + if (routeQuery?.selectedFunction) { | |
716 | + // items.value.push({ | |
717 | + // title: routeQuery.selectedFunction, | |
718 | + // disabled: false, | |
719 | + // href: href2.value + "&function=" + routeQuery.selectedFunction, | |
720 | + // }); | |
721 | + items.value[3].title = routeQuery.selectedFunction; | |
722 | + items.value[3].href = | |
723 | + href2.value + "&function=" + routeQuery.selectedFunction; | |
724 | + // routeQuery.updateFunction(null); | |
725 | + } | |
726 | + } else if (info?.productCrumbsVO?.category1) { | |
727 | + items.value[1].title = info.productCrumbsVO.category1; | |
728 | + items.value[1].href = | |
729 | + "https://www.canrud.com/products?categories=" + | |
730 | + info.productCrumbsVO.category1; | |
731 | + href1.value = | |
732 | + "https://www.canrud.com/products?categories=" + | |
733 | + info.productCrumbsVO.category1; | |
734 | + if (info?.productCrumbsVO?.category2) { | |
735 | + items.value[2].title = info.productCrumbsVO.category2; | |
736 | + href2.value = href1.value + "," + info.productCrumbsVO.category2; | |
737 | + items.value[2].href = href1.value + "," + info.productCrumbsVO.category2; | |
738 | + } | |
739 | + if (info?.productCrumbsVO?.function) { | |
740 | + // items.value.push({ | |
741 | + // title: info.productCrumbsVO.function, | |
742 | + // disabled: false, | |
743 | + // href: href2.value + "&function=" + info.productCrumbsVO.function, | |
744 | + // }); | |
745 | + items.value[3].title = info.productCrumbsVO.function; | |
746 | + items.value[3].href = | |
747 | + href2.value + "&function=" + info.productCrumbsVO.function; | |
748 | + } | |
749 | + } | |
750 | + recommendList.value = resData.value.data.records; | |
194 | 751 | |
752 | + // recommendImages.value = recommendList.value.slice(0, 10).map((item) => { | |
753 | + recommendImages.value = Array.from({ length: 10 }).map((_, index) => { | |
754 | + const item = recommendList.value[index]; | |
755 | + if (!item) { | |
756 | + return null; | |
757 | + } | |
758 | + // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析 | |
759 | + if (typeof item.productimageliststore === "string") { | |
760 | + try { | |
761 | + item.productimageliststore = JSON.parse(item.productimageliststore); | |
762 | + } catch (error) { | |
763 | + item.productimageliststore = []; // 解析失败时,设置为空数组 | |
764 | + } | |
765 | + } | |
766 | + const ree = (item.productimageliststore = item?.productimageliststore.map( | |
767 | + (productItem: ProductImage) => ({ | |
768 | + ...productItem, | |
769 | + // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`, | |
770 | + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`, | |
771 | + name: item.name, | |
772 | + productUrl: `https://www.canrud.com/products/detail/${item.id}`, | |
773 | + }) | |
774 | + )); | |
775 | + return ree; | |
776 | + }); | |
777 | +}); | |
195 | 778 | // onMounted(() => { |
196 | 779 | // dialogStore.updateDialog(true) |
197 | 780 | // }) |
781 | +const currentIndex = ref(0); | |
782 | +const navigateToUrl = (url) => { | |
783 | + window.open(url); // 在新标签页中打开链接 | |
784 | +}; | |
785 | +const toggleRow = () => { | |
786 | + currentIndex.value = currentIndex.value === 0 ? 1 : 0; | |
787 | +}; | |
198 | 788 | |
199 | 789 | const tab = ref(0); |
200 | 790 | const slide = ref(0); |
201 | -const router = useRouter(); | |
202 | 791 | </script> |
203 | 792 | |
204 | 793 | <style lang="scss" scoped> |
... | ... | @@ -206,10 +795,32 @@ const router = useRouter(); |
206 | 795 | border-top: 3px solid #1f88e5 !important; |
207 | 796 | } |
208 | 797 | |
209 | -.tabs { | |
210 | - border-bottom: 2px solid #1f88e5; | |
798 | +@media screen and (min-width: 1537px) { | |
799 | + .tabs { | |
800 | + border-bottom: 2px solid #1f88e5; | |
801 | + margin: 10px auto 20px; | |
802 | + width: 100%; | |
803 | + } | |
804 | +} | |
805 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
806 | + .tabs { | |
807 | + border-bottom: 2px solid #1f88e5; | |
808 | + margin: 10px auto 40px; | |
809 | + width: 100%; | |
810 | + } | |
811 | +} | |
812 | +@media screen and (max-width: 1280px) { | |
813 | + .tabs { | |
814 | + border-bottom: 2px solid #1f88e5; | |
815 | + margin: 10px auto 40px; | |
816 | + width: 100%; | |
817 | + } | |
211 | 818 | } |
212 | 819 | |
820 | +// .tabs { | |
821 | +// border-bottom: 2px solid #1f88e5; | |
822 | +// } | |
823 | + | |
213 | 824 | .active { |
214 | 825 | background-color: #1086e8; |
215 | 826 | } |
... | ... | @@ -231,4 +842,234 @@ th { |
231 | 842 | border-bottom: 1px solid #dcdcdc !important; |
232 | 843 | } |
233 | 844 | } |
845 | + | |
846 | +.tw-sticky { | |
847 | + position: sticky; | |
848 | +} | |
849 | + | |
850 | +.carousel-container { | |
851 | + position: relative; | |
852 | +} | |
853 | + | |
854 | +.table-container { | |
855 | + overflow-x: auto; /* 防止表格超出页面宽度 */ | |
856 | +} | |
857 | + | |
858 | +.table1 { | |
859 | + width: 100%; | |
860 | + min-width: 600px; /* 设置表格最小宽度 */ | |
861 | +} | |
862 | + | |
863 | +.tr { | |
864 | + border-bottom: 1px solid #e0e0e0; /* 表格行样式 */ | |
865 | +} | |
866 | + | |
867 | +.headerBorder { | |
868 | + border-bottom: 2px solid #ccc; /* 表头边框 */ | |
869 | +} | |
870 | + | |
871 | +.text-grey-darken-4 { | |
872 | + color: #333; /* 表格文字颜色 */ | |
873 | +} | |
874 | +.breadcrumb-disabled { | |
875 | + color: black; | |
876 | + pointer-events: none; /* 禁用点击 */ | |
877 | + text-decoration: none; /* 移除链接样式 */ | |
878 | +} | |
879 | + | |
880 | +.breadcrumb-active { | |
881 | + color: #1e88e5; | |
882 | +} | |
883 | + | |
884 | +@media screen and (min-width: 1537px) { | |
885 | + #image-container { | |
886 | + display: flex; | |
887 | + align-items: center; | |
888 | + justify-content: center; | |
889 | + height: 320px; | |
890 | + margin: 10px auto 80px; | |
891 | + width: 100%; | |
892 | + } | |
893 | +} | |
894 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
895 | + #image-container { | |
896 | + display: flex; | |
897 | + align-items: center; | |
898 | + justify-content: center; | |
899 | + height: 320px; | |
900 | + margin: 10px auto 0px; | |
901 | + width: 80%; | |
902 | + padding: 0; | |
903 | + } | |
904 | +} | |
905 | +@media screen and (max-width: 1280px) { | |
906 | + #image-container { | |
907 | + display: flex; | |
908 | + align-items: center; | |
909 | + justify-content: center; | |
910 | + height: 320px; | |
911 | + margin: 10px auto 0px; | |
912 | + width: 80%; | |
913 | + padding: 0; | |
914 | + } | |
915 | +} | |
916 | +.image-row { | |
917 | + display: flex; | |
918 | + height: 245px; | |
919 | +} | |
920 | +@media screen and (min-width: 1537px) { | |
921 | + .imageTotal { | |
922 | + display: inline-block; | |
923 | + margin: 0 5px; | |
924 | + text-align: center; | |
925 | + width: 300px; | |
926 | + } | |
927 | +} | |
928 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
929 | + .imageTotal { | |
930 | + display: inline-block; | |
931 | + margin: 0 5px; | |
932 | + text-align: center; | |
933 | + width: 200px; | |
934 | + } | |
935 | +} | |
936 | +@media screen and (max-width: 1280px) { | |
937 | + .imageTotal { | |
938 | + display: inline-block; | |
939 | + margin: 0 5px; | |
940 | + text-align: center; | |
941 | + width: 200px; | |
942 | + } | |
943 | +} | |
944 | +@media screen and (min-width: 1537px) { | |
945 | + .image-row img { | |
946 | + width: 200px; | |
947 | + height: 200px; | |
948 | + } | |
949 | +} | |
950 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
951 | + .image-row img { | |
952 | + width: 140px; | |
953 | + height: 140px; | |
954 | + } | |
955 | +} | |
956 | +@media screen and (max-width: 1280px) { | |
957 | + .image-row img { | |
958 | + width: 160px; | |
959 | + height: 160px; | |
960 | + } | |
961 | +} | |
962 | +@media screen and (min-width: 1537px) { | |
963 | + .image-name { | |
964 | + display: -webkit-box; /* Enables multi-line text handling */ | |
965 | + -webkit-box-orient: vertical; /* Defines the vertical orientation of the box */ | |
966 | + -webkit-line-clamp: 3; /* Limits the text to 3 lines */ | |
967 | + overflow: hidden; /* Hides the overflowed text */ | |
968 | + text-overflow: ellipsis; /* Adds an ellipsis when the text overflows */ | |
969 | + margin-top: 5px; | |
970 | + font-size: 16px; | |
971 | + width: 180px; | |
972 | + color: #555; | |
973 | + text-align: center; /* Centers the text horizontally */ | |
974 | + margin-left: 50px; | |
975 | + } | |
976 | +} | |
977 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
978 | + .image-name { | |
979 | + display: -webkit-box; /* Enables multi-line text handling */ | |
980 | + -webkit-box-orient: vertical; /* Defines the vertical orientation of the box */ | |
981 | + -webkit-line-clamp: 3; /* Limits the text to 3 lines */ | |
982 | + overflow: hidden; /* Hides the overflowed text */ | |
983 | + text-overflow: ellipsis; /* Adds an ellipsis when the text overflows */ | |
984 | + margin-top: 5px; | |
985 | + font-size: 16px; | |
986 | + width: 180px; | |
987 | + color: #555; | |
988 | + text-align: center; /* Centers the text horizontally */ | |
989 | + margin-left: 10px; | |
990 | + } | |
991 | +} | |
992 | +@media screen and (max-width: 1280px) { | |
993 | + .image-name { | |
994 | + display: -webkit-box; /* Enables multi-line text handling */ | |
995 | + -webkit-box-orient: vertical; /* Defines the vertical orientation of the box */ | |
996 | + -webkit-line-clamp: 3; /* Limits the text to 3 lines */ | |
997 | + overflow: hidden; /* Hides the overflowed text */ | |
998 | + text-overflow: ellipsis; /* Adds an ellipsis when the text overflows */ | |
999 | + margin-top: 5px; | |
1000 | + font-size: 16px; | |
1001 | + width: 180px; | |
1002 | + color: #555; | |
1003 | + text-align: center; /* Centers the text horizontally */ | |
1004 | + margin-left: 20px; | |
1005 | + } | |
1006 | +} | |
1007 | + | |
1008 | +button .recommendButton { | |
1009 | + margin: 0 0px; | |
1010 | + cursor: pointer; | |
1011 | +} | |
1012 | + | |
1013 | +.recommend-left-box { | |
1014 | +} | |
1015 | + | |
1016 | +.recommend-img-left { | |
1017 | + width: 26px; | |
1018 | + height: 27px; | |
1019 | + margin-right: 30px; | |
1020 | + margin-bottom: 50px; | |
1021 | +} | |
1022 | + | |
1023 | +.recommend-img-left:hover { | |
1024 | + cursor: pointer; | |
1025 | +} | |
1026 | +.recommend-right-box { | |
1027 | +} | |
1028 | + | |
1029 | +.recommend-img-right { | |
1030 | + width: 26px; | |
1031 | + height: 27px; | |
1032 | + margin-left: 30px; | |
1033 | + margin-bottom: 50px; | |
1034 | +} | |
1035 | + | |
1036 | +.recommend-img-right:hover { | |
1037 | + cursor: pointer; | |
1038 | +} | |
1039 | + | |
1040 | +.social-share-container { | |
1041 | + display: flex; | |
1042 | + flex-direction: row; /* 父容器横向排列 */ | |
1043 | + gap: 1rem; /* 设置每个分享项之间的间距 */ | |
1044 | + margin-top: 40px; | |
1045 | +} | |
1046 | + | |
1047 | +.social-share-item { | |
1048 | + display: flex; | |
1049 | + flex-direction: row; /* 每个分享项横向排列 */ | |
1050 | + align-items: center; /* 垂直居中对齐 */ | |
1051 | + gap: 0.5rem; /* 标题与按钮之间的间距 */ | |
1052 | +} | |
1053 | + | |
1054 | +.social-share-title { | |
1055 | + font-size: 1rem; | |
1056 | + font-weight: bold; | |
1057 | +} | |
1058 | + | |
1059 | +.title { | |
1060 | + font-size: 15px; /* 设置字体大小为24px */ | |
1061 | + color: black; /* 设置字体颜色为黑色 */ | |
1062 | + display: -webkit-box; /* 使用盒模型显示多行文本 */ | |
1063 | + -webkit-line-clamp: 2; /* 限制为两行 */ | |
1064 | + -webkit-box-orient: vertical; /* 盒子方向为垂直 */ | |
1065 | + overflow: hidden; /* 超过部分隐藏 */ | |
1066 | + transition: max-height 0.3s ease; /* 过渡效果 */ | |
1067 | +} | |
1068 | + | |
1069 | +.full-title { | |
1070 | + display: block; /* 当悬浮时显示为块级元素 */ | |
1071 | + color: #1e88e5; | |
1072 | + white-space: normal; /* 允许换行 */ | |
1073 | + overflow: visible; /* 显示全部内容 */ | |
1074 | +} | |
234 | 1075 | </style> | ... | ... |
deploy/prod2.sh
nuxt.config.ts
... | ... | @@ -20,7 +20,16 @@ export default defineNuxtConfig({ |
20 | 20 | // baseURL: 'http://47.89.254.121:8002/shop' || 'http://39.108.227.113:8002' // Exposed to the frontend as well. |
21 | 21 | // } |
22 | 22 | // }, |
23 | - modules: ["vuetify-nuxt-module", "@pinia/nuxt", "@nuxtjs/i18n"], | |
23 | + modules: [ | |
24 | + "vuetify-nuxt-module", | |
25 | + "@pinia/nuxt", | |
26 | + "@nuxtjs/i18n", | |
27 | + "@stefanobartoletti/nuxt-social-share", | |
28 | + ], | |
29 | + // optional configuration, should be added manually | |
30 | + socialShare: { | |
31 | + // module options | |
32 | + }, | |
24 | 33 | vuetify: { |
25 | 34 | moduleOptions: { |
26 | 35 | /* module specific options */ | ... | ... |
package-lock.json
... | ... | @@ -8,6 +8,7 @@ |
8 | 8 | "hasInstallScript": true, |
9 | 9 | "dependencies": { |
10 | 10 | "@pinia/nuxt": "^0.5.1", |
11 | + "@stefanobartoletti/nuxt-social-share": "^1.2.0", | |
11 | 12 | "lodash": "^4.17.21", |
12 | 13 | "nuxt": "^3.11.2", |
13 | 14 | "vue": "^3.4.27", |
... | ... | @@ -2440,6 +2441,15 @@ |
2440 | 2441 | "url": "https://github.com/sponsors/sindresorhus" |
2441 | 2442 | } |
2442 | 2443 | }, |
2444 | + "node_modules/@stefanobartoletti/nuxt-social-share": { | |
2445 | + "version": "1.2.0", | |
2446 | + "resolved": "https://registry.npmmirror.com/@stefanobartoletti/nuxt-social-share/-/nuxt-social-share-1.2.0.tgz", | |
2447 | + "integrity": "sha512-iehssGh971g49CFU3A9leUpAgrLMb9gPFIO7OORyh6ghclc6HNr7QaGnOXihAM0VTJRIqiL3f3AEMiAvwT79Kg==", | |
2448 | + "dependencies": { | |
2449 | + "@nuxt/kit": "^3.13.2", | |
2450 | + "defu": "^6.1.4" | |
2451 | + } | |
2452 | + }, | |
2443 | 2453 | "node_modules/@tailwindcss/aspect-ratio": { |
2444 | 2454 | "version": "0.4.2", |
2445 | 2455 | "resolved": "https://registry.npmmirror.com/@tailwindcss/aspect-ratio/-/aspect-ratio-0.4.2.tgz", | ... | ... |
package.json
... | ... | @@ -11,6 +11,7 @@ |
11 | 11 | }, |
12 | 12 | "dependencies": { |
13 | 13 | "@pinia/nuxt": "^0.5.1", |
14 | + "@stefanobartoletti/nuxt-social-share": "^1.2.0", | |
14 | 15 | "lodash": "^4.17.21", |
15 | 16 | "nuxt": "^3.11.2", |
16 | 17 | "vue": "^3.4.27", |
... | ... | @@ -25,4 +26,4 @@ |
25 | 26 | "sass": "^1.77.1", |
26 | 27 | "tailwindcss": "^3.4.3" |
27 | 28 | } |
28 | -} | |
29 | 29 | \ No newline at end of file |
30 | +} | ... | ... |
pages/index.vue
1 | 1 | <template> |
2 | 2 | <v-rows class="tw-w-full"> |
3 | - <v-carousel hide-delimiter-background show-arrows="hover" height="auto" v-if="!isMobile()"> | |
4 | - <v-carousel-item v-for="banner in banners" :src="banner" cover alt="canrud" :key="banner"> | |
3 | + <v-carousel | |
4 | + hide-delimiter-background | |
5 | + show-arrows="hover" | |
6 | + height="auto" | |
7 | + v-if="!isMobile()" | |
8 | + > | |
9 | + <v-carousel-item | |
10 | + v-for="banner in banners" | |
11 | + :src="banner" | |
12 | + cover | |
13 | + alt="canrud" | |
14 | + :key="banner" | |
15 | + > | |
5 | 16 | </v-carousel-item> |
6 | 17 | </v-carousel> |
7 | - <v-carousel hide-delimiter-background show-arrows="hover" height="auto" v-if="isMobile()"> | |
8 | - <v-carousel-item v-for="banner in mobileBanners" :src="banner" cover alt="canrud" :key="banner"> | |
18 | + <v-carousel | |
19 | + hide-delimiter-background | |
20 | + show-arrows="hover" | |
21 | + height="auto" | |
22 | + v-if="isMobile()" | |
23 | + > | |
24 | + <v-carousel-item | |
25 | + v-for="banner in mobileBanners" | |
26 | + :src="banner" | |
27 | + cover | |
28 | + alt="canrud" | |
29 | + :key="banner" | |
30 | + > | |
9 | 31 | </v-carousel-item> |
10 | 32 | </v-carousel> |
11 | 33 | </v-rows> |
34 | + <v-tabs | |
35 | + class="tabs" | |
36 | + v-model="tabRecom" | |
37 | + color="white" | |
38 | + bg-color="#eeeeee" | |
39 | + style="margin-top: 40px" | |
40 | + slider-color="blue-lighten-1" | |
41 | + selected-class="active" | |
42 | + v-if="recommendImages[0] !== null && !isMobile()" | |
43 | + > | |
44 | + <v-tab :value="1">Best Sellers</v-tab> | |
45 | + <!-- <v-tab :value="3">商品百科</v-tab> --> | |
46 | + </v-tabs> | |
47 | + <div id="image-container" v-if="recommendImages[0] !== null && !isMobile()"> | |
48 | + <div class="recommend-left-box"> | |
49 | + <v-img | |
50 | + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/76c987e13a334be481a346c19c2284f3qy4tjnxps7.png" | |
51 | + alt="往左移" | |
52 | + class="recommend-img-left" | |
53 | + @click="toggleRowLeft" | |
54 | + /> | |
55 | + </div> | |
56 | + <div class="image-row" id="row1"> | |
57 | + <!-- <img | |
58 | + v-for="(imageObj, index) in recommendImages.slice(0, 5)" | |
59 | + :key="'row1-' + index" | |
60 | + :src="imageObj[0]?.url" | |
61 | + :alt="'Image ' + (index + 1)" | |
62 | + style="margin: 0 5px; width: 200px; height: 200px" | |
63 | + /> --> | |
64 | + <div | |
65 | + v-for="(imageObj, index) in recommendImages" | |
66 | + :key="'row1-' + index" | |
67 | + class="imageTotal" | |
68 | + > | |
69 | + <a v-if="imageObj" :href="imageObj[0]?.productUrl" target="_blank"> | |
70 | + <img | |
71 | + :src="imageObj[0]?.url" | |
72 | + :alt="'Image ' + (index + 1)" | |
73 | + class="item-imgHot" | |
74 | + /> | |
75 | + <span class="image-name"> | |
76 | + {{ imageObj[0]?.name }} | |
77 | + </span> | |
78 | + </a> | |
79 | + <div v-else style="width: 200px; height: 200px"></div> | |
80 | + </div> | |
81 | + </div> | |
82 | + <div class="recommend-right-box"> | |
83 | + <v-img | |
84 | + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/b5daa0a8f2f140f5b406e984c730a453iznzlekysg.png" | |
85 | + alt="往右移" | |
86 | + class="recommend-img-right" | |
87 | + @click="toggleRowRight" | |
88 | + /> | |
89 | + </div> | |
90 | + </div> | |
91 | + <div style="padding-left: 16px; padding-right: 16px"> | |
92 | + <v-tabs | |
93 | + class="tabs2" | |
94 | + ref="tabs2" | |
95 | + v-model="tabRecom" | |
96 | + style="margin-top: 25px; margin-bottom: 20px; width: 100%" | |
97 | + color="white" | |
98 | + bg-color="#eeeeee" | |
99 | + slider-color="blue-lighten-1" | |
100 | + selected-class="active" | |
101 | + v-if="isMobile()" | |
102 | + > | |
103 | + <v-tab :value="1">Best Sellers</v-tab> | |
104 | + <!-- <v-tab :value="3">商品百科</v-tab> --> | |
105 | + </v-tabs> | |
106 | + <div class="tw-text-center" v-if="hotLoading && isMobile()"> | |
107 | + <v-progress-circular | |
108 | + color="blue-lighten-2" | |
109 | + indeterminate | |
110 | + size="64" | |
111 | + class="tw-m-auto" | |
112 | + ></v-progress-circular> | |
113 | + </div> | |
114 | + <v-item-group multiple v-if="isMobile()"> | |
115 | + <v-row v-if="!hotLoading"> | |
116 | + <v-col | |
117 | + v-for="(item, i) in recommendImages" | |
118 | + :key="i" | |
119 | + cols="6" | |
120 | + lg="3" | |
121 | + md="4" | |
122 | + sm="6" | |
123 | + > | |
124 | + <div v-if="item !== null"> | |
125 | + <v-card :elevation="4" class="mx-auto" :href="item[0].productUrl"> | |
126 | + <v-img | |
127 | + :src="item[0].url" | |
128 | + :alt="item[0].name" | |
129 | + eager | |
130 | + class="d-block" | |
131 | + /> | |
132 | + <v-card-text class="tw-text-left font-weight-medium title"> | |
133 | + <h4>{{ item[0].name }}</h4> | |
134 | + </v-card-text> | |
135 | + </v-card> | |
136 | + </div> | |
137 | + </v-col> | |
138 | + </v-row> | |
139 | + </v-item-group> | |
140 | + <v-row v-if="isMobile()"> | |
141 | + <v-col> | |
142 | + <v-pagination | |
143 | + :size="isMobile() ? 'small' : 'default'" | |
144 | + v-if="hotTotal" | |
145 | + v-model="currentIndex" | |
146 | + @update:modelValue="toggleRowMobile" | |
147 | + :length="hotLength" | |
148 | + rounded="0" | |
149 | + class="tw-float-right tw-mt-[32px]" | |
150 | + total-visible="5" | |
151 | + ></v-pagination></v-col | |
152 | + ></v-row> | |
153 | + </div> | |
12 | 154 | <!-- 能源材料 --> |
13 | 155 | <div class="tw-py-8 py-sm-16"> |
14 | 156 | <v-container> |
15 | - <MainTitleListOdd title="Material Reagents" :list="materials" | |
16 | - desc="Leading global provider of energy storage research materials and providing other professional/universal experimental materials. " /> | |
157 | + <MainTitleListOdd | |
158 | + title="Material Reagents" | |
159 | + :list="materials" | |
160 | + desc="Leading global provider of energy storage research materials and providing other professional/universal experimental materials. " | |
161 | + /> | |
17 | 162 | </v-container> |
18 | 163 | </div> |
19 | 164 | |
20 | 165 | <!-- 设备 --> |
21 | 166 | <div class="bg-grey-lighten-5 tw-py-8 py-sm-16"> |
22 | 167 | <v-container> |
23 | - <MainTitleList title="Lab Device" listType="equipment" | |
168 | + <MainTitleList | |
169 | + title="Lab Device" | |
170 | + listType="equipment" | |
24 | 171 | :list="lab.list.map((item) => ({ ...item, href: '/products' }))" |
25 | 172 | desc="Self-built High-precision Machining Center with Powerful Design and Manufacturing Capabilities. " |
26 | - href="/equipment" /> | |
173 | + href="/equipment" | |
174 | + /> | |
27 | 175 | </v-container> |
28 | 176 | </div> |
29 | 177 | |
30 | 178 | <!-- Customized Battery --> |
31 | 179 | <div class="tw-py-8 py-sm-16"> |
32 | 180 | <v-container> |
33 | - <MainTitleList :title="('Customized Battery')" :list="batteries" href="/customize" | |
34 | - desc="200mAh~10Ah, Winding/Stacking, Unfilled/Filled Electrolyte Cells, Three-Electrode, and More. " /> | |
181 | + <MainTitleList | |
182 | + :title="'Customized Battery'" | |
183 | + :list="batteries" | |
184 | + href="/customize" | |
185 | + desc="200mAh~10Ah, Winding/Stacking, Unfilled/Filled Electrolyte Cells, Three-Electrode, and More. " | |
186 | + /> | |
35 | 187 | </v-container> |
36 | 188 | </div> |
37 | 189 | <!-- Testing --> |
38 | 190 | <div class="bg-grey-lighten-5 tw-py-8 py-sm-16"> |
39 | 191 | <v-container> |
40 | - <MainTitleList title="Testing" :list="tests" href="/test" | |
41 | - desc="Self built testing center and signed strategic cooperation with ATL, Tsinghua and other units. " /> | |
192 | + <MainTitleList | |
193 | + title="Testing" | |
194 | + :list="tests" | |
195 | + href="/test" | |
196 | + desc="Self built testing center and signed strategic cooperation with ATL, Tsinghua and other units. " | |
197 | + /> | |
42 | 198 | </v-container> |
43 | 199 | </div> |
44 | 200 | <!-- Pack --> |
45 | 201 | <div class="pt-8 pb-8 pt pt-sm-16 pb-sm-32"> |
46 | 202 | <v-container> |
47 | - <MainTitleList title="Pack" href="/pack" :list="packs" | |
48 | - desc="Focusing on energy materials/new energy storage systems/modules and other fields, mastering advanced technologies to provide high-quality services. " /> | |
203 | + <MainTitleList | |
204 | + title="Pack" | |
205 | + href="/pack" | |
206 | + :list="packs" | |
207 | + desc="Focusing on energy materials/new energy storage systems/modules and other fields, mastering advanced technologies to provide high-quality services. " | |
208 | + /> | |
49 | 209 | </v-container> |
50 | 210 | </div> |
211 | + <div style="padding-left: 16px; padding-right: 16px; padding-bottom: 30px"> | |
212 | + <v-tabs | |
213 | + class="tabs2" | |
214 | + ref="tabs2" | |
215 | + v-model="tabRecom" | |
216 | + style="margin-top: 25px; margin-bottom: 20px; width: 100%" | |
217 | + color="white" | |
218 | + bg-color="#eeeeee" | |
219 | + slider-color="blue-lighten-1" | |
220 | + selected-class="active" | |
221 | + v-if="isMobile()" | |
222 | + > | |
223 | + <v-tab :value="1">Best Sellers</v-tab> | |
224 | + <!-- <v-tab :value="3">商品百科</v-tab> --> | |
225 | + </v-tabs> | |
226 | + <div class="tw-text-center" v-if="hotLoading && isMobile()"> | |
227 | + <v-progress-circular | |
228 | + color="blue-lighten-2" | |
229 | + indeterminate | |
230 | + size="64" | |
231 | + class="tw-m-auto" | |
232 | + ></v-progress-circular> | |
233 | + </div> | |
234 | + <v-item-group multiple v-if="isMobile()"> | |
235 | + <v-row v-if="!hotLoading"> | |
236 | + <v-col | |
237 | + v-for="(item, i) in recommendImages" | |
238 | + :key="i" | |
239 | + cols="6" | |
240 | + lg="3" | |
241 | + md="4" | |
242 | + sm="6" | |
243 | + > | |
244 | + <div v-if="item !== null"> | |
245 | + <v-card :elevation="4" class="mx-auto" :href="item[0].productUrl"> | |
246 | + <v-img | |
247 | + :src="item[0].url" | |
248 | + :alt="item[0].name" | |
249 | + eager | |
250 | + class="d-block" | |
251 | + /> | |
252 | + <v-card-text class="tw-text-left font-weight-medium title"> | |
253 | + <h4>{{ item[0].name }}</h4> | |
254 | + </v-card-text> | |
255 | + </v-card> | |
256 | + </div> | |
257 | + </v-col> | |
258 | + </v-row> | |
259 | + </v-item-group> | |
260 | + <v-row v-if="isMobile()"> | |
261 | + <v-col> | |
262 | + <v-pagination | |
263 | + :size="isMobile() ? 'small' : 'default'" | |
264 | + v-if="hotTotal" | |
265 | + v-model="currentIndex" | |
266 | + @update:modelValue="toggleRowMobile" | |
267 | + :length="hotLength" | |
268 | + rounded="0" | |
269 | + class="tw-float-right tw-mt-[32px]" | |
270 | + total-visible="5" | |
271 | + ></v-pagination></v-col | |
272 | + ></v-row> | |
273 | + </div> | |
51 | 274 | </template> |
52 | 275 | |
53 | 276 | <script setup lang="ts"> |
54 | -import MainTitleList from '../components/MainTitleList.vue' | |
55 | -import MainTitleListOdd from '../components/MainTitleListOdd.vue' | |
56 | -import { useCategoryStore } from '../stores/category' | |
57 | -import { computed } from 'vue' | |
58 | -import { isMobile } from '../utils' | |
277 | +import MainTitleList from "../components/MainTitleList.vue"; | |
278 | +import MainTitleListOdd from "../components/MainTitleListOdd.vue"; | |
279 | +import { useCategoryStore } from "../stores/category"; | |
280 | +import { computed, onMounted, reactive, ref } from "vue"; | |
281 | +import { isMobile } from "../utils"; | |
282 | + | |
283 | +const maxPage = ref(1); | |
284 | +const tabRecom = ref(); | |
285 | +const recommendList = ref(); | |
286 | +const recommendImages = ref(); | |
287 | +const currentIndex = ref(1); | |
288 | +const hotLoading = ref(false); | |
289 | +const hotTotal = ref(10); | |
290 | +const isOrNotMobile = isMobile(); | |
291 | +const loadHotProducts = async () => { | |
292 | + const pageSize = ref(5); | |
293 | + if (isOrNotMobile) { | |
294 | + pageSize.value = 4; | |
295 | + } | |
296 | + hotLoading.value = true; | |
297 | + let { data: hotProducts } = await useAsyncData( | |
298 | + "hotProducts", | |
299 | + () => | |
300 | + $fetch("/shop/product/hotProducts", { | |
301 | + method: "GET", | |
302 | + params: { | |
303 | + pageNo: currentIndex.value, | |
304 | + pageSize: pageSize.value, | |
305 | + }, | |
306 | + }), | |
307 | + { | |
308 | + server: true, // 仅在服务器端获取数据 | |
309 | + } | |
310 | + ); | |
311 | + hotTotal.value = hotProducts.value.data.total; | |
312 | + recommendList.value = hotProducts.value.data.records; | |
313 | + maxPage.value = hotProducts.value.data.pages; | |
314 | + // recommendImages.value = recommendList.value.slice(0, 10).map((item) => { | |
315 | + recommendImages.value = Array.from({ length: pageSize.value }).map( | |
316 | + (_, index) => { | |
317 | + const item = recommendList.value[index]; | |
318 | + if (!item) { | |
319 | + return null; | |
320 | + } | |
321 | + // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析 | |
322 | + if (typeof item.productimageliststore === "string") { | |
323 | + try { | |
324 | + item.productimageliststore = JSON.parse(item.productimageliststore); | |
325 | + } catch (error) { | |
326 | + item.productimageliststore = []; // 解析失败时,设置为空数组 | |
327 | + } | |
328 | + } | |
329 | + const ree = (item.productimageliststore = item?.productimageliststore.map( | |
330 | + (productItem: ProductImage) => ({ | |
331 | + ...productItem, | |
332 | + // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`, | |
333 | + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`, | |
334 | + name: item.name, | |
335 | + productUrl: `https://www.canrud.com/products/detail/${item.id}`, | |
336 | + }) | |
337 | + )); | |
338 | + return ree; | |
339 | + } | |
340 | + ); | |
341 | + hotLoading.value = false; | |
342 | +}; | |
343 | + | |
344 | +const toggleRowLeft = () => { | |
345 | + if (currentIndex.value !== 1) { | |
346 | + currentIndex.value--; | |
347 | + } else if (currentIndex.value == 1) { | |
348 | + currentIndex.value = maxPage.value; | |
349 | + } | |
350 | +}; | |
351 | +const toggleRowRight = () => { | |
352 | + if (currentIndex.value < maxPage.value) { | |
353 | + currentIndex.value++; | |
354 | + } else if (currentIndex.value == maxPage.value) { | |
355 | + currentIndex.value = 1; | |
356 | + } | |
357 | +}; | |
358 | +const toggleRowMobile = (value: number) => { | |
359 | + currentIndex.value = value; | |
360 | +}; | |
361 | + | |
362 | +watch(currentIndex, (newIndex) => { | |
363 | + loadHotProducts(); // Call loadHotProducts when currentIndex changes | |
364 | +}); | |
365 | +// Initial load of hot products | |
366 | +await loadHotProducts(); // Load hot products the first time | |
367 | + | |
59 | 368 | onMounted(() => { |
60 | - console.log('%c [ onMounted ]-10', 'font-size:13px; background:pink; color:#bf2c9f;', 111) | |
61 | -}) | |
369 | + console.log( | |
370 | + "%c [ onMounted ]-10", | |
371 | + "font-size:13px; background:pink; color:#bf2c9f;", | |
372 | + 111 | |
373 | + ); | |
374 | +}); | |
62 | 375 | useHead({ |
63 | - title: 'canrud', | |
376 | + title: "canrud", | |
64 | 377 | meta: [ |
65 | 378 | { |
66 | - name: 'title', | |
67 | - content: "科路得,助您科研之路势在必得。Canrd aims to be the world's leading one-stop service provider in new energy research. With a dedication to excellence, we offer Material Reagents, Lab Devices, Customized Batteries, Testing, and Advanced Packaging for energy materials and storage systems. We master advanced technologies to provide high-quality solutions. Our team's quick responses ensure tailored and professional services to meet your unique needs. Contact us at contact@canrd.com or call +86 19867737979 to explore our innovative offerings. Together, let's shape a greener, brighter world!" | |
68 | - }, { | |
69 | - name: 'keywords', | |
379 | + name: "title", | |
380 | + content: | |
381 | + "科路得,助您科研之路势在必得。Canrd aims to be the world's leading one-stop service provider in new energy research. With a dedication to excellence, we offer Material Reagents, Lab Devices, Customized Batteries, Testing, and Advanced Packaging for energy materials and storage systems. We master advanced technologies to provide high-quality solutions. Our team's quick responses ensure tailored and professional services to meet your unique needs. Contact us at contact@canrd.com or call +86 19867737979 to explore our innovative offerings. Together, let's shape a greener, brighter world!", | |
382 | + }, | |
383 | + { | |
384 | + name: "keywords", | |
70 | 385 | content: |
71 | - '科路得,canrd,canrud,Energy Storage Research,Lithium Batteries Research,Material Reagents,Lab Device,Customized Battery,Testing,Pack', | |
72 | - }, { | |
386 | + "科路得,canrd,canrud,Energy Storage Research,Lithium Batteries Research,Material Reagents,Lab Device,Customized Battery,Testing,Pack", | |
387 | + }, | |
388 | + { | |
73 | 389 | name: "description", |
74 | 390 | content: |
75 | - "科路得,助您科研之路势在必得。Canrd aims to be the world's leading one-stop service provider in new energy research. With a dedication to excellence, we offer Material Reagents, Lab Devices, Customized Batteries, Testing, and Advanced Packaging for energy materials and storage systems. We master advanced technologies to provide high-quality solutions. Our team's quick responses ensure tailored and professional services to meet your unique needs. Contact us at contact@canrd.com or call +86 19867737979 to explore our innovative offerings. Together, let's shape a greener, brighter world!" | |
76 | - }], | |
77 | - link: [ | |
78 | - { rel: 'preload', href: '/banner/banner1.jpg', as: 'image' } | |
79 | - ] | |
80 | -}) | |
391 | + "科路得,助您科研之路势在必得。Canrd aims to be the world's leading one-stop service provider in new energy research. With a dedication to excellence, we offer Material Reagents, Lab Devices, Customized Batteries, Testing, and Advanced Packaging for energy materials and storage systems. We master advanced technologies to provide high-quality solutions. Our team's quick responses ensure tailored and professional services to meet your unique needs. Contact us at contact@canrd.com or call +86 19867737979 to explore our innovative offerings. Together, let's shape a greener, brighter world!", | |
392 | + }, | |
393 | + ], | |
394 | + link: [{ rel: "preload", href: "/banner/banner1.jpg", as: "image" }], | |
395 | +}); | |
81 | 396 | |
82 | 397 | // useAsyncData(async ({ app }) => { |
83 | 398 | // console.log('%c [ app ]-70', 'font-size:13px; background:pink; color:#bf2c9f;', app) |
... | ... | @@ -85,76 +400,308 @@ useHead({ |
85 | 400 | // app.head.meta = [ |
86 | 401 | // { |
87 | 402 | // name: 'description', |
88 | -// content: "科路得,助您科研之路势在必得。Canrd aims to be the world's leading one-stop service provider in new energy research. With a dedication to excellence, we offer Material Reagents, Lab Devices, Customized Batteries, Testing, and Advanced Packaging for energy materials and storage systems. We master advanced technologies to provide high-quality solutions. Our team's quick responses ensure tailored and professional services to meet your unique needs. Contact us at | |
403 | +// content: "科路得,助您科研之路势在必得。Canrd aims to be the world's leading one-stop service provider in new energy research. With a dedication to excellence, we offer Material Reagents, Lab Devices, Customized Batteries, Testing, and Advanced Packaging for energy materials and storage systems. We master advanced technologies to provide high-quality solutions. Our team's quick responses ensure tailored and professional services to meet your unique needs. Contact us at | |
89 | 404 | // }] |
90 | 405 | // }) |
91 | -console.log(11) | |
406 | +console.log(11); | |
92 | 407 | |
93 | -const store = useCategoryStore() | |
408 | +const store = useCategoryStore(); | |
94 | 409 | |
95 | 410 | const lab = computed( |
96 | 411 | () => |
97 | 412 | store?.list?.[3] || { |
98 | - categoryDisplayName: '', | |
99 | - list: [] | |
413 | + categoryDisplayName: "", | |
414 | + list: [], | |
100 | 415 | } |
101 | -) | |
416 | +); | |
102 | 417 | |
103 | 418 | const banners = [ |
104 | - '/banner/banner1.jpg', | |
105 | - '/banner/banner2.jpg', | |
106 | - '/banner/banner3.jpg', | |
107 | - '/banner/banner5.jpg' | |
108 | -] | |
419 | + "/banner/banner1.jpg", | |
420 | + "/banner/banner2.jpg", | |
421 | + "/banner/banner3.jpg", | |
422 | + "/banner/banner5.jpg", | |
423 | +]; | |
109 | 424 | |
110 | 425 | const mobileBanners = [ |
111 | - '/mobile/banner-index1.png', | |
112 | - '/mobile/banner-index2.png', | |
113 | - '/mobile/banner-index3.png' | |
114 | -] | |
426 | + "/mobile/banner-index1.png", | |
427 | + "/mobile/banner-index2.png", | |
428 | + "/mobile/banner-index3.png", | |
429 | +]; | |
115 | 430 | |
116 | 431 | const materials = [ |
117 | - { name: 'Energy materials', imageUrl: '/home/1.jpg', href: '/products' }, | |
432 | + { name: "Energy materials", imageUrl: "/home/1.jpg", href: "/products" }, | |
118 | 433 | { |
119 | - name: 'Laboratory consumables', | |
120 | - imageUrl: '/home/2-Universal-consumables.png', | |
121 | - href: '/products' | |
434 | + name: "Laboratory consumables", | |
435 | + imageUrl: "/home/2-Universal-consumables.png", | |
436 | + href: "/products", | |
122 | 437 | }, |
123 | 438 | { |
124 | - name: 'Low-dimensional materials', | |
125 | - imageUrl: '/home/3-Low-dimensional-materials.png', | |
126 | - href: '/products' | |
127 | - } | |
128 | -] | |
439 | + name: "Low-dimensional materials", | |
440 | + imageUrl: "/home/3-Low-dimensional-materials.png", | |
441 | + href: "/products", | |
442 | + }, | |
443 | +]; | |
129 | 444 | |
130 | 445 | const tests = [ |
131 | 446 | { |
132 | - name: 'Electrochemical performance', | |
133 | - imageUrl: '/home/8_Electrochemical_performance.svg', | |
134 | - href: '/test' | |
447 | + name: "Electrochemical performance", | |
448 | + imageUrl: "/home/8_Electrochemical_performance.svg", | |
449 | + href: "/test", | |
450 | + }, | |
451 | + { | |
452 | + name: "Reliability testing", | |
453 | + imageUrl: "/home/9 Reliability testing.svg", | |
454 | + href: "/test", | |
455 | + }, | |
456 | + { | |
457 | + name: "Material testing", | |
458 | + imageUrl: "/home/10 Material testing.svg", | |
459 | + href: "/test", | |
135 | 460 | }, |
136 | - { name: 'Reliability testing', imageUrl: '/home/9 Reliability testing.svg', href: '/test' }, | |
137 | - { name: 'Material testing', imageUrl: '/home/10 Material testing.svg', href: '/test' }, | |
138 | - { name: 'Calibration', imageUrl: '/home/11 Calibration.svg', href: '/test' } | |
139 | -] | |
461 | + { name: "Calibration", imageUrl: "/home/11 Calibration.svg", href: "/test" }, | |
462 | +]; | |
140 | 463 | const batteries = [ |
141 | - { name: 'Material evaluation', imageUrl: '/home/4-Material-evaluation.png', href: '/customize' }, | |
142 | - { name: 'R&D foundry', imageUrl: '/home/5-R&D-foundry.png', href: '/customize' }, | |
143 | - { name: 'Chemical system', imageUrl: '/home/6-Chemical-system.png', href: '/customize' }, | |
144 | 464 | { |
145 | - name: 'Semi product customization', | |
146 | - imageUrl: '/home/7-Semi-product-customization.png', | |
147 | - href: '/customize' | |
148 | - } | |
149 | -] | |
465 | + name: "Material evaluation", | |
466 | + imageUrl: "/home/4-Material-evaluation.png", | |
467 | + href: "/customize", | |
468 | + }, | |
469 | + { | |
470 | + name: "R&D foundry", | |
471 | + imageUrl: "/home/5-R&D-foundry.png", | |
472 | + href: "/customize", | |
473 | + }, | |
474 | + { | |
475 | + name: "Chemical system", | |
476 | + imageUrl: "/home/6-Chemical-system.png", | |
477 | + href: "/customize", | |
478 | + }, | |
479 | + { | |
480 | + name: "Semi product customization", | |
481 | + imageUrl: "/home/7-Semi-product-customization.png", | |
482 | + href: "/customize", | |
483 | + }, | |
484 | +]; | |
150 | 485 | const packs = [ |
151 | - { name: 'Power bank', imageUrl: '/home/12-power-bank.png', href: '/pack' }, | |
152 | - { name: 'Energy storage', imageUrl: '/home/13-Energy-storage.png', href: '/pack' }, | |
153 | - { name: 'power tool', imageUrl: '/home/3-powertool.png', href: '/pack' }, | |
486 | + { name: "Power bank", imageUrl: "/home/12-power-bank.png", href: "/pack" }, | |
154 | 487 | { |
155 | - name: 'portable energy storage', | |
156 | - imageUrl: '/home/4-portableenergystorage.png', | |
157 | - href: '/pack' | |
158 | - } | |
159 | -] | |
488 | + name: "Energy storage", | |
489 | + imageUrl: "/home/13-Energy-storage.png", | |
490 | + href: "/pack", | |
491 | + }, | |
492 | + { name: "power tool", imageUrl: "/home/3-powertool.png", href: "/pack" }, | |
493 | + { | |
494 | + name: "portable energy storage", | |
495 | + imageUrl: "/home/4-portableenergystorage.png", | |
496 | + href: "/pack", | |
497 | + }, | |
498 | +]; | |
499 | +const hotLength = computed(() => | |
500 | + hotTotal.value ? Math.ceil(hotTotal.value / 4) : 0 | |
501 | +); | |
160 | 502 | </script> |
503 | +<style scoped> | |
504 | +@media screen and (min-width: 1537px) { | |
505 | + .tabs { | |
506 | + border-bottom: 2px solid #1f88e5; | |
507 | + margin: 10px auto 100px; | |
508 | + width: 85%; | |
509 | + } | |
510 | +} | |
511 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
512 | + .tabs { | |
513 | + border-bottom: 2px solid #1f88e5; | |
514 | + margin: 10px auto 20px; | |
515 | + width: 80%; | |
516 | + } | |
517 | +} | |
518 | +@media screen and (max-width: 1280px) { | |
519 | + .tabs { | |
520 | + border-bottom: 2px solid #1f88e5; | |
521 | + margin: 10px auto 0px; | |
522 | + width: 85%; | |
523 | + } | |
524 | +} | |
525 | + | |
526 | +.active { | |
527 | + background-color: #1086e8; | |
528 | +} | |
529 | + | |
530 | +/* #image-container { | |
531 | + display: flex; | |
532 | + align-items: center; | |
533 | + justify-content: center; | |
534 | + height: 320px; | |
535 | + margin: 10px auto 50px; | |
536 | + width: 80%; | |
537 | +} */ | |
538 | + | |
539 | +@media screen and (min-width: 1537px) { | |
540 | + #image-container { | |
541 | + display: flex; | |
542 | + align-items: center; | |
543 | + justify-content: center; | |
544 | + height: 320px; | |
545 | + margin: 10px auto 50px; | |
546 | + width: 80%; | |
547 | + } | |
548 | +} | |
549 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
550 | + #image-container { | |
551 | + display: flex; | |
552 | + align-items: center; | |
553 | + justify-content: center; | |
554 | + height: 240px; | |
555 | + margin: 10px auto 0px; | |
556 | + width: 80%; | |
557 | + } | |
558 | +} | |
559 | +@media screen and (max-width: 1280px) { | |
560 | + #image-container { | |
561 | + display: flex; | |
562 | + align-items: center; | |
563 | + justify-content: center; | |
564 | + height: 260px; | |
565 | + margin: 10px auto 0px; | |
566 | + width: 90%; | |
567 | + } | |
568 | +} | |
569 | +.image-row { | |
570 | + display: flex; | |
571 | + height: 305px; | |
572 | +} | |
573 | +@media screen and (min-width: 1537px) { | |
574 | + .image-row { | |
575 | + display: flex; | |
576 | + height: 305px; | |
577 | + } | |
578 | +} | |
579 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
580 | + .image-row { | |
581 | + display: flex; | |
582 | + height: 245px; | |
583 | + } | |
584 | +} | |
585 | +@media screen and (max-width: 1280px) { | |
586 | + .image-row { | |
587 | + display: flex; | |
588 | + height: 220px; | |
589 | + } | |
590 | +} | |
591 | +@media screen and (min-width: 1537px) { | |
592 | + .imageTotal { | |
593 | + display: inline-block; | |
594 | + margin: 0 5px; | |
595 | + text-align: center; | |
596 | + width: 290px; | |
597 | + } | |
598 | +} | |
599 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
600 | + .imageTotal { | |
601 | + display: inline-block; | |
602 | + margin: 0 5px; | |
603 | + text-align: center; | |
604 | + width: 210px; | |
605 | + } | |
606 | +} | |
607 | +@media screen and (min-width: 1537px) { | |
608 | + .image-row img { | |
609 | + width: 240px; | |
610 | + height: 240px; | |
611 | + } | |
612 | +} | |
613 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
614 | + .image-row img { | |
615 | + width: 140px; | |
616 | + height: 140px; | |
617 | + } | |
618 | +} | |
619 | +@media screen and (max-width: 1280px) { | |
620 | + .image-row img { | |
621 | + width: 160px; | |
622 | + height: 160px; | |
623 | + margin-left: 10px; | |
624 | + } | |
625 | +} | |
626 | +@media screen and (min-width: 1537px) { | |
627 | + .image-name { | |
628 | + display: -webkit-box; /* Enables multi-line text handling */ | |
629 | + -webkit-box-orient: vertical; /* Defines the vertical orientation of the box */ | |
630 | + -webkit-line-clamp: 3; /* Limits the text to 3 lines */ | |
631 | + overflow: hidden; /* Hides the overflowed text */ | |
632 | + text-overflow: ellipsis; /* Adds an ellipsis when the text overflows */ | |
633 | + margin-top: 5px; | |
634 | + font-size: 16px; | |
635 | + width: 180px; | |
636 | + color: #555; | |
637 | + text-align: center; /* Centers the text horizontally */ | |
638 | + margin-left: 50px; | |
639 | + } | |
640 | +} | |
641 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
642 | + .image-name { | |
643 | + display: -webkit-box; /* Enables multi-line text handling */ | |
644 | + -webkit-box-orient: vertical; /* Defines the vertical orientation of the box */ | |
645 | + -webkit-line-clamp: 3; /* Limits the text to 3 lines */ | |
646 | + overflow: hidden; /* Hides the overflowed text */ | |
647 | + text-overflow: ellipsis; /* Adds an ellipsis when the text overflows */ | |
648 | + margin-top: 5px; | |
649 | + font-size: 16px; | |
650 | + width: 180px; | |
651 | + color: #555; | |
652 | + text-align: center; /* Centers the text horizontally */ | |
653 | + margin-left: 10px; | |
654 | + } | |
655 | +} | |
656 | +@media screen and (max-width: 1280px) { | |
657 | + .image-name { | |
658 | + display: -webkit-box; /* Enables multi-line text handling */ | |
659 | + -webkit-box-orient: vertical; /* Defines the vertical orientation of the box */ | |
660 | + -webkit-line-clamp: 3; /* Limits the text to 3 lines */ | |
661 | + overflow: hidden; /* Hides the overflowed text */ | |
662 | + text-overflow: ellipsis; /* Adds an ellipsis when the text overflows */ | |
663 | + margin-top: 5px; | |
664 | + font-size: 16px; | |
665 | + width: 180px; | |
666 | + color: #555; | |
667 | + text-align: center; /* Centers the text horizontally */ | |
668 | + margin-left: 5px; | |
669 | + } | |
670 | +} | |
671 | +button .recommendButton { | |
672 | + margin: 0 0px; | |
673 | + cursor: pointer; | |
674 | +} | |
675 | + | |
676 | +.recommend-left-box { | |
677 | +} | |
678 | + | |
679 | +.recommend-img-left { | |
680 | + width: 26px; | |
681 | + height: 27px; | |
682 | + margin-right: 30px; | |
683 | +} | |
684 | + | |
685 | +.recommend-img-left:hover { | |
686 | + cursor: pointer; | |
687 | +} | |
688 | +.recommend-right-box { | |
689 | +} | |
690 | + | |
691 | +.recommend-img-right { | |
692 | + width: 26px; | |
693 | + height: 27px; | |
694 | + margin-left: 30px; | |
695 | +} | |
696 | + | |
697 | +.recommend-img-right:hover { | |
698 | + cursor: pointer; | |
699 | +} | |
700 | +.image-grid { | |
701 | + padding: 16px; | |
702 | +} | |
703 | + | |
704 | +.v-card { | |
705 | + transition: all 0.3s ease-in-out; | |
706 | +} | |
707 | +</style> | ... | ... |
pages/products/detail/[id]/index.vue
1 | 1 | <template> |
2 | - <MobileProductDetail v-if="isMobile()" :info="info" /> | |
2 | + <MobileProductDetail v-if="isMobile()" :info="info" :res="resData" /> | |
3 | 3 | <ProductDetail v-else :info="info" /> |
4 | 4 | </template> |
5 | 5 | |
... | ... | @@ -10,8 +10,9 @@ import MobileProductDetail from "~/components/MobileProductDetail.vue"; |
10 | 10 | import type { Product, ProductImage } from "~/type"; |
11 | 11 | import { useRoute, useRouter } from "vue-router"; |
12 | 12 | |
13 | +const productStore = useProductListStore(); | |
13 | 14 | const route = useRoute(); |
14 | - | |
15 | +const router = useRouter(); | |
15 | 16 | const info = ref<Partial<Product>>({ |
16 | 17 | productimageliststore: [], |
17 | 18 | productAttributeList: [], |
... | ... | @@ -44,32 +45,28 @@ watchEffect(() => { |
44 | 45 | }, |
45 | 46 | { |
46 | 47 | name: "keywords", |
47 | - content: | |
48 | - info.value.name || | |
49 | - "科路得,Equipment,High-precision,Machining center,Design,Manufacturing capabilities,Equipment supply,Production line planning,Construction services,Battery assembly lines,Pouch cell testing lines", | |
48 | + content: info.value.name || info.value.metakeywords, | |
50 | 49 | }, |
51 | 50 | { |
52 | 51 | name: "description", |
53 | - content: | |
54 | - info.value.name || | |
55 | - "科路得,助您科研之路势在必得。Equipment Business: With our self-built high-precision machining center, we possess robust design and manufacturing capabilities. We offer comprehensive equipment supply, production line planning, and construction services, including battery assembly lines, pouch cell testing lines, and more. Our aim is to provide complete equipment solutions that cater to the diverse needs of our clients. Expect top-quality equipment and professional services that will help you stand out in a fiercely competitive market!", | |
52 | + content: info.value.metadescription || info.value.name, | |
56 | 53 | }, |
57 | 54 | ], |
58 | 55 | }); |
59 | 56 | }); |
60 | 57 | |
61 | 58 | const newData: Product = resData.value.data; |
62 | - | |
63 | 59 | newData.productimageliststore = |
64 | 60 | typeof newData.productimageliststore === "string" |
65 | 61 | ? JSON.parse(newData.productimageliststore) || [] |
66 | 62 | : (newData.productimageliststore as ProductImage[]); |
67 | -newData.productimageliststore = newData?.productimageliststore.map( | |
63 | +newData.productimageliststore = newData?.productimageliststore?.map( | |
68 | 64 | (item: ProductImage) => ({ |
69 | 65 | ...item, |
70 | 66 | // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}` |
71 | 67 | url: `/api/show/image?fileKey=${item.fileKey}&psize=p512`, |
72 | 68 | }) |
73 | 69 | ); |
70 | + | |
74 | 71 | info.value = { ...newData }; |
75 | 72 | </script> | ... | ... |
pages/products/index.vue
1 | 1 | <template> |
2 | - <div class="tw-m-auto tw-pb-[64px]"> | |
2 | + <div class="tw-m-auto tw-pb-[64px]" style="padding-bottom: 0px"> | |
3 | 3 | <CategoryList v-if="categoryStore.categoryVisible && !isMobile()" /> |
4 | 4 | <MobileCategoryList v-if="categoryStore.categoryVisible && isMobile()" /> |
5 | + <v-tabs | |
6 | + class="tabs" | |
7 | + v-model="tabRecom" | |
8 | + style="margin-top: 25px" | |
9 | + color="white" | |
10 | + bg-color="#eeeeee" | |
11 | + slider-color="blue-lighten-1" | |
12 | + selected-class="active" | |
13 | + v-if="recommendImages[0] !== null && !isMobile()" | |
14 | + > | |
15 | + <v-tab :value="1">Best Sellers</v-tab> | |
16 | + <!-- <v-tab :value="3">商品百科</v-tab> --> | |
17 | + </v-tabs> | |
18 | + <div id="image-container" v-if="recommendImages[0] !== null && !isMobile()"> | |
19 | + <div class="recommend-left-box"> | |
20 | + <v-img | |
21 | + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/76c987e13a334be481a346c19c2284f3qy4tjnxps7.png" | |
22 | + alt="往左移" | |
23 | + class="recommend-img-left" | |
24 | + @click="toggleRowLeft" | |
25 | + /> | |
26 | + </div> | |
27 | + <div class="image-row" id="row1"> | |
28 | + <!-- <img | |
29 | + v-for="(imageObj, index) in recommendImages.slice(0, 5)" | |
30 | + :key="'row1-' + index" | |
31 | + :src="imageObj[0]?.url" | |
32 | + :alt="'Image ' + (index + 1)" | |
33 | + style="margin: 0 5px; width: 200px; height: 200px" | |
34 | + /> --> | |
35 | + <div | |
36 | + v-for="(imageObj, index) in recommendImages" | |
37 | + :key="'row1-' + index" | |
38 | + class="imageTotal" | |
39 | + > | |
40 | + <a v-if="imageObj" :href="imageObj[0]?.productUrl" target="_blank"> | |
41 | + <img | |
42 | + :src="imageObj[0]?.url" | |
43 | + :alt="'Image ' + (index + 1)" | |
44 | + class="item-imgHot" | |
45 | + /> | |
46 | + <span class="image-name"> | |
47 | + {{ imageObj[0]?.name }} | |
48 | + </span> | |
49 | + </a> | |
50 | + <div v-else style="width: 200px; height: 200px"></div> | |
51 | + </div> | |
52 | + </div> | |
53 | + <div class="recommend-right-box"> | |
54 | + <v-img | |
55 | + src="https://m-canrd.oss-cn-shenzhen.aliyuncs.com/crmebimage/public/maintain/2024/09/14/b5daa0a8f2f140f5b406e984c730a453iznzlekysg.png" | |
56 | + alt="往右移" | |
57 | + class="recommend-img-right" | |
58 | + @click="toggleRowRight" | |
59 | + /> | |
60 | + </div> | |
61 | + </div> | |
5 | 62 | <v-container class=""> |
6 | - <div class="tw-text-center" v-if="loading"> | |
63 | + <div class="tw-text-center tw-mt-[32px]" v-if="loading"> | |
7 | 64 | <v-progress-circular |
8 | 65 | color="blue-lighten-2" |
9 | 66 | indeterminate |
... | ... | @@ -60,11 +117,79 @@ |
60 | 117 | <v-row> |
61 | 118 | <v-col> |
62 | 119 | <v-pagination |
120 | + :size="isMobile() ? 'small' : 'default'" | |
121 | + v-if="productStore.total" | |
122 | + v-model="productStore.pageNo" | |
123 | + @update:modelValue="productStore.updatePageNo" | |
124 | + :length="length" | |
125 | + rounded="0" | |
126 | + class="tw-float-right tw-mt-[32px]" | |
127 | + total-visible="5" | |
128 | + ></v-pagination></v-col | |
129 | + ></v-row> | |
130 | + <v-tabs | |
131 | + class="tabs2" | |
132 | + ref="tabs2" | |
133 | + v-model="tabRecom" | |
134 | + style="margin-top: 25px; margin-bottom: 20px" | |
135 | + color="white" | |
136 | + bg-color="#eeeeee" | |
137 | + slider-color="blue-lighten-1" | |
138 | + selected-class="active" | |
139 | + v-if="isMobile()" | |
140 | + > | |
141 | + <v-tab :value="1">Best Sellers</v-tab> | |
142 | + <!-- <v-tab :value="3">商品百科</v-tab> --> | |
143 | + </v-tabs> | |
144 | + <div class="tw-text-center" v-if="hotLoading && isMobile()"> | |
145 | + <v-progress-circular | |
146 | + color="blue-lighten-2" | |
147 | + indeterminate | |
148 | + size="64" | |
149 | + class="tw-m-auto" | |
150 | + ></v-progress-circular> | |
151 | + </div> | |
152 | + <v-item-group multiple v-if="isMobile()"> | |
153 | + <v-row v-if="!hotLoading"> | |
154 | + <v-col | |
155 | + v-for="(item, i) in recommendImages" | |
156 | + :key="i" | |
157 | + cols="6" | |
158 | + lg="3" | |
159 | + md="4" | |
160 | + sm="6" | |
161 | + > | |
162 | + <div v-if="item !== null"> | |
163 | + <v-card :elevation="4" class="mx-auto" :href="item[0].productUrl"> | |
164 | + <!-- 设置 eager 属性,确保图片直接加载 --> | |
165 | + <v-img | |
166 | + :src="item[0].url" | |
167 | + :alt="item[0].name" | |
168 | + eager | |
169 | + class="d-block" | |
170 | + /> | |
171 | + <v-card-text class="tw-text-left font-weight-medium title"> | |
172 | + <h4>{{ item[0].name }}</h4> | |
173 | + </v-card-text> | |
174 | + </v-card> | |
175 | + </div> | |
176 | + </v-col> | |
177 | + </v-row> | |
178 | + <!-- <div | |
179 | + v-if="!hotTotal" | |
180 | + class="text-medium-emphasis text-body-1 tw-text-center tw-m-[64px]" | |
181 | + > | |
182 | + no data | |
183 | + </div> --> | |
184 | + </v-item-group> | |
185 | + <v-row v-if="isMobile()"> | |
186 | + <v-col> | |
187 | + <v-pagination | |
63 | 188 | :size="isMobile() ? 'small' : 'default'" |
64 | - v-if="productStore.total" | |
65 | - v-model="productStore.pageNo" | |
66 | - @update:modelValue="productStore.updatePageNo" | |
67 | - :length="length" | |
189 | + v-if="hotTotal" | |
190 | + v-model="currentIndex" | |
191 | + @update:modelValue="toggleRowMobile" | |
192 | + :length="hotLength" | |
68 | 193 | rounded="0" |
69 | 194 | class="tw-float-right tw-mt-[32px]" |
70 | 195 | total-visible="5" |
... | ... | @@ -75,6 +200,7 @@ |
75 | 200 | </template> |
76 | 201 | |
77 | 202 | <script setup lang="ts"> |
203 | +import type { ProductImage } from "~/type"; | |
78 | 204 | import { isMobile, isEqual } from "~/utils"; |
79 | 205 | import { useProductListStore } from "~/stores/product_list"; |
80 | 206 | import { useCategoryStore } from "~/stores/category"; |
... | ... | @@ -85,10 +211,110 @@ import { watchEffect, computed, ref } from "vue"; |
85 | 211 | const productStore = useProductListStore(); |
86 | 212 | const categoryStore = useCategoryStore(); |
87 | 213 | const loading = ref(false); |
214 | +const hotLoading = ref(false); | |
88 | 215 | const route = useRoute(); // 获取路由信息 |
216 | +const router = useRouter(); // 获取路由信息 | |
217 | +const title = ref(""); | |
218 | +const keywordTitle = ref(""); | |
219 | +const maxPage = ref(1); | |
220 | +const tabRecom = ref(); | |
221 | +const recommendList = ref(); | |
222 | +const recommendImages = ref(); | |
223 | +const currentIndex = ref(1); | |
224 | +const hotTotal = ref(10); | |
225 | +const isOrNotMobile = isMobile(); | |
226 | + | |
227 | +const loadHotProducts = async () => { | |
228 | + const pageSize = ref(5); | |
229 | + if (isOrNotMobile) { | |
230 | + pageSize.value = 4; | |
231 | + } | |
232 | + hotLoading.value = true; | |
233 | + let { data: hotProducts } = await useAsyncData( | |
234 | + "hotProducts", | |
235 | + () => | |
236 | + $fetch("/shop/product/hotProducts", { | |
237 | + method: "GET", | |
238 | + params: { | |
239 | + pageNo: currentIndex.value, | |
240 | + pageSize: pageSize.value, | |
241 | + }, | |
242 | + }), | |
243 | + { | |
244 | + server: true, // 仅在服务器端获取数据 | |
245 | + } | |
246 | + ); | |
247 | + hotTotal.value = hotProducts.value.data.total; | |
248 | + recommendList.value = hotProducts.value.data.records; | |
249 | + maxPage.value = hotProducts.value.data.pages; | |
250 | + // recommendImages.value = recommendList.value.slice(0, 10).map((item) => { | |
251 | + recommendImages.value = Array.from({ length: pageSize.value }).map( | |
252 | + (_, index) => { | |
253 | + const item = recommendList.value[index]; | |
254 | + if (!item) { | |
255 | + return null; | |
256 | + } | |
257 | + // 检查 productimageliststore 是否为字符串格式,如果是,则尝试解析 | |
258 | + if (typeof item.productimageliststore === "string") { | |
259 | + try { | |
260 | + item.productimageliststore = JSON.parse(item.productimageliststore); | |
261 | + } catch (error) { | |
262 | + item.productimageliststore = []; // 解析失败时,设置为空数组 | |
263 | + } | |
264 | + } | |
265 | + const ree = (item.productimageliststore = item?.productimageliststore.map( | |
266 | + (productItem: ProductImage) => ({ | |
267 | + ...productItem, | |
268 | + // url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}`, | |
269 | + url: `https://www.canrud.com/api/show/image?fileKey=${productItem.fileKey}&psize=p256`, | |
270 | + name: item.name, | |
271 | + productUrl: `https://www.canrud.com/products/detail/${item.id}`, | |
272 | + }) | |
273 | + )); | |
274 | + return ree; | |
275 | + } | |
276 | + ); | |
277 | + hotLoading.value = false; | |
278 | +}; | |
279 | + | |
280 | +const toggleRowLeft = () => { | |
281 | + if (currentIndex.value !== 1) { | |
282 | + currentIndex.value--; | |
283 | + } else if (currentIndex.value == 1) { | |
284 | + currentIndex.value = maxPage.value; | |
285 | + } | |
286 | +}; | |
287 | +const toggleRowRight = () => { | |
288 | + if (currentIndex.value < maxPage.value) { | |
289 | + currentIndex.value++; | |
290 | + } else if (currentIndex.value == maxPage.value) { | |
291 | + currentIndex.value = 1; | |
292 | + } | |
293 | +}; | |
294 | +const toggleRowMobile = (value: number) => { | |
295 | + currentIndex.value = value; | |
296 | +}; | |
297 | + | |
298 | +watch(currentIndex, (newIndex) => { | |
299 | + loadHotProducts(); // Call loadHotProducts when currentIndex changes | |
300 | +}); | |
301 | +// Initial load of hot products | |
302 | +await loadHotProducts(); // Load hot products the first time | |
303 | + | |
304 | +watchEffect(() => { | |
305 | + // 遍历数组 | |
306 | + if (router.currentRoute.value.query.categories) { | |
307 | + title.value = `${router.currentRoute.value.query.categories}`; | |
308 | + } else if (router.currentRoute.value.query.function) { | |
309 | + title.value += `,${router.currentRoute.value.query.function}`; | |
310 | + } | |
311 | + if (router.currentRoute.value.query.keyword) { | |
312 | + keywordTitle.value = `${router.currentRoute.value.query.keyword}`; | |
313 | + } | |
314 | +}); | |
89 | 315 | |
90 | 316 | useHead({ |
91 | - title: "canrud", | |
317 | + title: title.value || keywordTitle.value, | |
92 | 318 | meta: [ |
93 | 319 | { |
94 | 320 | name: "title", |
... | ... | @@ -107,9 +333,8 @@ useHead({ |
107 | 333 | }, |
108 | 334 | ], |
109 | 335 | }); |
110 | - | |
336 | +const firstIndex = ref(0); | |
111 | 337 | const loadProducts = async () => { |
112 | - console.log(productStore, "5656productStore"); | |
113 | 338 | let params: any = { |
114 | 339 | pageNo: productStore.pageNo, |
115 | 340 | pageSize: 20, |
... | ... | @@ -123,35 +348,146 @@ const loadProducts = async () => { |
123 | 348 | loading.value = false; |
124 | 349 | return; |
125 | 350 | } |
351 | + const categories = ref(); | |
352 | + const mainCategory = ref(); | |
353 | + if (router.currentRoute.value.query.categories) { | |
354 | + categories.value = router.currentRoute.value.query.categories.split(","); | |
355 | + mainCategory.value = categories.value[0].trim(); // 取第一个值 | |
356 | + } | |
126 | 357 | |
127 | 358 | params.productCategoryId = categoryStore.selectedSubCategory; |
128 | 359 | // productCategoryId: '78b86c6e917841cf9a292234f2805e24', |
129 | - | |
130 | 360 | if (categoryStore.selectedFuncCategory) { |
131 | 361 | params.productFunctionId = categoryStore.selectedFuncCategory; |
362 | + if (firstIndex.value == 1) { | |
363 | + params.productFunctionId = selectedFuncCategoryBak.value; | |
364 | + firstIndex.value += 1; | |
365 | + if ( | |
366 | + params.productFunctionId == "" && | |
367 | + selectedFuncCategoryBak.value == "" | |
368 | + ) { | |
369 | + categoryStore.selectedFuncCategory = selectedFuncCategoryBak.value; | |
370 | + params.productFunctionId = categoryStore.selectedFuncCategory; | |
371 | + // categoryStore.selectedFuncCategory = selectedFuncCategoryBak.value; | |
372 | + } | |
373 | + console.log( | |
374 | + params.productFunctionId, | |
375 | + selectedFuncCategoryBak.value, | |
376 | + categoryStore.selectedFuncCategory | |
377 | + ); | |
378 | + } | |
379 | + // const savedSubCategory = localStorage.getItem("selectedFuncCategory2"); | |
380 | + // console.log(savedSubCategory, "5656savedSubCategory"); | |
381 | + // localStorage.setItem("selectedFuncCategory3", "2"); | |
382 | + } else if (mainCategory.value == "Energy materials") { | |
383 | + params.productCategoryId = categoryStore.selectedSubCategory; | |
132 | 384 | } |
133 | 385 | |
386 | + console.log( | |
387 | + // params, | |
388 | + // productStore?.params, | |
389 | + // categoryStore.selectedSubCategory, | |
390 | + // categoryStore.selectedFuncCategory, | |
391 | + window.selectedSubCategory, | |
392 | + window.selectedFuncCategory | |
393 | + ); | |
134 | 394 | if ( |
135 | 395 | categoryStore.selectedSubCategory && |
136 | 396 | !isEqual(productStore.params, params) |
137 | 397 | ) { |
398 | + // console.log(route, "5656route"); | |
138 | 399 | productStore.updateParams(params); |
400 | + const categories = ref("Laboratory consumables,Others"); | |
401 | + const mainCategory = categories.value[0].trim(); // 取第一个值 | |
402 | + // console.log(categoryStore.selectedSubCategory, "5656mobi"); | |
139 | 403 | |
140 | 404 | await productStore.getList(params); |
141 | 405 | } |
406 | + // if ( | |
407 | + // categoryStore.selectedFuncCategory && | |
408 | + // !isEqual(productStore.params, params) | |
409 | + // ) { | |
410 | + // console.log( | |
411 | + // categoryStore.selectedFuncCategory, | |
412 | + // "5656categoryStore.selectedFuncCategoryss" | |
413 | + // ); | |
414 | + // productStore.updateParams(params); | |
415 | + // await productStore.getList(params); | |
416 | + // } | |
417 | + if (firstIndex.value === 0 && categoryStore.selectedFuncCategory) { | |
418 | + // console.log(route, "5656route"); | |
419 | + productStore.updateParams(params); | |
420 | + // const categories = ref("Laboratory consumables,Others"); | |
421 | + // const mainCategory = categories.value[0].trim(); // 取第一个值 | |
422 | + // console.log(categoryStore.selectedFuncCategory, "5656mobi"); | |
423 | + await productStore.getList(params); | |
424 | + firstIndex.value = 1; | |
425 | + } | |
142 | 426 | loading.value = false; |
143 | 427 | }; |
144 | - | |
428 | +const isWindowAssigned = ref(false); | |
429 | +const selectedSubCategoryBak = ref(); | |
430 | +const selectedFuncCategoryBak = ref(); | |
145 | 431 | watchEffect(async () => { |
146 | 432 | if (route.query.keyword !== undefined) { |
147 | 433 | productStore.keyword = route.query.keyword; |
148 | 434 | } |
435 | + // console.log(categoryStore.selectedSubCategory, "5656index-categoryStore"); | |
436 | + if (typeof window !== "undefined" && !isWindowAssigned.value) { | |
437 | + window.selectedSubCategory = categoryStore.selectedSubCategory; | |
438 | + window.selectedFuncCategory = categoryStore.selectedFuncCategory; | |
439 | + selectedSubCategoryBak.value = categoryStore.selectedSubCategory; | |
440 | + selectedFuncCategoryBak.value = categoryStore.selectedFuncCategory; | |
441 | + isWindowAssigned.value = true; | |
442 | + } | |
149 | 443 | loadProducts(); |
444 | + // console.log(categoryStore.selectedSubCategory, "5656index-categoryStore"); | |
150 | 445 | }); |
446 | +// onMounted(() => { | |
447 | +// // window.selectedSubCategory = categoryStore.selectedSubCategory; | |
448 | +// // window.selectedFuncCategory = categoryStore.selectedFuncCategory; | |
449 | +// if (route.query.function) { | |
450 | +// const functionName = route.query.function.trim(); | |
451 | +// const funcCategoryList = computed(() => { | |
452 | +// if (categoryStore.selectedCategory) { | |
453 | +// const tmp = categoryStore.list.filter( | |
454 | +// (item) => item.categoryDisplayName === categoryStore.selectedCategory | |
455 | +// ); | |
456 | +// return tmp?.[0]?.productFunctions || []; | |
457 | +// } | |
458 | +// return []; | |
459 | +// }); | |
460 | +// const foundFuncCategory = funcCategoryList.value.find( | |
461 | +// (func) => func.name === functionName | |
462 | +// ); | |
463 | + | |
464 | +// if (foundFuncCategory) { | |
465 | +// const funcCategoryId = foundFuncCategory.id; | |
466 | +// if (typeof localStorage !== "undefined") { | |
467 | +// localStorage.setItem("selectedFuncCategory2", funcCategoryId); | |
468 | +// localStorage.setItem("selectedFuncCategory3", "1"); | |
469 | +// } | |
470 | +// // 你可以在这里使用找到的 funcCategoryId | |
471 | +// } | |
472 | +// // // 6. 查找对应的功能分类 ID | |
473 | +// // const foundFuncCategory = funcCategories.find( | |
474 | +// // (func) => func.name === functionName | |
475 | +// // ); | |
476 | + | |
477 | +// // if (foundFuncCategory) { | |
478 | +// // const funcCategoryId = foundFuncCategory.id; | |
479 | +// // // 使用找到的 funcCategoryId | |
480 | +// // categoryStore.updateFuncCategory(funcCategoryId); | |
481 | +// // } | |
482 | +// } | |
483 | +// }); | |
151 | 484 | |
152 | 485 | const length = computed(() => |
153 | 486 | productStore.total ? Math.ceil(productStore.total / productStore.pageSize) : 0 |
154 | 487 | ); |
488 | +const hotLength = computed(() => | |
489 | + hotTotal.value ? Math.ceil(hotTotal.value / 4) : 0 | |
490 | +); | |
155 | 491 | </script> |
156 | 492 | |
157 | 493 | <style scoped> |
... | ... | @@ -163,4 +499,200 @@ const length = computed(() => |
163 | 499 | -webkit-line-clamp: 2; |
164 | 500 | -webkit-box-orient: vertical; |
165 | 501 | } |
502 | + | |
503 | +@media screen and (min-width: 1537px) { | |
504 | + .tabs { | |
505 | + border-bottom: 2px solid #1f88e5; | |
506 | + margin: 10px auto 20px; | |
507 | + width: 93%; | |
508 | + } | |
509 | +} | |
510 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
511 | + .tabs { | |
512 | + border-bottom: 2px solid #1f88e5; | |
513 | + margin: 10px auto 40px; | |
514 | + width: 78%; | |
515 | + } | |
516 | +} | |
517 | +@media screen and (max-width: 1280px) { | |
518 | + .tabs { | |
519 | + border-bottom: 2px solid #1f88e5; | |
520 | + margin: 10px auto 40px; | |
521 | + width: 92%; | |
522 | + } | |
523 | +} | |
524 | +/* .tabs { | |
525 | + border-bottom: 2px solid #1f88e5; | |
526 | +} */ | |
527 | + | |
528 | +.active { | |
529 | + background-color: #1086e8; | |
530 | +} | |
531 | + | |
532 | +@media screen and (min-width: 1537px) { | |
533 | + #image-container { | |
534 | + display: flex; | |
535 | + align-items: center; | |
536 | + justify-content: center; | |
537 | + height: 320px; | |
538 | + margin: 10px auto 50px; | |
539 | + width: 80%; | |
540 | + } | |
541 | +} | |
542 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
543 | + #image-container { | |
544 | + display: flex; | |
545 | + align-items: center; | |
546 | + justify-content: center; | |
547 | + height: 260px; | |
548 | + margin: 10px auto 0px; | |
549 | + width: 80%; | |
550 | + padding: 0; | |
551 | + } | |
552 | +} | |
553 | +@media screen and (max-width: 1280px) { | |
554 | + #image-container { | |
555 | + display: flex; | |
556 | + align-items: center; | |
557 | + justify-content: center; | |
558 | + height: 260px; | |
559 | + margin: 10px auto 0px; | |
560 | + width: 80%; | |
561 | + padding: 0; | |
562 | + } | |
563 | +} | |
564 | +.image-row { | |
565 | + display: flex; | |
566 | + height: 245px; | |
567 | +} | |
568 | +@media screen and (min-width: 1537px) { | |
569 | + .imageTotal { | |
570 | + display: inline-block; | |
571 | + margin: 0 5px; | |
572 | + text-align: center; | |
573 | + width: 300px; | |
574 | + } | |
575 | +} | |
576 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
577 | + .imageTotal { | |
578 | + display: inline-block; | |
579 | + margin: 0 5px; | |
580 | + text-align: center; | |
581 | + width: 200px; | |
582 | + } | |
583 | +} | |
584 | +@media screen and (max-width: 1280px) { | |
585 | + .imageTotal { | |
586 | + display: inline-block; | |
587 | + margin: 0 5px; | |
588 | + text-align: center; | |
589 | + width: 200px; | |
590 | + } | |
591 | +} | |
592 | +@media screen and (min-width: 1537px) { | |
593 | + .image-row img { | |
594 | + width: 240px; | |
595 | + height: 240px; | |
596 | + } | |
597 | +} | |
598 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
599 | + .image-row img { | |
600 | + width: 140px; | |
601 | + height: 140px; | |
602 | + } | |
603 | +} | |
604 | +@media screen and (max-width: 1280px) { | |
605 | + .image-row img { | |
606 | + width: 120px; | |
607 | + height: 120px; | |
608 | + } | |
609 | +} | |
610 | +@media screen and (min-width: 1537px) { | |
611 | + .image-name { | |
612 | + display: -webkit-box; /* Enables multi-line text handling */ | |
613 | + -webkit-box-orient: vertical; /* Defines the vertical orientation of the box */ | |
614 | + -webkit-line-clamp: 3; /* Limits the text to 3 lines */ | |
615 | + overflow: hidden; /* Hides the overflowed text */ | |
616 | + text-overflow: ellipsis; /* Adds an ellipsis when the text overflows */ | |
617 | + margin-top: 5px; | |
618 | + font-size: 16px; | |
619 | + width: 180px; | |
620 | + color: #555; | |
621 | + text-align: center; /* Centers the text horizontally */ | |
622 | + margin-left: 50px; | |
623 | + } | |
624 | +} | |
625 | +@media screen and (max-width: 1536px) and (min-width: 1281px) { | |
626 | + .image-name { | |
627 | + display: -webkit-box; /* Enables multi-line text handling */ | |
628 | + -webkit-box-orient: vertical; /* Defines the vertical orientation of the box */ | |
629 | + -webkit-line-clamp: 3; /* Limits the text to 3 lines */ | |
630 | + overflow: hidden; /* Hides the overflowed text */ | |
631 | + text-overflow: ellipsis; /* Adds an ellipsis when the text overflows */ | |
632 | + margin-top: 5px; | |
633 | + font-size: 16px; | |
634 | + width: 180px; | |
635 | + color: #555; | |
636 | + text-align: center; /* Centers the text horizontally */ | |
637 | + margin-left: 10px; | |
638 | + } | |
639 | +} | |
640 | +@media screen and (max-width: 1280px) { | |
641 | + .image-name { | |
642 | + display: -webkit-box; /* Enables multi-line text handling */ | |
643 | + -webkit-box-orient: vertical; /* Defines the vertical orientation of the box */ | |
644 | + -webkit-line-clamp: 3; /* Limits the text to 3 lines */ | |
645 | + overflow: hidden; /* Hides the overflowed text */ | |
646 | + text-overflow: ellipsis; /* Adds an ellipsis when the text overflows */ | |
647 | + margin-top: 5px; | |
648 | + font-size: 16px; | |
649 | + width: 180px; | |
650 | + color: #555; | |
651 | + text-align: center; /* Centers the text horizontally */ | |
652 | + margin-left: 10px; | |
653 | + } | |
654 | +} | |
655 | + | |
656 | +button .recommendButton { | |
657 | + margin: 0 0px; | |
658 | + cursor: pointer; | |
659 | +} | |
660 | + | |
661 | +.recommend-left-box { | |
662 | +} | |
663 | + | |
664 | +@media screen and (max-width: 1280px) { | |
665 | + .recommend-img-left { | |
666 | + width: 26px; | |
667 | + height: 27px; | |
668 | + margin-bottom: 60px; | |
669 | + } | |
670 | +} | |
671 | +.recommend-img-left { | |
672 | + width: 26px; | |
673 | + height: 27px; | |
674 | + margin-right: 30px; | |
675 | + margin-bottom: 60px; | |
676 | +} | |
677 | + | |
678 | +.recommend-img-left:hover { | |
679 | + cursor: pointer; | |
680 | +} | |
681 | +.recommend-right-box { | |
682 | +} | |
683 | + | |
684 | +.recommend-img-right { | |
685 | + width: 26px; | |
686 | + height: 27px; | |
687 | + margin-left: 30px; | |
688 | + margin-bottom: 60px; | |
689 | +} | |
690 | + | |
691 | +.recommend-img-right:hover { | |
692 | + cursor: pointer; | |
693 | +} | |
694 | + | |
695 | +.v-card { | |
696 | + transition: all 0.3s ease-in-out; | |
697 | +} | |
166 | 698 | </style> | ... | ... |