Commit a7dcdd6b4c4711fa79c93251f88a41bf2d4b908a
0 parents
init
Showing
48 changed files
with
1307 additions
and
0 deletions
Too many changes to show.
To preserve performance only 48 of 49 files are displayed.
.eslintrc.cjs
0 → 100644
1 | +++ a/.eslintrc.cjs | ||
1 | +/* eslint-env node */ | ||
2 | +require('@rushstack/eslint-patch/modern-module-resolution') | ||
3 | + | ||
4 | +module.exports = { | ||
5 | + root: true, | ||
6 | + 'extends': [ | ||
7 | + 'plugin:vue/vue3-essential', | ||
8 | + 'eslint:recommended', | ||
9 | + '@vue/eslint-config-typescript', | ||
10 | + '@vue/eslint-config-prettier/skip-formatting', | ||
11 | + ], | ||
12 | + parserOptions: { | ||
13 | + ecmaVersion: 'latest' | ||
14 | + }, | ||
15 | + rules: { | ||
16 | + 'vue/multi-word-component-names': 'off', | ||
17 | + } | ||
18 | +} |
.gitignore
0 → 100644
1 | +++ a/.gitignore | ||
1 | +# Logs | ||
2 | +logs | ||
3 | +*.log | ||
4 | +npm-debug.log* | ||
5 | +yarn-debug.log* | ||
6 | +yarn-error.log* | ||
7 | +pnpm-debug.log* | ||
8 | +lerna-debug.log* | ||
9 | + | ||
10 | +node_modules | ||
11 | +.DS_Store | ||
12 | +dist | ||
13 | +dist-ssr | ||
14 | +coverage | ||
15 | +*.local | ||
16 | + | ||
17 | +/cypress/videos/ | ||
18 | +/cypress/screenshots/ | ||
19 | + | ||
20 | +# Editor directories and files | ||
21 | +.vscode/* | ||
22 | +!.vscode/extensions.json | ||
23 | +.idea | ||
24 | +*.suo | ||
25 | +*.ntvs* | ||
26 | +*.njsproj | ||
27 | +*.sln | ||
28 | +*.sw? |
.prettierrc.json
0 → 100644
.vscode/extensions.json
0 → 100644
README.md
0 → 100644
1 | +++ a/README.md | ||
1 | +# kelude-web | ||
2 | + | ||
3 | +This template should help get you started developing with Vue 3 in Vite. | ||
4 | + | ||
5 | +## Recommended IDE Setup | ||
6 | + | ||
7 | +[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). | ||
8 | + | ||
9 | +## Type Support for `.vue` Imports in TS | ||
10 | + | ||
11 | +TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types. | ||
12 | + | ||
13 | +If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps: | ||
14 | + | ||
15 | +1. Disable the built-in TypeScript Extension | ||
16 | + 1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette | ||
17 | + 2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)` | ||
18 | +2. Reload the VSCode window by running `Developer: Reload Window` from the command palette. | ||
19 | + | ||
20 | +## Customize configuration | ||
21 | + | ||
22 | +See [Vite Configuration Reference](https://vitejs.dev/config/). | ||
23 | + | ||
24 | +## Project Setup | ||
25 | + | ||
26 | +```sh | ||
27 | +npm install | ||
28 | +``` | ||
29 | + | ||
30 | +### Compile and Hot-Reload for Development | ||
31 | + | ||
32 | +```sh | ||
33 | +npm run dev | ||
34 | +``` | ||
35 | + | ||
36 | +### Type-Check, Compile and Minify for Production | ||
37 | + | ||
38 | +```sh | ||
39 | +npm run build | ||
40 | +``` | ||
41 | + | ||
42 | +### Run Unit Tests with [Vitest](https://vitest.dev/) | ||
43 | + | ||
44 | +```sh | ||
45 | +npm run test:unit | ||
46 | +``` | ||
47 | + | ||
48 | +### Lint with [ESLint](https://eslint.org/) | ||
49 | + | ||
50 | +```sh | ||
51 | +npm run lint | ||
52 | +``` |
env.d.ts
0 → 100644
index.html
0 → 100644
1 | +++ a/index.html | ||
1 | +<!DOCTYPE html> | ||
2 | +<html lang="en"> | ||
3 | + <head> | ||
4 | + <meta charset="UTF-8"> | ||
5 | + <link rel="icon" href="/favicon.ico"> | ||
6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
7 | + <title>Vite App</title> | ||
8 | + </head> | ||
9 | + <body> | ||
10 | + <div id="app"></div> | ||
11 | + <script type="module" src="/src/main.ts"></script> | ||
12 | + </body> | ||
13 | +</html> |
package.json
0 → 100644
1 | +++ a/package.json | ||
1 | +{ | ||
2 | + "name": "kelude-web", | ||
3 | + "version": "0.0.0", | ||
4 | + "private": true, | ||
5 | + "scripts": { | ||
6 | + "dev": "vite", | ||
7 | + "build": "run-p type-check build-only", | ||
8 | + "preview": "vite preview", | ||
9 | + "test:unit": "vitest", | ||
10 | + "build-only": "vite build", | ||
11 | + "type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false", | ||
12 | + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", | ||
13 | + "format": "prettier --write src/" | ||
14 | + }, | ||
15 | + "dependencies": { | ||
16 | + "autoprefixer": "^10.4.14", | ||
17 | + "axios": "^1.4.0", | ||
18 | + "pinia": "^2.0.36", | ||
19 | + "postcss": "^8.4.23", | ||
20 | + "sass": "^1.62.1", | ||
21 | + "tailwindcss": "^3.3.2", | ||
22 | + "vite-plugin-vuetify": "^1.0.2", | ||
23 | + "vue": "^3.3.2", | ||
24 | + "vue-router": "^4.2.0", | ||
25 | + "vuetify": "^3.3.1" | ||
26 | + }, | ||
27 | + "devDependencies": { | ||
28 | + "@mdi/font": "^7.2.96", | ||
29 | + "@rushstack/eslint-patch": "^1.2.0", | ||
30 | + "@tsconfig/node18": "^2.0.1", | ||
31 | + "@types/jsdom": "^21.1.1", | ||
32 | + "@types/node": "^18.16.8", | ||
33 | + "@vitejs/plugin-vue": "^4.2.3", | ||
34 | + "@vitejs/plugin-vue-jsx": "^3.0.1", | ||
35 | + "@vue/eslint-config-prettier": "^7.1.0", | ||
36 | + "@vue/eslint-config-typescript": "^11.0.3", | ||
37 | + "@vue/test-utils": "^2.3.2", | ||
38 | + "@vue/tsconfig": "^0.4.0", | ||
39 | + "eslint": "^8.39.0", | ||
40 | + "eslint-plugin-vue": "^9.11.0", | ||
41 | + "jsdom": "^22.0.0", | ||
42 | + "material-design-icons-iconfont": "^6.7.0", | ||
43 | + "npm-run-all": "^4.1.5", | ||
44 | + "prettier": "^2.8.8", | ||
45 | + "typescript": "~5.0.4", | ||
46 | + "vite": "^4.3.5", | ||
47 | + "vitest": "^0.31.0", | ||
48 | + "vue-tsc": "^1.6.4" | ||
49 | + } | ||
50 | +} |
postcss.config.js
0 → 100644
public/favicon.ico
0 → 100644
No preview for this file type
src/App.vue
0 → 100644
1 | +++ a/src/App.vue | ||
1 | +<script setup lang="ts"> | ||
2 | +import { onMounted, watchEffect, toValue } from 'vue' | ||
3 | +import { RouterLink, RouterView } from 'vue-router' | ||
4 | +import Header from '@/components/Header.vue' | ||
5 | +import Footer from '@/components/Footer.vue' | ||
6 | +import axios from 'axios' | ||
7 | +import { useCategoryStore } from './stores/category' | ||
8 | +import { useProductListStore } from './stores/product_list' | ||
9 | + | ||
10 | +const categoryStore = useCategoryStore() | ||
11 | +const productListStore = useProductListStore() | ||
12 | + | ||
13 | +onMounted(() => { | ||
14 | + // 请求分类列表 | ||
15 | + categoryStore.getList() | ||
16 | +}) | ||
17 | + | ||
18 | +watchEffect(() => { | ||
19 | + if (toValue(categoryStore?.list)) { | ||
20 | + const cId = categoryStore?.list?.[0]?.list?.[0]?.id | ||
21 | + if (cId) productListStore.updateCategory(cId) | ||
22 | + } | ||
23 | +}) | ||
24 | +</script> | ||
25 | + | ||
26 | +<template> | ||
27 | + <Header /> | ||
28 | + <div class="tw-min-h-[700px]"> | ||
29 | + <RouterView /> | ||
30 | + </div> | ||
31 | + <Footer /> | ||
32 | +</template> |
src/assets/1.jpeg
0 → 100644
24.5 KB
src/assets/banner1.png
0 → 100644
213 KB
src/assets/banner2.jpeg
0 → 100644
66 KB
src/assets/banner3.jpeg
0 → 100644
75.9 KB
src/assets/base.css
0 → 100644
1 | +++ a/src/assets/base.css | ||
1 | +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ | ||
2 | + | ||
3 | +/* Document | ||
4 | + ========================================================================== */ | ||
5 | + | ||
6 | +/** | ||
7 | + * 1. Correct the line height in all browsers. | ||
8 | + * 2. Prevent adjustments of font size after orientation changes in iOS. | ||
9 | + */ | ||
10 | + | ||
11 | +html { | ||
12 | + line-height: 1.15; /* 1 */ | ||
13 | + -webkit-text-size-adjust: 100%; /* 2 */ | ||
14 | +} | ||
15 | + | ||
16 | +/* Sections | ||
17 | + ========================================================================== */ | ||
18 | + | ||
19 | +/** | ||
20 | + * Remove the margin in all browsers. | ||
21 | + */ | ||
22 | + | ||
23 | +body { | ||
24 | + margin: 0; | ||
25 | +} | ||
26 | + | ||
27 | +/** | ||
28 | + * Render the `main` element consistently in IE. | ||
29 | + */ | ||
30 | + | ||
31 | +main { | ||
32 | + display: block; | ||
33 | +} | ||
34 | + | ||
35 | +/** | ||
36 | + * Correct the font size and margin on `h1` elements within `section` and | ||
37 | + * `article` contexts in Chrome, Firefox, and Safari. | ||
38 | + */ | ||
39 | + | ||
40 | +h1 { | ||
41 | + font-size: 2em; | ||
42 | + margin: 0.67em 0; | ||
43 | +} | ||
44 | + | ||
45 | +/* Grouping content | ||
46 | + ========================================================================== */ | ||
47 | + | ||
48 | +/** | ||
49 | + * 1. Add the correct box sizing in Firefox. | ||
50 | + * 2. Show the overflow in Edge and IE. | ||
51 | + */ | ||
52 | + | ||
53 | +hr { | ||
54 | + box-sizing: content-box; /* 1 */ | ||
55 | + height: 0; /* 1 */ | ||
56 | + overflow: visible; /* 2 */ | ||
57 | +} | ||
58 | + | ||
59 | +/** | ||
60 | + * 1. Correct the inheritance and scaling of font size in all browsers. | ||
61 | + * 2. Correct the odd `em` font sizing in all browsers. | ||
62 | + */ | ||
63 | + | ||
64 | +pre { | ||
65 | + font-family: monospace, monospace; /* 1 */ | ||
66 | + font-size: 1em; /* 2 */ | ||
67 | +} | ||
68 | + | ||
69 | +/* Text-level semantics | ||
70 | + ========================================================================== */ | ||
71 | + | ||
72 | +/** | ||
73 | + * Remove the gray background on active links in IE 10. | ||
74 | + */ | ||
75 | + | ||
76 | +a { | ||
77 | + background-color: transparent; | ||
78 | +} | ||
79 | + | ||
80 | +/** | ||
81 | + * 1. Remove the bottom border in Chrome 57- | ||
82 | + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. | ||
83 | + */ | ||
84 | + | ||
85 | +abbr[title] { | ||
86 | + border-bottom: none; /* 1 */ | ||
87 | + text-decoration: underline; /* 2 */ | ||
88 | + text-decoration: underline dotted; /* 2 */ | ||
89 | +} | ||
90 | + | ||
91 | +/** | ||
92 | + * Add the correct font weight in Chrome, Edge, and Safari. | ||
93 | + */ | ||
94 | + | ||
95 | +b, | ||
96 | +strong { | ||
97 | + font-weight: bolder; | ||
98 | +} | ||
99 | + | ||
100 | +/** | ||
101 | + * 1. Correct the inheritance and scaling of font size in all browsers. | ||
102 | + * 2. Correct the odd `em` font sizing in all browsers. | ||
103 | + */ | ||
104 | + | ||
105 | +code, | ||
106 | +kbd, | ||
107 | +samp { | ||
108 | + font-family: monospace, monospace; /* 1 */ | ||
109 | + font-size: 1em; /* 2 */ | ||
110 | +} | ||
111 | + | ||
112 | +/** | ||
113 | + * Add the correct font size in all browsers. | ||
114 | + */ | ||
115 | + | ||
116 | +small { | ||
117 | + font-size: 80%; | ||
118 | +} | ||
119 | + | ||
120 | +/** | ||
121 | + * Prevent `sub` and `sup` elements from affecting the line height in | ||
122 | + * all browsers. | ||
123 | + */ | ||
124 | + | ||
125 | +sub, | ||
126 | +sup { | ||
127 | + font-size: 75%; | ||
128 | + line-height: 0; | ||
129 | + position: relative; | ||
130 | + vertical-align: baseline; | ||
131 | +} | ||
132 | + | ||
133 | +sub { | ||
134 | + bottom: -0.25em; | ||
135 | +} | ||
136 | + | ||
137 | +sup { | ||
138 | + top: -0.5em; | ||
139 | +} | ||
140 | + | ||
141 | +/* Embedded content | ||
142 | + ========================================================================== */ | ||
143 | + | ||
144 | +/** | ||
145 | + * Remove the border on images inside links in IE 10. | ||
146 | + */ | ||
147 | + | ||
148 | +img { | ||
149 | + border-style: none; | ||
150 | +} | ||
151 | + | ||
152 | +/* Forms | ||
153 | + ========================================================================== */ | ||
154 | + | ||
155 | +/** | ||
156 | + * 1. Change the font styles in all browsers. | ||
157 | + * 2. Remove the margin in Firefox and Safari. | ||
158 | + */ | ||
159 | + | ||
160 | +button, | ||
161 | +input, | ||
162 | +optgroup, | ||
163 | +select, | ||
164 | +textarea { | ||
165 | + font-family: inherit; /* 1 */ | ||
166 | + font-size: 100%; /* 1 */ | ||
167 | + line-height: 1.15; /* 1 */ | ||
168 | + margin: 0; /* 2 */ | ||
169 | +} | ||
170 | + | ||
171 | +/** | ||
172 | + * Show the overflow in IE. | ||
173 | + * 1. Show the overflow in Edge. | ||
174 | + */ | ||
175 | + | ||
176 | +button, | ||
177 | +input { | ||
178 | + /* 1 */ | ||
179 | + overflow: visible; | ||
180 | +} | ||
181 | + | ||
182 | +/** | ||
183 | + * Remove the inheritance of text transform in Edge, Firefox, and IE. | ||
184 | + * 1. Remove the inheritance of text transform in Firefox. | ||
185 | + */ | ||
186 | + | ||
187 | +button, | ||
188 | +select { | ||
189 | + /* 1 */ | ||
190 | + text-transform: none; | ||
191 | +} | ||
192 | + | ||
193 | +/** | ||
194 | + * Correct the inability to style clickable types in iOS and Safari. | ||
195 | + */ | ||
196 | + | ||
197 | +button, | ||
198 | +[type='button'], | ||
199 | +[type='reset'], | ||
200 | +[type='submit'] { | ||
201 | + -webkit-appearance: button; | ||
202 | +} | ||
203 | + | ||
204 | +/** | ||
205 | + * Remove the inner border and padding in Firefox. | ||
206 | + */ | ||
207 | + | ||
208 | +button::-moz-focus-inner, | ||
209 | +[type='button']::-moz-focus-inner, | ||
210 | +[type='reset']::-moz-focus-inner, | ||
211 | +[type='submit']::-moz-focus-inner { | ||
212 | + border-style: none; | ||
213 | + padding: 0; | ||
214 | +} | ||
215 | + | ||
216 | +/** | ||
217 | + * Restore the focus styles unset by the previous rule. | ||
218 | + */ | ||
219 | + | ||
220 | +button:-moz-focusring, | ||
221 | +[type='button']:-moz-focusring, | ||
222 | +[type='reset']:-moz-focusring, | ||
223 | +[type='submit']:-moz-focusring { | ||
224 | + outline: 1px dotted ButtonText; | ||
225 | +} | ||
226 | + | ||
227 | +/** | ||
228 | + * Correct the padding in Firefox. | ||
229 | + */ | ||
230 | + | ||
231 | +fieldset { | ||
232 | + padding: 0.35em 0.75em 0.625em; | ||
233 | +} | ||
234 | + | ||
235 | +/** | ||
236 | + * 1. Correct the text wrapping in Edge and IE. | ||
237 | + * 2. Correct the color inheritance from `fieldset` elements in IE. | ||
238 | + * 3. Remove the padding so developers are not caught out when they zero out | ||
239 | + * `fieldset` elements in all browsers. | ||
240 | + */ | ||
241 | + | ||
242 | +legend { | ||
243 | + box-sizing: border-box; /* 1 */ | ||
244 | + color: inherit; /* 2 */ | ||
245 | + display: table; /* 1 */ | ||
246 | + max-width: 100%; /* 1 */ | ||
247 | + padding: 0; /* 3 */ | ||
248 | + white-space: normal; /* 1 */ | ||
249 | +} | ||
250 | + | ||
251 | +/** | ||
252 | + * Add the correct vertical alignment in Chrome, Firefox, and Opera. | ||
253 | + */ | ||
254 | + | ||
255 | +progress { | ||
256 | + vertical-align: baseline; | ||
257 | +} | ||
258 | + | ||
259 | +/** | ||
260 | + * Remove the default vertical scrollbar in IE 10+. | ||
261 | + */ | ||
262 | + | ||
263 | +textarea { | ||
264 | + overflow: auto; | ||
265 | +} | ||
266 | + | ||
267 | +/** | ||
268 | + * 1. Add the correct box sizing in IE 10. | ||
269 | + * 2. Remove the padding in IE 10. | ||
270 | + */ | ||
271 | + | ||
272 | +[type='checkbox'], | ||
273 | +[type='radio'] { | ||
274 | + box-sizing: border-box; /* 1 */ | ||
275 | + padding: 0; /* 2 */ | ||
276 | +} | ||
277 | + | ||
278 | +/** | ||
279 | + * Correct the cursor style of increment and decrement buttons in Chrome. | ||
280 | + */ | ||
281 | + | ||
282 | +[type='number']::-webkit-inner-spin-button, | ||
283 | +[type='number']::-webkit-outer-spin-button { | ||
284 | + height: auto; | ||
285 | +} | ||
286 | + | ||
287 | +/** | ||
288 | + * 1. Correct the odd appearance in Chrome and Safari. | ||
289 | + * 2. Correct the outline style in Safari. | ||
290 | + */ | ||
291 | + | ||
292 | +[type='search'] { | ||
293 | + -webkit-appearance: textfield; /* 1 */ | ||
294 | + outline-offset: -2px; /* 2 */ | ||
295 | +} | ||
296 | + | ||
297 | +/** | ||
298 | + * Remove the inner padding in Chrome and Safari on macOS. | ||
299 | + */ | ||
300 | + | ||
301 | +[type='search']::-webkit-search-decoration { | ||
302 | + -webkit-appearance: none; | ||
303 | +} | ||
304 | + | ||
305 | +/** | ||
306 | + * 1. Correct the inability to style clickable types in iOS and Safari. | ||
307 | + * 2. Change font properties to `inherit` in Safari. | ||
308 | + */ | ||
309 | + | ||
310 | +::-webkit-file-upload-button { | ||
311 | + -webkit-appearance: button; /* 1 */ | ||
312 | + font: inherit; /* 2 */ | ||
313 | +} | ||
314 | + | ||
315 | +/* Interactive | ||
316 | + ========================================================================== */ | ||
317 | + | ||
318 | +/* | ||
319 | + * Add the correct display in Edge, IE 10+, and Firefox. | ||
320 | + */ | ||
321 | + | ||
322 | +details { | ||
323 | + display: block; | ||
324 | +} | ||
325 | + | ||
326 | +/* | ||
327 | + * Add the correct display in all browsers. | ||
328 | + */ | ||
329 | + | ||
330 | +summary { | ||
331 | + display: list-item; | ||
332 | +} | ||
333 | + | ||
334 | +/* Misc | ||
335 | + ========================================================================== */ | ||
336 | + | ||
337 | +/** | ||
338 | + * Add the correct display in IE 10+. | ||
339 | + */ | ||
340 | + | ||
341 | +template { | ||
342 | + display: none; | ||
343 | +} | ||
344 | + | ||
345 | +/** | ||
346 | + * Add the correct display in IE 10. | ||
347 | + */ | ||
348 | + | ||
349 | +[hidden] { | ||
350 | + display: none; | ||
351 | +} | ||
352 | + | ||
353 | +li { | ||
354 | + list-style: none; | ||
355 | +} | ||
356 | + | ||
357 | +a { | ||
358 | + color: #fff; | ||
359 | + text-decoration: none; | ||
360 | +} |
src/assets/logo.png
0 → 100644
2.28 KB
src/assets/logo.svg
0 → 100644
1 | +++ a/src/assets/logo.svg | ||
1 | +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg> |
src/assets/main.css
0 → 100644
src/components/CategoryList.vue
0 → 100644
1 | +++ a/src/components/CategoryList.vue | ||
1 | +<template> | ||
2 | + <v-container> | ||
3 | + <div height="100" class="pa-2"> | ||
4 | + <v-row> | ||
5 | + <div class="tw-pr-0 tw-font-bold tw-w-[95px] tw-h-[36px] tw-leading-[36px]">一级类别:</div> | ||
6 | + <v-col class="pa-0"> | ||
7 | + <v-btn | ||
8 | + v-for="(item, index) in categoryStore.list" | ||
9 | + :key="index" | ||
10 | + variant="text" | ||
11 | + @click="handleCategoryClick(item)" | ||
12 | + :color=" | ||
13 | + categoryStore.selectedCategory === item.categoryDisplayName ? 'blue-accent-2' : '' | ||
14 | + " | ||
15 | + > | ||
16 | + {{ item.categoryDisplayName }} | ||
17 | + </v-btn> | ||
18 | + </v-col> | ||
19 | + </v-row> | ||
20 | + <v-row> | ||
21 | + <div class="tw-pr-0 tw-font-bold tw-w-[95px] tw-h-[36px] tw-leading-[36px]">二级类别:</div> | ||
22 | + <v-col class="pa-0"> | ||
23 | + <v-btn | ||
24 | + v-for="(item, index) in subCategoryList" | ||
25 | + :key="index" | ||
26 | + variant="text" | ||
27 | + @click="handleSubCategoryClick(item.id)" | ||
28 | + :color="categoryStore.selectedSubCategory === item.id ? 'blue-accent-2' : ''" | ||
29 | + > | ||
30 | + {{ item.name }} | ||
31 | + </v-btn> | ||
32 | + </v-col> | ||
33 | + </v-row> | ||
34 | + </div> | ||
35 | + </v-container> | ||
36 | +</template> | ||
37 | + | ||
38 | +<script setup lang="ts"> | ||
39 | +import { useCategoryStore } from '@/stores/category' | ||
40 | +import type { CategoryRootType } from '@/type' | ||
41 | +import { computed } from 'vue' | ||
42 | + | ||
43 | +const categoryStore = useCategoryStore() | ||
44 | + | ||
45 | +const handleCategoryClick = (item: CategoryRootType) => { | ||
46 | + categoryStore.updateCategory(item.categoryDisplayName) | ||
47 | + categoryStore.updateSubCategory(item.list[0].id) | ||
48 | +} | ||
49 | + | ||
50 | +const handleSubCategoryClick = (value: string) => { | ||
51 | + categoryStore.updateSubCategory(value) | ||
52 | +} | ||
53 | + | ||
54 | +const subCategoryList = computed(() => { | ||
55 | + if (categoryStore.selectedCategory) { | ||
56 | + const tmp = categoryStore.list.filter( | ||
57 | + (item) => item.categoryDisplayName === categoryStore.selectedCategory | ||
58 | + ) | ||
59 | + return tmp?.[0]?.list || [] | ||
60 | + } | ||
61 | + return [] | ||
62 | +}) | ||
63 | +</script> |
src/components/Footer.vue
0 → 100644
src/components/Header.vue
0 → 100644
1 | +++ a/src/components/Header.vue | ||
1 | +<template> | ||
2 | + <v-container> | ||
3 | + <div class="tw-m-auto"> | ||
4 | + <div class="tw-mr-[32px] tw-float-left tw-h-[64px] tw-mt-[-4px]"> | ||
5 | + <img src="@/assets/logo.png" /> | ||
6 | + </div> | ||
7 | + <div class="tw-m-auto"> | ||
8 | + <v-text-field | ||
9 | + density="comfortable" | ||
10 | + label="请输入搜索内容" | ||
11 | + hide-details="auto" | ||
12 | + variant="solo" | ||
13 | + append-inner-icon="mdi-magnify" | ||
14 | + ></v-text-field> | ||
15 | + </div> | ||
16 | + </div> | ||
17 | + </v-container> | ||
18 | + <div class="tabs tw-mb-[24px]"> | ||
19 | + <div class="tw-max-w-[1200px] tw-mx-auto"> | ||
20 | + <v-tabs v-model="tab" bg-color="blue-darken-2" slider-color="yellow-darken-4"> | ||
21 | + <v-tab :value="1" to="/">首页 </v-tab> | ||
22 | + <v-tab :value="2" to="/products">产品中心</v-tab> | ||
23 | + <v-tab :value="3" to="/contact">联系我们</v-tab> | ||
24 | + </v-tabs> | ||
25 | + </div> | ||
26 | + </div> | ||
27 | +</template> | ||
28 | + | ||
29 | +<script setup lang="ts"> | ||
30 | +import { ref, onMounted } from 'vue' | ||
31 | + | ||
32 | +const tab = ref(1) | ||
33 | +const handleTabsChange = (val) => { | ||
34 | + console.log(12121) | ||
35 | +} | ||
36 | +</script> | ||
37 | + | ||
38 | +<style lang="scss" scoped> | ||
39 | +.tabs { | ||
40 | + background-color: #1976d2; | ||
41 | +} | ||
42 | +</style> |
src/components/HomeCategoryList.vue
0 → 100644
1 | +++ a/src/components/HomeCategoryList.vue | ||
1 | +<template> | ||
2 | + <v-container class=""> | ||
3 | + <div class="text-blue-darken-2 text-h3 tw-text-center tw-mb-16">{{ title }}</div> | ||
4 | + <v-item-group multiple> | ||
5 | + <v-row> | ||
6 | + <v-col v-for="(item, index) in list" :key="index" cols="12" lg="3" md="4" sm="6"> | ||
7 | + <v-hover v-slot="{ isHovering, props }" open-delay="200"> | ||
8 | + <v-card | ||
9 | + :elevation="isHovering ? 16 : 2" | ||
10 | + :class="{ 'on-hover': isHovering }" | ||
11 | + class="mx-auto" | ||
12 | + height="350" | ||
13 | + width="260" | ||
14 | + v-bind="props" | ||
15 | + > | ||
16 | + <v-img :src="item.imageUrl" /> | ||
17 | + <v-card-text class="text-center font-weight-medium text-subtitle-1"> | ||
18 | + {{ item.name }} | ||
19 | + </v-card-text> | ||
20 | + </v-card> | ||
21 | + </v-hover> | ||
22 | + </v-col> | ||
23 | + </v-row> | ||
24 | + </v-item-group> | ||
25 | + </v-container> | ||
26 | + <!-- <li class="tw-flex-1 tw-flex-row item tw-mb-8"></li> --> | ||
27 | +</template> | ||
28 | + | ||
29 | +<script setup lang="ts"> | ||
30 | +import type { Category } from '@/type' | ||
31 | +import { onMounted, ref } from 'vue' | ||
32 | + | ||
33 | +defineProps<{ | ||
34 | + title: string | ||
35 | + list: Category[] | ||
36 | +}>() | ||
37 | +</script> |
src/components/__tests__/HelloWorld.spec.ts
0 → 100644
1 | +++ a/src/components/__tests__/HelloWorld.spec.ts | ||
1 | +import { describe, it, expect } from 'vitest' | ||
2 | + | ||
3 | +import { mount } from '@vue/test-utils' | ||
4 | +import HelloWorld from '../HelloWorld.vue' | ||
5 | + | ||
6 | +describe('HelloWorld', () => { | ||
7 | + it('renders properly', () => { | ||
8 | + const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } }) | ||
9 | + expect(wrapper.text()).toContain('Hello Vitest') | ||
10 | + }) | ||
11 | +}) |
src/components/icons/IconCommunity.vue
0 → 100644
1 | +++ a/src/components/icons/IconCommunity.vue | ||
1 | +<template> | ||
2 | + <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor"> | ||
3 | + <path | ||
4 | + d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z" | ||
5 | + /> | ||
6 | + </svg> | ||
7 | +</template> |
src/components/icons/IconDocumentation.vue
0 → 100644
1 | +++ a/src/components/icons/IconDocumentation.vue | ||
1 | +<template> | ||
2 | + <svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor"> | ||
3 | + <path | ||
4 | + d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z" | ||
5 | + /> | ||
6 | + </svg> | ||
7 | +</template> |
src/components/icons/IconEcosystem.vue
0 → 100644
1 | +++ a/src/components/icons/IconEcosystem.vue | ||
1 | +<template> | ||
2 | + <svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor"> | ||
3 | + <path | ||
4 | + d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z" | ||
5 | + /> | ||
6 | + </svg> | ||
7 | +</template> |
src/components/icons/IconSupport.vue
0 → 100644
1 | +++ a/src/components/icons/IconSupport.vue | ||
1 | +<template> | ||
2 | + <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor"> | ||
3 | + <path | ||
4 | + d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z" | ||
5 | + /> | ||
6 | + </svg> | ||
7 | +</template> |
src/components/icons/IconTooling.vue
0 → 100644
1 | +++ a/src/components/icons/IconTooling.vue | ||
1 | +<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license--> | ||
2 | +<template> | ||
3 | + <svg | ||
4 | + xmlns="http://www.w3.org/2000/svg" | ||
5 | + xmlns:xlink="http://www.w3.org/1999/xlink" | ||
6 | + aria-hidden="true" | ||
7 | + role="img" | ||
8 | + class="iconify iconify--mdi" | ||
9 | + width="24" | ||
10 | + height="24" | ||
11 | + preserveAspectRatio="xMidYMid meet" | ||
12 | + viewBox="0 0 24 24" | ||
13 | + > | ||
14 | + <path | ||
15 | + d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z" | ||
16 | + fill="currentColor" | ||
17 | + ></path> | ||
18 | + </svg> | ||
19 | +</template> |
src/constant.ts
0 → 100644
1 | +++ a/src/constant.ts |
src/constant/index.ts
0 → 100644
src/main.ts
0 → 100644
1 | +++ a/src/main.ts | ||
1 | +import './assets/main.css' | ||
2 | + | ||
3 | +// Vuetify | ||
4 | +import 'vuetify/styles' | ||
5 | +import { createVuetify } from 'vuetify' | ||
6 | +// import 'material-design-icons-iconfont/dist/material-design-icons.css' | ||
7 | +import '@mdi/font/css/materialdesignicons.css' // Ensure you are using css-loader | ||
8 | + | ||
9 | +import { createApp } from 'vue' | ||
10 | +import { createPinia } from 'pinia' | ||
11 | + | ||
12 | +import App from './App.vue' | ||
13 | +import router from './router' | ||
14 | + | ||
15 | +const app = createApp(App) | ||
16 | + | ||
17 | +const vuetify = createVuetify() | ||
18 | + | ||
19 | +app.use(createPinia()) | ||
20 | +app.use(router) | ||
21 | + | ||
22 | +app.use(vuetify) | ||
23 | + | ||
24 | +app.mount('#app') |
src/router/index.ts
0 → 100644
1 | +++ a/src/router/index.ts | ||
1 | +import { createRouter, createWebHistory } from 'vue-router' | ||
2 | +import Home from '../views/Home.vue' | ||
3 | +import Contact from '../views/Contact.vue' | ||
4 | +import ProductList from '../views/ProductList.vue' | ||
5 | +import ProductDetail from '../views/ProductDetail.vue' | ||
6 | + | ||
7 | +const router = createRouter({ | ||
8 | + history: createWebHistory(import.meta.env.BASE_URL), | ||
9 | + routes: [ | ||
10 | + { | ||
11 | + path: '/', | ||
12 | + name: 'home', | ||
13 | + component: Home | ||
14 | + }, | ||
15 | + { | ||
16 | + path: '/products/detail/:id', | ||
17 | + name: 'detail', | ||
18 | + component: ProductDetail | ||
19 | + }, | ||
20 | + { | ||
21 | + path: '/products', | ||
22 | + name: 'products', | ||
23 | + component: ProductList | ||
24 | + }, | ||
25 | + { | ||
26 | + path: '/contact', | ||
27 | + name: 'contact', | ||
28 | + component: Contact | ||
29 | + } | ||
30 | + // { | ||
31 | + // path: '/about', | ||
32 | + // name: 'about', | ||
33 | + // // route level code-splitting | ||
34 | + // // this generates a separate chunk (About.[hash].js) for this route | ||
35 | + // // which is lazy-loaded when the route is visited. | ||
36 | + // // component: () => import('../views/AboutView.vue') | ||
37 | + // } | ||
38 | + ] | ||
39 | +}) | ||
40 | + | ||
41 | +export default router |
src/service.ts
0 → 100644
1 | +++ a/src/service.ts | ||
1 | +import axios from 'axios' | ||
2 | +import type { ProductListQuery } from './type' | ||
3 | + | ||
4 | +export const getCategoryList = () => { | ||
5 | + return axios.post('/shop/product/category', {}) | ||
6 | +} | ||
7 | + | ||
8 | +export const getProductList = (params: ProductListQuery) => { | ||
9 | + return axios.post('/shop/product/list', params) | ||
10 | +} | ||
11 | + | ||
12 | +export const getDetail = (params: { id: string }) => { | ||
13 | + return axios.post('/shop/product/detail', params) | ||
14 | +} |
src/stores/category.ts
0 → 100644
1 | +++ a/src/stores/category.ts | ||
1 | +import { getCategoryList } from './../service' | ||
2 | +import { ref } from 'vue' | ||
3 | +import { defineStore } from 'pinia' | ||
4 | +import type { Category, CategoryRootType } from '@/type' | ||
5 | + | ||
6 | +export const useCategoryStore = defineStore('category', () => { | ||
7 | + const list = ref<CategoryRootType[]>([]) | ||
8 | + const selectedCategory = ref('') // 选中的一级类别 | ||
9 | + const selectedSubCategory = ref('') // 选中的二级类别 | ||
10 | + | ||
11 | + const getList = () => { | ||
12 | + getCategoryList().then((res) => { | ||
13 | + const rootList = res.data?.data?.rootCategoryList | ||
14 | + list.value = rootList || [] | ||
15 | + selectedCategory.value = rootList[0].categoryDisplayName | ||
16 | + selectedSubCategory.value = rootList[0].list[0].id | ||
17 | + }) | ||
18 | + } | ||
19 | + | ||
20 | + const updateCategory = (value: string) => { | ||
21 | + selectedCategory.value = value | ||
22 | + } | ||
23 | + | ||
24 | + const updateSubCategory = (value: string) => { | ||
25 | + selectedSubCategory.value = value | ||
26 | + } | ||
27 | + | ||
28 | + return { list, selectedCategory, selectedSubCategory, getList, updateCategory, updateSubCategory } | ||
29 | +}) |
src/stores/product_list.ts
0 → 100644
1 | +++ a/src/stores/product_list.ts | ||
1 | +import { getProductList } from './../service' | ||
2 | +import { ref } from 'vue' | ||
3 | +import { defineStore } from 'pinia' | ||
4 | +import type { Product, ProductListQuery } from '@/type' | ||
5 | + | ||
6 | +export const useProductListStore = defineStore('productList', () => { | ||
7 | + const list = ref<Product[]>([]) | ||
8 | + const productCategoryId = ref('') // 选中的类别 | ||
9 | + const keywork = ref(null) | ||
10 | + const pageNo = ref('') | ||
11 | + const pageSize = ref(20) | ||
12 | + const total = ref(0) | ||
13 | + | ||
14 | + const getList = (params: ProductListQuery) => { | ||
15 | + if (params.productCategoryId) { | ||
16 | + getProductList(params).then((res) => { | ||
17 | + const data = res.data?.data || {} | ||
18 | + list.value = data?.records || [] | ||
19 | + total.value = data?.total || 0 | ||
20 | + }) | ||
21 | + } | ||
22 | + } | ||
23 | + | ||
24 | + const updateCategory = (value: string) => { | ||
25 | + productCategoryId.value = value | ||
26 | + } | ||
27 | + | ||
28 | + return { total, list, getList, updateCategory } | ||
29 | +}) |
src/type.d.ts
0 → 100644
1 | +++ a/src/type.d.ts | ||
1 | +import { Category } from './type.d' | ||
2 | +export interface Category { | ||
3 | + name: string | ||
4 | + imageUrl: string | ||
5 | + id: string | ||
6 | +} | ||
7 | + | ||
8 | +export interface CategoryRootType { | ||
9 | + categoryDisplayName: string | ||
10 | + list: Category[] | ||
11 | +} | ||
12 | + | ||
13 | +export interface Product { | ||
14 | + name: string | ||
15 | + id: string | ||
16 | + imageUrl: string | ||
17 | +} | ||
18 | + | ||
19 | +export interface ProductListQuery { | ||
20 | + productCategoryId: string | ||
21 | + keyword?: string | ||
22 | + pageNo: number | ||
23 | + pageSize: number | ||
24 | +} |
src/views/Contact.vue
0 → 100644
1 | +++ a/src/views/Contact.vue | ||
1 | +<template> | ||
2 | + <v-card width="800" class="pa-10 tw-m-auto"> | ||
3 | + <div class="tw-text-center tw-text-4xl tw-mb-20">联系我们</div> | ||
4 | + <h3 class="text-h5 tw-mb-5">联系我们</h3> | ||
5 | + <div class="tw-mb-10"> | ||
6 | + <label class="text-subtitle-1 tw-mr-4 tw-w-20 tw-inline-block">官网地址</label> | ||
7 | + <span>http://www.canrd.com</span> | ||
8 | + </div> | ||
9 | + | ||
10 | + <h3 class="text-h5 tw-mb-5">技术中心</h3> | ||
11 | + <div> | ||
12 | + <label class="text-subtitle-1 tw-mr-4 tw-w-20 tw-inline-block">QQ</label> | ||
13 | + <span>3003597584/2902385824</span> | ||
14 | + </div> | ||
15 | + <div class="tw-mb-10"> | ||
16 | + <label class="text-subtitle-1 tw-mr-4 tw-w-20 tw-inline-block">Email</label> | ||
17 | + <span>Linda@canrd.com</span> | ||
18 | + </div> | ||
19 | + <div> | ||
20 | + <h3 class="text-h5 tw-mb-5">关注微信</h3> | ||
21 | + <div></div> | ||
22 | + </div> | ||
23 | + </v-card> | ||
24 | +</template> | ||
25 | + | ||
26 | +<script setup lang="ts"></script> | ||
27 | + | ||
28 | +<style lang="less" scoped></style> |
src/views/Home.vue
0 → 100644
1 | +++ a/src/views/Home.vue | ||
1 | +<template> | ||
2 | + <v-container class="mx-auto content"> | ||
3 | + <v-carousel cycle height="360" hide-delimiter-background show-arrows="hover" class="tw-mb-16"> | ||
4 | + <v-carousel-item v-for="(slide, i) in slides" :key="i" :src="slide" cover> </v-carousel-item> | ||
5 | + </v-carousel> | ||
6 | + <div v-for="item in store.list" :key="item.categoryDisplayName" class="tw-mb-[64px]"> | ||
7 | + <HomeProductList :title="item.categoryDisplayName" :list="item.list" /> | ||
8 | + </div> | ||
9 | + </v-container> | ||
10 | +</template> | ||
11 | + | ||
12 | +<script setup lang="ts"> | ||
13 | +import HomeProductList from '@/components/HomeCategoryList.vue' | ||
14 | +import { useCategoryStore } from '@/stores/category' | ||
15 | +const slides = ['src/assets/banner1.png', 'src/assets/banner2.jpeg', 'src/assets/banner3.jpeg'] | ||
16 | + | ||
17 | +const store = useCategoryStore() | ||
18 | +</script> | ||
19 | + | ||
20 | +<style lang="scss" scoped> | ||
21 | +.content { | ||
22 | + max-width: 1200px; | ||
23 | +} | ||
24 | +</style> |
src/views/ProductDetail.vue
0 → 100644
1 | +++ a/src/views/ProductDetail.vue | ||
1 | +<template> | ||
2 | + <v-container class="pa-1"> | ||
3 | + <v-breadcrumbs class="pt-0" :items="['首页', '产品中心', '详情']"></v-breadcrumbs> | ||
4 | + <v-card max-width="1000" class="tw-m-auto tw-mb-[32px]"> | ||
5 | + <v-row class="ma-0"> | ||
6 | + <div class="tw-float-left tw-w-[300px]"> | ||
7 | + <v-carousel | ||
8 | + show-arrows="hover" | ||
9 | + hide-delimiter-background | ||
10 | + delimiter-icon="mdi-square" | ||
11 | + class="tw-float-left" | ||
12 | + height="300" | ||
13 | + v-model="slide" | ||
14 | + > | ||
15 | + <v-carousel-item | ||
16 | + cover | ||
17 | + v-for="(slide, i) in info.productimageliststore" | ||
18 | + :src="slide.url" | ||
19 | + :key="i" | ||
20 | + > | ||
21 | + </v-carousel-item> | ||
22 | + </v-carousel> | ||
23 | + </div> | ||
24 | + <v-col> | ||
25 | + <v-card-title class="text-h5"> {{ info.name }} </v-card-title> | ||
26 | + <v-row> | ||
27 | + <v-col> | ||
28 | + <v-card-text class="tw-leading-[10px]">品牌:{{ info.brandName }}</v-card-text> | ||
29 | + </v-col> | ||
30 | + <v-col> | ||
31 | + <v-card-text>产品分类:{{ info.brandName }}</v-card-text> | ||
32 | + </v-col> | ||
33 | + </v-row> | ||
34 | + <v-row> | ||
35 | + <v-col> | ||
36 | + <v-card-text>产品型号:{{ info.model }}</v-card-text> | ||
37 | + </v-col> | ||
38 | + <v-col> | ||
39 | + <v-card-text>{{ info.basename1 }}:{{ info.basecore1 }}</v-card-text> | ||
40 | + </v-col> | ||
41 | + </v-row> | ||
42 | + <v-row> | ||
43 | + <v-col> | ||
44 | + <v-card-text>{{ info.basename2 }}:{{ info.basecore2 }}</v-card-text> | ||
45 | + </v-col> | ||
46 | + <v-col> | ||
47 | + <v-card-text>{{ info.basename2 }}:{{ info.basecore3 }}</v-card-text> | ||
48 | + </v-col> | ||
49 | + </v-row> | ||
50 | + </v-col> | ||
51 | + </v-row> | ||
52 | + </v-card> | ||
53 | + <div class="tw-m-auto tw-max-w-[1000px]"> | ||
54 | + <v-tabs v-model="tab" color="blue-lighten-1" align-tabs="start"> | ||
55 | + <v-tab :value="1">商品介绍</v-tab> | ||
56 | + <v-tab :value="2">规格参数</v-tab> | ||
57 | + <!-- <v-tab :value="3">商品百科</v-tab> --> | ||
58 | + </v-tabs> | ||
59 | + <v-window v-model="tab" class="tw-p-[24px]"> | ||
60 | + <v-window-item key="1" :value="1"> | ||
61 | + <div v-html="info.physicalproperty"></div> | ||
62 | + <div v-html="info.storage"></div> | ||
63 | + </v-window-item> | ||
64 | + <v-window-item key="2" :value="2"> 参数规格</v-window-item> | ||
65 | + <!-- <v-window-item key="3" :value="3"> 2 </v-window-item> --> | ||
66 | + </v-window> | ||
67 | + </div> | ||
68 | + </v-container> | ||
69 | +</template> | ||
70 | + | ||
71 | +<script setup lang="ts"> | ||
72 | +import { getDetail } from '@/service' | ||
73 | +import { onMounted, ref } from 'vue' | ||
74 | +import { useRoute } from 'vue-router' | ||
75 | + | ||
76 | +const route = useRoute() | ||
77 | +const info = ref({ | ||
78 | + productimageliststore: [] | ||
79 | +}) | ||
80 | + | ||
81 | +onMounted(() => { | ||
82 | + getDetail({ id: route.params.id as string }).then((res) => { | ||
83 | + const data = res.data.data || {} | ||
84 | + console.log('%c [ data ]-117', 'font-size:13px; background:pink; color:#bf2c9f;', data) | ||
85 | + data.productimageliststore = JSON.parse(data.productimageliststore) || [] | ||
86 | + data.productimageliststore = data.productimageliststore.map((item) => ({ | ||
87 | + ...item, | ||
88 | + url: `http://112.74.45.244:8100/api/show/image?fileKey=${item.fileKey}` | ||
89 | + })) | ||
90 | + info.value = data | ||
91 | + }) | ||
92 | +}) | ||
93 | + | ||
94 | +const tab = ref(0) | ||
95 | +const slide = ref(0) | ||
96 | +</script> | ||
97 | + | ||
98 | +<style lang="less" scoped></style> |
src/views/ProductList.vue
0 → 100644
1 | +++ a/src/views/ProductList.vue | ||
1 | +<template> | ||
2 | + <div class="tw-m-auto"> | ||
3 | + <CategoryList /> | ||
4 | + <v-container class=""> | ||
5 | + <v-item-group multiple> | ||
6 | + <v-row> | ||
7 | + <v-col v-for="(item, i) in productStore.list" :key="i" cols="12" lg="3" md="4" sm="6"> | ||
8 | + <v-hover v-slot="{ isHovering, props }" open-delay="200"> | ||
9 | + <v-card | ||
10 | + :elevation="isHovering ? 16 : 2" | ||
11 | + :class="{ 'on-hover': isHovering }" | ||
12 | + class="mx-auto" | ||
13 | + height="350" | ||
14 | + width="260" | ||
15 | + v-bind="props" | ||
16 | + :to="`/products/detail/${item.id}`" | ||
17 | + > | ||
18 | + <v-img :src="item.imageUrl"> | ||
19 | + <!-- <v-expand-transition> | ||
20 | + <div | ||
21 | + v-if="isHovering" | ||
22 | + class="d-flex transition-fast-in-fast-out bg-orange-darken-2 v-card--reveal tw-p-[12px] tw-text-justify" | ||
23 | + style="height: 100%" | ||
24 | + > | ||
25 | + 产品描述描述描述描述描述描述描述描述 | ||
26 | + </div> | ||
27 | + </v-expand-transition> --> | ||
28 | + </v-img> | ||
29 | + <v-card-text class="tw-text-left">{{ item.name }}</v-card-text> | ||
30 | + </v-card> | ||
31 | + </v-hover> | ||
32 | + </v-col> | ||
33 | + </v-row> | ||
34 | + </v-item-group> | ||
35 | + </v-container> | ||
36 | + </div> | ||
37 | +</template> | ||
38 | + | ||
39 | +<script setup lang="ts"> | ||
40 | +import { useProductListStore } from '@/stores/product_list' | ||
41 | +import { useCategoryStore } from '@/stores/category' | ||
42 | +import CategoryList from '@/components/CategoryList.vue' | ||
43 | +import { watchEffect } from 'vue' | ||
44 | + | ||
45 | +const productStore = useProductListStore() | ||
46 | +const categoryStore = useCategoryStore() | ||
47 | +watchEffect(() => { | ||
48 | + if (categoryStore.selectedSubCategory) { | ||
49 | + productStore.getList({ | ||
50 | + // productCategoryId: '78b86c6e917841cf9a292234f2805e24', | ||
51 | + productCategoryId: categoryStore.selectedSubCategory, | ||
52 | + pageNo: 1, | ||
53 | + pageSize: 20 | ||
54 | + }) | ||
55 | + } | ||
56 | +}) | ||
57 | +</script> | ||
58 | + | ||
59 | +<style lang="scss" scoped></style> |
tailwind.config.js
0 → 100644
1 | +++ a/tailwind.config.js | ||
1 | +/** @type {import('tailwindcss').Config} */ | ||
2 | +export default { | ||
3 | + corePlugins: { | ||
4 | + preflight: false, | ||
5 | + }, | ||
6 | + prefix: 'tw-', | ||
7 | + content: [ | ||
8 | + "./index.html", | ||
9 | + "./src/**/*.{vue,js,ts,jsx,tsx}", | ||
10 | + ], | ||
11 | + theme: { | ||
12 | + extend: { | ||
13 | + w: { | ||
14 | + screen: { | ||
15 | + 1200: '1200px' | ||
16 | + } | ||
17 | + } | ||
18 | + }, | ||
19 | + }, | ||
20 | + plugins: [], | ||
21 | +} | ||
0 | \ No newline at end of file | 22 | \ No newline at end of file |
tsconfig.app.json
0 → 100644
1 | +++ a/tsconfig.app.json | ||
1 | +{ | ||
2 | + "extends": "@vue/tsconfig/tsconfig.dom.json", | ||
3 | + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], | ||
4 | + "exclude": ["src/**/__tests__/*"], | ||
5 | + "compilerOptions": { | ||
6 | + "composite": true, | ||
7 | + "baseUrl": ".", | ||
8 | + "paths": { | ||
9 | + "@/*": ["./src/*"] | ||
10 | + } | ||
11 | + } | ||
12 | +} |
tsconfig.json
0 → 100644
tsconfig.node.json
0 → 100644
tsconfig.vitest.json
0 → 100644
vite.config.ts
0 → 100644
1 | +++ a/vite.config.ts | ||
1 | +import { fileURLToPath, URL } from 'node:url' | ||
2 | + | ||
3 | +import { defineConfig } from 'vite' | ||
4 | +import vue from '@vitejs/plugin-vue' | ||
5 | +import vueJsx from '@vitejs/plugin-vue-jsx' | ||
6 | + | ||
7 | +import vuetify from 'vite-plugin-vuetify' | ||
8 | + | ||
9 | +// https://vitejs.dev/config/ | ||
10 | +export default defineConfig({ | ||
11 | + plugins: [vue(), vueJsx(), vuetify()], | ||
12 | + resolve: { | ||
13 | + alias: { | ||
14 | + '@': fileURLToPath(new URL('./src', import.meta.url)) | ||
15 | + } | ||
16 | + }, | ||
17 | + server: { | ||
18 | + proxy: { | ||
19 | + '/shop': { | ||
20 | + target: 'http://39.108.227.113:8002' | ||
21 | + } | ||
22 | + } | ||
23 | + } | ||
24 | +}) |
vitest.config.ts
0 → 100644
1 | +++ a/vitest.config.ts | ||
1 | +import { fileURLToPath } from 'node:url' | ||
2 | +import { mergeConfig } from 'vite' | ||
3 | +import { configDefaults, defineConfig } from 'vitest/config' | ||
4 | +import viteConfig from './vite.config' | ||
5 | + | ||
6 | +export default mergeConfig( | ||
7 | + viteConfig, | ||
8 | + defineConfig({ | ||
9 | + test: { | ||
10 | + environment: 'jsdom', | ||
11 | + exclude: [...configDefaults.exclude, 'e2e/*'], | ||
12 | + root: fileURLToPath(new URL('./', import.meta.url)), | ||
13 | + transformMode: { | ||
14 | + web: [/\.[jt]sx$/], | ||
15 | + }, | ||
16 | + } | ||
17 | + }) | ||
18 | +) |