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 | 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 | +) | ... | ... |