Quellcode durchsuchen

Merge branch 'feature-user-page' into store

# Conflicts:
#	components.d.ts
#	package-lock.json
#	package.json
#	src/App.vue
#	src/api/store.ts
#	src/main.ts
#	src/router/index.ts
#	src/style.css
#	src/utils/request.ts
chillqi vor 2 Jahren
Ursprung
Commit
425a7888b3

+ 10 - 0
auto-imports.d.ts

@@ -0,0 +1,10 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// noinspection JSUnusedGlobalSymbols
+// Generated by unplugin-auto-import
+export {}
+declare global {
+  const ElMessage: typeof import('element-plus/es')['ElMessage']
+  const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
+}

+ 12 - 0
components.d.ts

@@ -10,6 +10,12 @@ declare module 'vue' {
     ElAside: typeof import('element-plus/es')['ElAside']
     ElAvatar: typeof import('element-plus/es')['ElAvatar']
     ElCard: typeof import('element-plus/es')['ElCard']
+    Dashboard: typeof import('./src/components/Dashboard.vue')['default']
+    DropFile: typeof import('./src/components/DropFile.vue')['default']
+    ElAside: typeof import('element-plus/es')['ElAside']
+    ElAvatar: typeof import('element-plus/es')['ElAvatar']
+    ElCard: typeof import('element-plus/es')['ElCard']
+    ElCol: typeof import('element-plus/es')['ElCol']
     ElContainer: typeof import('element-plus/es')['ElContainer']
     ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
     ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
@@ -18,6 +24,12 @@ declare module 'vue' {
     ElIcon: typeof import('element-plus/es')['ElIcon']
     ElMain: typeof import('element-plus/es')['ElMain']
     HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
+    ElRow: typeof import('element-plus/es')['ElRow']
+    Footer: typeof import('./src/components/Footer.vue')['default']
+    Header: typeof import('./src/components/Header.vue')['default']
+    HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
+    Login: typeof import('./src/components/Login.vue')['default']
+    Register: typeof import('./src/components/Register.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
   }

+ 1 - 2
index.html

@@ -2,9 +2,8 @@
 <html lang="en">
   <head>
     <meta charset="UTF-8" />
-    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>Vite + Vue + TS</title>
+    <title>Bluewhale Online</title>
   </head>
   <body>
     <div id="app"></div>

Datei-Diff unterdrückt, da er zu groß ist
+ 706 - 55
package-lock.json


+ 5 - 1
package.json

@@ -9,13 +9,17 @@
     "preview": "vite preview"
   },
   "dependencies": {
-    "element-plus": "^2.4.3",
+    "axios": "^1.6.2",
+    "element-plus": "^2.4.2",
+    "tailwindcss": "^3.3.5",
     "vue": "^3.3.8",
     "vue-router": "^4.2.5"
   },
   "devDependencies": {
     "@vitejs/plugin-vue": "^4.5.0",
     "typescript": "^5.2.2",
+    "unplugin-auto-import": "^0.16.7",
+    "unplugin-vue-components": "^0.25.2",
     "vite": "^5.0.0",
     "vue-tsc": "^1.8.22"
   }

+ 14 - 0
src/api/image.ts

@@ -0,0 +1,14 @@
+import {axios} from '../utils/request'
+import {IMAGE_MODULE} from './_prefix'
+
+// 上传文件
+export const uploadImage = payload => {
+    return axios.post(`${IMAGE_MODULE}/`, payload, {
+        headers:{
+            'Content-Type': "multipart/form-data;"
+        }
+    })
+        .then(res => {
+            return res;
+        })
+}

+ 10 - 0
src/api/product.ts

@@ -0,0 +1,10 @@
+import {axios} from '../utils/request'
+import {PRODUCT_MODULE} from './_prefix'
+
+// 创建商店
+export const createProduct = payload => {
+    return axios.post(`${PRODUCT_MODULE}/create`, payload)
+        .then(res => {
+            return res;
+        })
+}

+ 9 - 0
src/api/store.ts

@@ -1,6 +1,15 @@
 import {axios} from '../utils/request'
 import {STORE_MODULE} from './_prefix'
 
+// 创建商店
+export const createStore = payload => {
+    console.log(payload)
+    return axios.post(`${STORE_MODULE}/`, payload, {headers: {'Content-Type': 'application/json','token': localStorage.getItem('token')}})
+        .then(res => {
+            return res;
+        })
+}
+
 // 获取全部商店
 export const getAllStore = payload => {
     return axios.get(`${STORE_MODULE}/all`, payload)

+ 51 - 0
src/api/user.ts

@@ -0,0 +1,51 @@
+import {axios} from '../utils/request'
+import {USER_MODULE} from './_prefix'
+
+// 用户登录
+export const userLogin = payload => {
+    return axios.post(`${USER_MODULE}/login`,null, {params: payload})
+        .then(res => {
+            if(res.code == '400'){
+                throw new Error('密码错误!')
+            } else {
+                return res
+            }
+        })
+}
+
+
+export const userRegister = payload => {
+    return axios.post(`${USER_MODULE}/register`, payload,
+                    {headers: {'Content-Type': 'application/json'}})
+        .then(res => {
+            if(res.code == '400'){
+                throw new Error('注册失败!')
+            } else {
+                return res
+            }
+        })
+}
+
+
+export const userInfo = () => {
+    return axios.get(`${USER_MODULE}`)
+        .then(res => {
+            if(res.code == '400'){
+                throw new Error('个人信息获取失败!')
+            } else {
+                return res
+            }
+        })
+}
+
+
+export const userInfoUpdate = payload => {
+    return axios.post(`${USER_MODULE}`, payload, {headers: {'Content-Type': 'application/json'}})
+        .then(res => {
+            if (res.code == '400') {
+                throw new Error('个人信息更新失败!')
+            } else {
+                return res
+            }
+        })
+}

+ 155 - 0
src/assets/shopping-1s-1084px.svg

@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="240" height="240" preserveAspectRatio="xMidYMid" viewBox="0 0 240 240" style="display:block;background-color:rgb(241, 242, 243);animation-play-state:paused" ><g transform="scale(0.94)" style="animation-play-state:paused" ><defs style="animation-play-state:paused" ><g id="id-0.49142306393388036" style="animation-play-state:paused" ><g style="animation-play-state:paused" ><path d=" M35.214,89.83H23.625C16.1,89.83,10,83.729,10,76.204V24.751h54.41v6.637" data-stroke="c1" style="stroke-width:3px;stroke-miterlimit:10;stroke-linejoin:round;stroke-linecap:round;stroke:rgb(147, 219, 233);fill:none;animation-play-state:paused" ></path>
+<path d=" M23.514,34.708V23.862c0-7.562,6.13-13.691,13.691-13.691h0c7.562,0,13.691,6.13,13.691,13.691v10.846" data-stroke="c1" style="stroke-width:3px;stroke-miterlimit:10;stroke-linejoin:round;stroke-linecap:round;stroke:rgb(147, 219, 233);fill:none;animation-play-state:paused" ></path>
+<line x1="19.468" y1="74.805" x2="39.197" y2="74.805" data-stroke="c1" style="stroke-width:3px;stroke-miterlimit:10;stroke-linejoin:round;stroke-linecap:round;stroke:rgb(147, 219, 233);fill:none;animation-play-state:paused" ></line></g>
+<g style="animation-play-state:paused" ><polygon points=" 90,89.683 44.537,89.683 51.693,52.753 82.845,52.753 	" data-stroke="c2" style="stroke-width:3px;stroke-miterlimit:10;stroke-linejoin:round;stroke-linecap:round;stroke:rgb(104, 156, 197);fill:none;animation-play-state:paused" ></polygon>
+<path d=" M59.045,59.324V49.278c0-4.542,3.682-8.224,8.224-8.224h0c4.542,0,8.224,3.682,8.224,8.224v10.046" data-stroke="c2" style="stroke-width:3px;stroke-miterlimit:10;stroke-linejoin:round;stroke-linecap:round;stroke:rgb(104, 156, 197);fill:none;animation-play-state:paused" ></path></g>
+<metadata xmlns:d="https://loading.io/stock/" style="animation-play-state:paused" ><d:name style="animation-play-state:paused" >shopping</d:name>
+<d:tags style="animation-play-state:paused" >shopping,purchase,buy,consumption,department store,cart,checkout,bag,on sale</d:tags>
+<d:license style="animation-play-state:paused" >by</d:license>
+<d:slug style="animation-play-state:paused" >6jirrf</d:slug></metadata></g></defs>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<g data-idx="0" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-33.234997px;transform-origin-x:-43.696999px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><polygon fill="#B3B4B4" points="95.261,98.953 73.345,96.2 74.22,90.577 94.461,93.724 " data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></polygon></g></g>
+<g data-idx="0" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-33.234997px;transform-origin-x:212.303001px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><polygon fill="#B3B4B4" points="95.261,98.953 73.345,96.2 74.22,90.577 94.461,93.724 " data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></polygon></g></g>
+<g data-idx="1" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:85.47165px;transform-origin-x:-89.898998px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M38.105,224.49c-0.05,0-0.099,0-0.149-0.001c-2.943-0.039-5.694-1.221-7.748-3.33
+	c-4.239-4.352-4.147-11.342,0.205-15.581c2.108-2.054,4.884-3.157,7.833-3.124c2.943,0.039,5.694,1.222,7.748,3.33
+	c4.239,4.352,4.147,11.342-0.205,15.58C43.716,223.383,40.992,224.49,38.105,224.49z M38.098,207.494
+	c-1.567,0-3.044,0.6-4.169,1.695c-2.361,2.3-2.411,6.092-0.111,8.453c1.114,1.144,2.607,1.785,4.204,1.806
+	c1.609,0.014,3.106-0.581,4.25-1.695c2.361-2.3,2.411-6.092,0.112-8.453l0,0c-1.114-1.144-2.607-1.785-4.204-1.806
+	C38.152,207.494,38.125,207.494,38.098,207.494z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="1" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:85.47165px;transform-origin-x:166.101009px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M38.105,224.49c-0.05,0-0.099,0-0.149-0.001c-2.943-0.039-5.694-1.221-7.748-3.33
+	c-4.239-4.352-4.147-11.342,0.205-15.581c2.108-2.054,4.884-3.157,7.833-3.124c2.943,0.039,5.694,1.222,7.748,3.33
+	c4.239,4.352,4.147,11.342-0.205,15.58C43.716,223.383,40.992,224.49,38.105,224.49z M38.098,207.494
+	c-1.567,0-3.044,0.6-4.169,1.695c-2.361,2.3-2.411,6.092-0.111,8.453c1.114,1.144,2.607,1.785,4.204,1.806
+	c1.609,0.014,3.106-0.581,4.25-1.695c2.361-2.3,2.411-6.092,0.112-8.453l0,0c-1.114-1.144-2.607-1.785-4.204-1.806
+	C38.152,207.494,38.125,207.494,38.098,207.494z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="2" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-80.499468px;transform-origin-x:106.489845px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><ellipse transform="matrix(0.7249 -0.6888 0.6888 0.7249 31.7828 174.5914)" fill="#B3B4B4" cx="234.5" cy="47.5" rx="4.4" ry="4.4" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></ellipse></g></g>
+<g data-idx="2" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-80.499468px;transform-origin-x:362.489845px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><ellipse transform="matrix(0.7249 -0.6888 0.6888 0.7249 31.7828 174.5914)" fill="#B3B4B4" cx="234.5" cy="47.5" rx="4.4" ry="4.4" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></ellipse></g></g>
+<g data-idx="3" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:18.653026px;transform-origin-x:28.533323px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M156.299,159.176l-1.714-5.053c3.929-1.333,6.042-5.614,4.709-9.543c-0.645-1.903-1.994-3.442-3.796-4.331
+	c-1.803-0.89-3.844-1.024-5.747-0.378l-1.714-5.053c3.254-1.103,6.741-0.874,9.822,0.646c3.08,1.52,5.385,4.149,6.488,7.402
+	C166.625,149.582,163.015,156.898,156.299,159.176z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="3" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:18.653026px;transform-origin-x:284.533323px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M156.299,159.176l-1.714-5.053c3.929-1.333,6.042-5.614,4.709-9.543c-0.645-1.903-1.994-3.442-3.796-4.331
+	c-1.803-0.89-3.844-1.024-5.747-0.378l-1.714-5.053c3.254-1.103,6.741-0.874,9.822,0.646c3.08,1.52,5.385,4.149,6.488,7.402
+	C166.625,149.582,163.015,156.898,156.299,159.176z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="4" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:21.494942px;transform-origin-x:-25.505844px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><ellipse transform="matrix(0.7249 -0.6888 0.6888 0.7249 -74.7837 111.7244)" fill="#B3B4B4" cx="102.5" cy="149.5" rx="4.4" ry="4.4" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></ellipse></g></g>
+<g data-idx="4" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:21.494942px;transform-origin-x:230.494148px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><ellipse transform="matrix(0.7249 -0.6888 0.6888 0.7249 -74.7837 111.7244)" fill="#B3B4B4" cx="102.5" cy="149.5" rx="4.4" ry="4.4" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></ellipse></g></g>
+<g data-idx="5" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:116.691002px;transform-origin-x:114.171494px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><polygon fill="#B3B4B4" points="231.486,249.178 247.065,235.877 252.857,238.786 235.18,253.505 " data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></polygon></g></g>
+<g data-idx="5" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:116.691002px;transform-origin-x:370.171494px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><polygon fill="#B3B4B4" points="231.486,249.178 247.065,235.877 252.857,238.786 235.18,253.505 " data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></polygon></g></g>
+<g data-idx="6" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-96.931499px;transform-origin-x:-20.197002px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><polygon fill="#B3B4B4" points="115.367,21.954 103.961,43.014 100.239,36.891 110.431,19.123 " data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></polygon></g></g>
+<g data-idx="6" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-96.931499px;transform-origin-x:235.802998px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><polygon fill="#B3B4B4" points="115.367,21.954 103.961,43.014 100.239,36.891 110.431,19.123 " data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></polygon></g></g>
+<g data-idx="7" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-7.150312px;transform-origin-x:94.099995px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M222.104,130.57c-0.044,0-0.088,0-0.132-0.001c-2.596-0.034-5.024-1.077-6.835-2.937
+	c-3.74-3.84-3.659-10.006,0.181-13.746c1.86-1.812,4.308-2.785,6.91-2.756c2.596,0.034,5.024,1.078,6.835,2.938
+	c3.74,3.839,3.659,10.006-0.181,13.745C227.054,129.594,224.651,130.57,222.104,130.57z M222.099,115.576
+	c-1.382,0-2.686,0.53-3.678,1.496c-2.083,2.029-2.127,5.374-0.098,7.458c0.983,1.009,2.3,1.575,3.708,1.594
+	c1.42,0.013,2.74-0.512,3.749-1.495c2.083-2.029,2.127-5.374,0.098-7.457l0,0c-0.983-1.009-2.3-1.575-3.708-1.594
+	C222.146,115.576,222.122,115.576,222.099,115.576z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="7" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-7.150312px;transform-origin-x:350.10001px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M222.104,130.57c-0.044,0-0.088,0-0.132-0.001c-2.596-0.034-5.024-1.077-6.835-2.937
+	c-3.74-3.84-3.659-10.006,0.181-13.746c1.86-1.812,4.308-2.785,6.91-2.756c2.596,0.034,5.024,1.078,6.835,2.938
+	c3.74,3.839,3.659,10.006-0.181,13.745C227.054,129.594,224.651,130.57,222.104,130.57z M222.099,115.576
+	c-1.382,0-2.686,0.53-3.678,1.496c-2.083,2.029-2.127,5.374-0.098,7.458c0.983,1.009,2.3,1.575,3.708,1.594
+	c1.42,0.013,2.74-0.512,3.749-1.495c2.083-2.029,2.127-5.374,0.098-7.457l0,0c-0.983-1.009-2.3-1.575-3.708-1.594
+	C222.146,115.576,222.122,115.576,222.099,115.576z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="8" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-119.157701px;transform-origin-x:46.664726px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M184.509,10.022l-4.015,1.058c-0.822-3.122-4.032-4.993-7.154-4.17c-1.512,0.398-2.779,1.362-3.567,2.713
+	c-0.788,1.352-1.003,2.929-0.603,4.44l-4.015,1.058c-0.681-2.585-0.315-5.28,1.031-7.589c1.346-2.309,3.511-3.956,6.096-4.637
+	C177.619,1.489,183.103,4.687,184.509,10.022z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="8" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-119.157701px;transform-origin-x:302.664711px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M184.509,10.022l-4.015,1.058c-0.822-3.122-4.032-4.993-7.154-4.17c-1.512,0.398-2.779,1.362-3.567,2.713
+	c-0.788,1.352-1.003,2.929-0.603,4.44l-4.015,1.058c-0.681-2.585-0.315-5.28,1.031-7.589c1.346-2.309,3.511-3.956,6.096-4.637
+	C177.619,1.489,183.103,4.687,184.509,10.022z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="9" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:94.399567px;transform-origin-x:-6.924164px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><g transform="matrix(0.9912 0.1325 -0.1325 0.9912 30.542 -14.083) translate(121.067,222.398) scale(0.6786666666666666)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="9" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:94.399567px;transform-origin-x:249.075836px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><g transform="matrix(0.9912 0.1325 -0.1325 0.9912 30.542 -14.083) translate(121.067,222.398) scale(0.6786666666666666)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="10" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-52.825768px;transform-origin-x:33.256638px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><g transform="matrix(-0.0456 0.999 -0.999 -0.0456 243.7065 -82.5047) translate(161.268,75.17099999999999) scale(0.47706666666666664)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="10" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-52.825768px;transform-origin-x:289.256653px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><g transform="matrix(-0.0456 0.999 -0.999 -0.0456 243.7065 -82.5047) translate(161.268,75.17099999999999) scale(0.47706666666666664)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="11" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:65.082581px;transform-origin-x:87.308403px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><g transform="matrix(0.5905 -0.8071 0.8071 0.5905 -67.6584 252.8308) translate(215.293,193.0815) scale(0.5214666666666667)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="11" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:65.082581px;transform-origin-x:343.308388px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><g transform="matrix(0.5905 -0.8071 0.8071 0.5905 -67.6584 252.8308) translate(215.293,193.0815) scale(0.5214666666666667)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="12" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-89.695759px;transform-origin-x:-78.814707px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><g transform="matrix(-0.4749 -0.8801 0.8801 -0.4749 38.8277 99.7848) translate(49.185,38.308499999999995) scale(0.5980444444444444)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="12" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:-89.695759px;transform-origin-x:177.185293px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><g transform="matrix(-0.4749 -0.8801 0.8801 -0.4749 38.8277 99.7848) translate(49.185,38.308499999999995) scale(0.5980444444444444)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="13" data-dup="" dx="-128" dy="-128" style="transform-origin-z:0px;transform-origin-y:14.246841px;transform-origin-x:-89.567221px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,-128)" style="animation-play-state:paused" ><g transform="matrix(0.7381 -0.6747 0.6747 0.7381 -85.9069 63.1885) translate(38.434,142.243) scale(0.607688888888889)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="13" data-dup="" dx="128" dy="-128" style="transform-origin-z:0px;transform-origin-y:14.246841px;transform-origin-x:166.432779px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,-128)" style="animation-play-state:paused" ><g transform="matrix(0.7381 -0.6747 0.6747 0.7381 -85.9069 63.1885) translate(38.434,142.243) scale(0.607688888888889)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="0" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:222.764996px;transform-origin-x:-43.696999px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><polygon fill="#B3B4B4" points="95.261,98.953 73.345,96.2 74.22,90.577 94.461,93.724 " data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></polygon></g></g>
+<g data-idx="0" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:222.764996px;transform-origin-x:212.303001px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><polygon fill="#B3B4B4" points="95.261,98.953 73.345,96.2 74.22,90.577 94.461,93.724 " data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></polygon></g></g>
+<g data-idx="1" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:341.471665px;transform-origin-x:-89.898998px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M38.105,224.49c-0.05,0-0.099,0-0.149-0.001c-2.943-0.039-5.694-1.221-7.748-3.33
+	c-4.239-4.352-4.147-11.342,0.205-15.581c2.108-2.054,4.884-3.157,7.833-3.124c2.943,0.039,5.694,1.222,7.748,3.33
+	c4.239,4.352,4.147,11.342-0.205,15.58C43.716,223.383,40.992,224.49,38.105,224.49z M38.098,207.494
+	c-1.567,0-3.044,0.6-4.169,1.695c-2.361,2.3-2.411,6.092-0.111,8.453c1.114,1.144,2.607,1.785,4.204,1.806
+	c1.609,0.014,3.106-0.581,4.25-1.695c2.361-2.3,2.411-6.092,0.112-8.453l0,0c-1.114-1.144-2.607-1.785-4.204-1.806
+	C38.152,207.494,38.125,207.494,38.098,207.494z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="1" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:341.471665px;transform-origin-x:166.101009px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M38.105,224.49c-0.05,0-0.099,0-0.149-0.001c-2.943-0.039-5.694-1.221-7.748-3.33
+	c-4.239-4.352-4.147-11.342,0.205-15.581c2.108-2.054,4.884-3.157,7.833-3.124c2.943,0.039,5.694,1.222,7.748,3.33
+	c4.239,4.352,4.147,11.342-0.205,15.58C43.716,223.383,40.992,224.49,38.105,224.49z M38.098,207.494
+	c-1.567,0-3.044,0.6-4.169,1.695c-2.361,2.3-2.411,6.092-0.111,8.453c1.114,1.144,2.607,1.785,4.204,1.806
+	c1.609,0.014,3.106-0.581,4.25-1.695c2.361-2.3,2.411-6.092,0.112-8.453l0,0c-1.114-1.144-2.607-1.785-4.204-1.806
+	C38.152,207.494,38.125,207.494,38.098,207.494z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="2" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:175.500525px;transform-origin-x:106.489845px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><ellipse transform="matrix(0.7249 -0.6888 0.6888 0.7249 31.7828 174.5914)" fill="#B3B4B4" cx="234.5" cy="47.5" rx="4.4" ry="4.4" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></ellipse></g></g>
+<g data-idx="2" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:175.500525px;transform-origin-x:362.489845px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><ellipse transform="matrix(0.7249 -0.6888 0.6888 0.7249 31.7828 174.5914)" fill="#B3B4B4" cx="234.5" cy="47.5" rx="4.4" ry="4.4" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></ellipse></g></g>
+<g data-idx="3" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:274.653041px;transform-origin-x:28.533323px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M156.299,159.176l-1.714-5.053c3.929-1.333,6.042-5.614,4.709-9.543c-0.645-1.903-1.994-3.442-3.796-4.331
+	c-1.803-0.89-3.844-1.024-5.747-0.378l-1.714-5.053c3.254-1.103,6.741-0.874,9.822,0.646c3.08,1.52,5.385,4.149,6.488,7.402
+	C166.625,149.582,163.015,156.898,156.299,159.176z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="3" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:274.653041px;transform-origin-x:284.533323px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M156.299,159.176l-1.714-5.053c3.929-1.333,6.042-5.614,4.709-9.543c-0.645-1.903-1.994-3.442-3.796-4.331
+	c-1.803-0.89-3.844-1.024-5.747-0.378l-1.714-5.053c3.254-1.103,6.741-0.874,9.822,0.646c3.08,1.52,5.385,4.149,6.488,7.402
+	C166.625,149.582,163.015,156.898,156.299,159.176z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="4" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:277.494942px;transform-origin-x:-25.505844px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><ellipse transform="matrix(0.7249 -0.6888 0.6888 0.7249 -74.7837 111.7244)" fill="#B3B4B4" cx="102.5" cy="149.5" rx="4.4" ry="4.4" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></ellipse></g></g>
+<g data-idx="4" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:277.494942px;transform-origin-x:230.494148px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><ellipse transform="matrix(0.7249 -0.6888 0.6888 0.7249 -74.7837 111.7244)" fill="#B3B4B4" cx="102.5" cy="149.5" rx="4.4" ry="4.4" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></ellipse></g></g>
+<g data-idx="5" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:372.691017px;transform-origin-x:114.171494px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><polygon fill="#B3B4B4" points="231.486,249.178 247.065,235.877 252.857,238.786 235.18,253.505 " data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></polygon></g></g>
+<g data-idx="5" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:372.691017px;transform-origin-x:370.171494px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><polygon fill="#B3B4B4" points="231.486,249.178 247.065,235.877 252.857,238.786 235.18,253.505 " data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></polygon></g></g>
+<g data-idx="6" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:159.068501px;transform-origin-x:-20.197002px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><polygon fill="#B3B4B4" points="115.367,21.954 103.961,43.014 100.239,36.891 110.431,19.123 " data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></polygon></g></g>
+<g data-idx="6" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:159.068501px;transform-origin-x:235.802998px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><polygon fill="#B3B4B4" points="115.367,21.954 103.961,43.014 100.239,36.891 110.431,19.123 " data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></polygon></g></g>
+<g data-idx="7" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:248.84968px;transform-origin-x:94.099995px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M222.104,130.57c-0.044,0-0.088,0-0.132-0.001c-2.596-0.034-5.024-1.077-6.835-2.937
+	c-3.74-3.84-3.659-10.006,0.181-13.746c1.86-1.812,4.308-2.785,6.91-2.756c2.596,0.034,5.024,1.078,6.835,2.938
+	c3.74,3.839,3.659,10.006-0.181,13.745C227.054,129.594,224.651,130.57,222.104,130.57z M222.099,115.576
+	c-1.382,0-2.686,0.53-3.678,1.496c-2.083,2.029-2.127,5.374-0.098,7.458c0.983,1.009,2.3,1.575,3.708,1.594
+	c1.42,0.013,2.74-0.512,3.749-1.495c2.083-2.029,2.127-5.374,0.098-7.457l0,0c-0.983-1.009-2.3-1.575-3.708-1.594
+	C222.146,115.576,222.122,115.576,222.099,115.576z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="7" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:248.84968px;transform-origin-x:350.10001px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M222.104,130.57c-0.044,0-0.088,0-0.132-0.001c-2.596-0.034-5.024-1.077-6.835-2.937
+	c-3.74-3.84-3.659-10.006,0.181-13.746c1.86-1.812,4.308-2.785,6.91-2.756c2.596,0.034,5.024,1.078,6.835,2.938
+	c3.74,3.839,3.659,10.006-0.181,13.745C227.054,129.594,224.651,130.57,222.104,130.57z M222.099,115.576
+	c-1.382,0-2.686,0.53-3.678,1.496c-2.083,2.029-2.127,5.374-0.098,7.458c0.983,1.009,2.3,1.575,3.708,1.594
+	c1.42,0.013,2.74-0.512,3.749-1.495c2.083-2.029,2.127-5.374,0.098-7.457l0,0c-0.983-1.009-2.3-1.575-3.708-1.594
+	C222.146,115.576,222.122,115.576,222.099,115.576z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="8" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:136.842299px;transform-origin-x:46.664726px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M184.509,10.022l-4.015,1.058c-0.822-3.122-4.032-4.993-7.154-4.17c-1.512,0.398-2.779,1.362-3.567,2.713
+	c-0.788,1.352-1.003,2.929-0.603,4.44l-4.015,1.058c-0.681-2.585-0.315-5.28,1.031-7.589c1.346-2.309,3.511-3.956,6.096-4.637
+	C177.619,1.489,183.103,4.687,184.509,10.022z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="8" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:136.842299px;transform-origin-x:302.664711px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><path fill="#B3B4B4" d="M184.509,10.022l-4.015,1.058c-0.822-3.122-4.032-4.993-7.154-4.17c-1.512,0.398-2.779,1.362-3.567,2.713
+	c-0.788,1.352-1.003,2.929-0.603,4.44l-4.015,1.058c-0.681-2.585-0.315-5.28,1.031-7.589c1.346-2.309,3.511-3.956,6.096-4.637
+	C177.619,1.489,183.103,4.687,184.509,10.022z" data-fill="c3" style="fill:rgb(94, 111, 163);animation-play-state:paused" ></path></g></g>
+<g data-idx="9" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:350.399567px;transform-origin-x:-6.924164px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><g transform="matrix(0.9912 0.1325 -0.1325 0.9912 30.542 -14.083) translate(121.067,222.398) scale(0.6786666666666666)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="9" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:350.399567px;transform-origin-x:249.075836px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><g transform="matrix(0.9912 0.1325 -0.1325 0.9912 30.542 -14.083) translate(121.067,222.398) scale(0.6786666666666666)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="10" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:203.174225px;transform-origin-x:33.256638px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><g transform="matrix(-0.0456 0.999 -0.999 -0.0456 243.7065 -82.5047) translate(161.268,75.17099999999999) scale(0.47706666666666664)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="10" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:203.174225px;transform-origin-x:289.256653px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><g transform="matrix(-0.0456 0.999 -0.999 -0.0456 243.7065 -82.5047) translate(161.268,75.17099999999999) scale(0.47706666666666664)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="11" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:321.082565px;transform-origin-x:87.308403px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><g transform="matrix(0.5905 -0.8071 0.8071 0.5905 -67.6584 252.8308) translate(215.293,193.0815) scale(0.5214666666666667)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="11" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:321.082565px;transform-origin-x:343.308388px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><g transform="matrix(0.5905 -0.8071 0.8071 0.5905 -67.6584 252.8308) translate(215.293,193.0815) scale(0.5214666666666667)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="12" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:166.304249px;transform-origin-x:-78.814707px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><g transform="matrix(-0.4749 -0.8801 0.8801 -0.4749 38.8277 99.7848) translate(49.185,38.308499999999995) scale(0.5980444444444444)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="12" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:166.304249px;transform-origin-x:177.185293px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><g transform="matrix(-0.4749 -0.8801 0.8801 -0.4749 38.8277 99.7848) translate(49.185,38.308499999999995) scale(0.5980444444444444)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="13" data-dup="" dx="-128" dy="128" style="transform-origin-z:0px;transform-origin-y:270.246841px;transform-origin-x:-89.567221px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(-128,128)" style="animation-play-state:paused" ><g transform="matrix(0.7381 -0.6747 0.6747 0.7381 -85.9069 63.1885) translate(38.434,142.243) scale(0.607688888888889)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g>
+<g data-idx="13" data-dup="" dx="128" dy="128" style="transform-origin-z:0px;transform-origin-y:270.246841px;transform-origin-x:166.432779px;animation-timing-function:linear;animation-play-state:paused;animation-name:static-1c001d8d-81b8-440d-a02e-4cff11b8ea18;animation-iteration-count:infinite;animation-fill-mode:forwards;animation-duration:1s" ><g transform="translate(128,128)" style="animation-play-state:paused" ><g transform="matrix(0.7381 -0.6747 0.6747 0.7381 -85.9069 63.1885) translate(38.434,142.243) scale(0.607688888888889)" style="animation-play-state:paused" ><g data-icon="" transform="scale(0.9)" style="animation-play-state:paused" ><use xlink:href="#id-0.49142306393388036" x="-50" y="-50" style="animation-play-state:paused" ></use></g></g></g></g></g></svg>

+ 337 - 0
src/components/Dashboard.vue

@@ -0,0 +1,337 @@
+<script setup lang="ts">
+import {onBeforeMount, ref, computed} from 'vue';
+  import {userInfo, userInfoUpdate} from '../api/user.ts'
+import {router} from '../router'
+
+  const name = ref('')
+  const tel = ref('')
+  const address = ref('')
+
+  const password = ref('')
+  const confirmPassword = ref('')
+
+  const hasPasswordInput = computed(() => password.value != '');
+  const hasConfirmPasswordInput = computed(() => confirmPassword.value != '');
+  const isPasswordIdentical = computed(() => password.value == confirmPassword.value);
+
+  const isCustomer = ref('true')
+  const isStaff = ref('false')
+
+  onBeforeMount(() => {
+    userInfo()
+        .then(res => {
+          console.log(res)
+
+          name.value = res.result.name
+          tel.value = res.result.phone
+          address.value = res.result.address
+
+          isCustomer.value = (res.result.role == 'CUSTOMER')
+          isStaff.value = (res.result.role == 'STAFF')
+        })
+        .catch(e => {
+          //todo: 处理错误
+        })
+  })
+
+
+  function updateInfo() {
+
+    const payload = {
+      name: name.value,
+      password: null,
+      address: null
+    }
+
+    userInfoUpdate(payload)
+        .then(() => {
+          ElMessage({
+            customClass: 'customMessage',
+            type: 'success',
+            message: '更新成功!',
+          })
+        })
+        .catch(e => {
+          ElMessage({
+            customClass: 'customMessage',
+            type: 'error',
+            message: '更新失败!',
+          })
+        })
+        .finally(() =>{
+          userInfo().then(res => {
+            name.value = res.result.name
+          })
+        })
+  }
+
+  function updatePassword() {
+
+    const payload = {
+      name: null,
+      password: password.value,
+      address: null
+    }
+
+    userInfoUpdate(payload)
+        .then(() => {
+
+          ElMessageBox.alert(
+              `请重新登录`,
+              '修改成功',
+              {
+                customClass: "customDialog",
+                confirmButtonText: '跳转到登录',
+                type: "success",
+                showClose: false,
+                roundButton: true,
+                center: true
+
+              }).then(() => router.push({ path: "/login" }))
+
+        })
+        .catch(e => {
+          ElMessage({
+            customClass: 'customMessage',
+            type: 'error',
+            message: '更新失败!',
+          })
+        })
+        .finally(() => {
+            password.value = ''
+            confirmPassword.value = ''
+        })
+  }
+
+  function updateAddress() {
+
+    const payload = {
+      name: null,
+      password: null,
+      address: address.value
+    }
+
+    userInfoUpdate(payload)
+        .then(() => {
+          ElMessage({
+            customClass: 'customMessage',
+            type: 'success',
+            message: '更新成功!',
+          })
+        })
+        .catch(e => {
+          ElMessage({
+            customClass: 'customMessage',
+            type: 'error',
+            message: '更新失败!',
+          })
+        })
+        .finally(() =>{
+          userInfo().then(res => {
+            address.value = res.result.address
+          })
+        })
+  }
+
+</script>
+
+<template>
+
+
+  <div class="flex items-center justify-center p-8 h-full overflow-y-clip space-x-5">
+    <el-aside width="20%" class="h-full bg-gray-100  rounded-lg flex flex-col items-center justify-between">
+
+      <div class="py-6 h-1/2 w-full flex flex-col space-y-6 items-center justify-center border-b-2 border-gray-200">
+        <el-avatar :size="150" class="block">
+          <span class="text-3xl"> 头像 </span>
+        </el-avatar>
+
+        <label v-if="isCustomer"
+            class="w-2/5 px-3 py-2 text-center bg-primary-400 rounded-3xl text-white">顾客</label>
+        <label v-else-if="isStaff"
+               class="w-3/5 px-3 py-2 text-center bg-amber-500 rounded-3xl text-white">工作人员</label>
+
+      </div>
+
+
+      <div class="bg-gray-50 px-3 py-4 overflow-y-scroll h-full w-full flex flex-col items-center justify-start shadow-md space-y-5">
+
+          <ul class="space-y-3 font-medium">
+            <li>
+              <router-link to="" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group">
+                <svg class="flex-shrink-0 w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
+                  <path d="m17.418 3.623-.018-.008a6.713 6.713 0 0 0-2.4-.569V2h1a1 1 0 1 0 0-2h-2a1 1 0 0 0-1 1v2H9.89A6.977 6.977 0 0 1 12 8v5h-2V8A5 5 0 1 0 0 8v6a1 1 0 0 0 1 1h8v4a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1v-4h6a1 1 0 0 0 1-1V8a5 5 0 0 0-2.582-4.377ZM6 12H4a1 1 0 0 1 0-2h2a1 1 0 0 1 0 2Z"/>
+                </svg>
+                <span class="flex-1 ms-3 whitespace-nowrap">私聊信箱</span>
+<!--                <span class="inline-flex items-center justify-center w-3 h-3 p-3 ms-3 text-sm font-medium text-blue-800 bg-blue-100 rounded-full dark:bg-blue-900 dark:text-blue-300">-->
+<!--                  3-->
+<!--                </span>-->
+              </router-link>
+            </li>
+
+
+            <li>
+              <router-link to="" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group">
+                <svg class="flex-shrink-0 w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 20">
+                  <path d="M17 5.923A1 1 0 0 0 16 5h-3V4a4 4 0 1 0-8 0v1H2a1 1 0 0 0-1 .923L.086 17.846A2 2 0 0 0 2.08 20h13.84a2 2 0 0 0 1.994-2.153L17 5.923ZM7 9a1 1 0 0 1-2 0V7h2v2Zm0-5a2 2 0 1 1 4 0v1H7V4Zm6 5a1 1 0 1 1-2 0V7h2v2Z"/>
+                </svg>
+                <span class="flex-1 ms-3 whitespace-nowrap">已收藏商品</span>
+              </router-link>
+            </li>
+
+            <li>
+              <router-link to="" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group">
+                <svg class="flex-shrink-0 w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 16">
+                  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 8h11m0 0L8 4m4 4-4 4m4-11h3a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-3"/>
+                </svg>
+                <span class="flex-1 ms-3 whitespace-nowrap">关注店铺</span>
+              </router-link>
+            </li>
+
+          </ul>
+
+
+          <ul class="pt-4 space-y-3 font-medium border-t border-gray-200">
+
+
+            <li>
+              <router-link to="" class="flex items-center p-2 text-gray-900 transition duration-75 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-white group">
+                <svg class="flex-shrink-0 w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 20">
+                  <path d="M16 14V2a2 2 0 0 0-2-2H2a2 2 0 0 0-2 2v15a3 3 0 0 0 3 3h12a1 1 0 0 0 0-2h-1v-2a2 2 0 0 0 2-2ZM4 2h2v12H4V2Zm8 16H3a1 1 0 0 1 0-2h9v2Z"/>
+                </svg>
+                <span class="ms-3">使用帮助</span>
+              </router-link>
+            </li>
+
+            <li>
+              <router-link to="" class="flex items-center p-2 text-gray-900 transition duration-75 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-white group">
+                <svg class="flex-shrink-0 w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 21 21">
+                  <path d="m5.4 2.736 3.429 3.429A5.046 5.046 0 0 1 10.134 6c.356.01.71.06 1.056.147l3.41-3.412c.136-.133.287-.248.45-.344A9.889 9.889 0 0 0 10.269 1c-1.87-.041-3.713.44-5.322 1.392a2.3 2.3 0 0 1 .454.344Zm11.45 1.54-.126-.127a.5.5 0 0 0-.706 0l-2.932 2.932c.029.023.049.054.078.077.236.194.454.41.65.645.034.038.078.067.11.107l2.927-2.927a.5.5 0 0 0 0-.707Zm-2.931 9.81c-.024.03-.057.052-.081.082a4.963 4.963 0 0 1-.633.639c-.041.036-.072.083-.115.117l2.927 2.927a.5.5 0 0 0 .707 0l.127-.127a.5.5 0 0 0 0-.707l-2.932-2.931Zm-1.442-4.763a3.036 3.036 0 0 0-1.383-1.1l-.012-.007a2.955 2.955 0 0 0-1-.213H10a2.964 2.964 0 0 0-2.122.893c-.285.29-.509.634-.657 1.013l-.01.016a2.96 2.96 0 0 0-.21 1 2.99 2.99 0 0 0 .489 1.716c.009.014.022.026.032.04a3.04 3.04 0 0 0 1.384 1.1l.012.007c.318.129.657.2 1 .213.392.015.784-.05 1.15-.192.012-.005.02-.013.033-.018a3.011 3.011 0 0 0 1.676-1.7v-.007a2.89 2.89 0 0 0 0-2.207 2.868 2.868 0 0 0-.27-.515c-.007-.012-.02-.025-.03-.039Zm6.137-3.373a2.53 2.53 0 0 1-.35.447L14.84 9.823c.112.428.166.869.16 1.311-.01.356-.06.709-.147 1.054l3.413 3.412c.132.134.249.283.347.444A9.88 9.88 0 0 0 20 11.269a9.912 9.912 0 0 0-1.386-5.319ZM14.6 19.264l-3.421-3.421c-.385.1-.781.152-1.18.157h-.134c-.356-.01-.71-.06-1.056-.147l-3.41 3.412a2.503 2.503 0 0 1-.443.347A9.884 9.884 0 0 0 9.732 21H10a9.9 9.9 0 0 0 5.044-1.388 2.519 2.519 0 0 1-.444-.348ZM1.735 15.6l3.426-3.426a4.608 4.608 0 0 1-.013-2.367L1.735 6.4a2.507 2.507 0 0 1-.35-.447 9.889 9.889 0 0 0 0 10.1c.1-.164.217-.316.35-.453Zm5.101-.758a4.957 4.957 0 0 1-.651-.645c-.033-.038-.077-.067-.11-.107L3.15 17.017a.5.5 0 0 0 0 .707l.127.127a.5.5 0 0 0 .706 0l2.932-2.933c-.03-.018-.05-.053-.078-.076ZM6.08 7.914c.03-.037.07-.063.1-.1.183-.22.384-.423.6-.609.047-.04.082-.092.129-.13L3.983 4.149a.5.5 0 0 0-.707 0l-.127.127a.5.5 0 0 0 0 .707L6.08 7.914Z"/>
+                </svg>
+                <span class="ms-3">建议与反馈</span>
+              </router-link>
+            </li>
+
+            <li>
+              <router-link to="" class="flex items-center p-2 text-gray-900 transition duration-75 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-white group">
+                <svg class="flex-shrink-0 w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
+                  <path d="M18 0H6a2 2 0 0 0-2 2h14v12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2Z"/>
+                  <path d="M14 4H2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2ZM2 16v-6h12v6H2Z"/>
+                </svg>
+                <span class="ms-3">关于我们</span>
+              </router-link>
+            </li>
+
+          </ul>
+
+      </div>
+
+    </el-aside>
+
+
+    <div  class="flex flex-col h-full w-full  rounded-lg space-y-4 overflow-y-scroll">
+
+      <div class="h-auto bg-gray-50 rounded-lg">
+
+        <div class="flex flex-row justify-between px-3 py-2 rounded-t-lg bg-gray-100 border-b-2 border-gray-200">
+          <span class="ml-1 text-2xl font-semibold text-center">个人信息</span>
+          <button type="submit" @click="updateInfo"
+                  class="inline text-white bg-primary-600 hover:bg-primary-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-2xl text-xs px-4 py-2 text-center">更新</button>
+        </div>
+
+        <form class="m-3">
+          <div class="grid gap-6 mb-6 grid-cols-2 mx-4">
+            <div>
+              <label for="name" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">昵称</label>
+              <input type="text" id="name" v-model="name"
+                     class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
+            </div>
+
+            <div>
+              <label for="phone" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">手机号</label>
+              <input type="tel" id="phone" v-model="tel" disabled
+                     class="disabled:bg-gray-100 disabled:text-gray-400 bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
+            </div>
+
+<!--            <div>-->
+<!--              <label for="email" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">邮箱</label>-->
+<!--              <input type="email" id="email" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">-->
+<!--            </div>-->
+          </div>
+        </form>
+      </div>
+
+      <div class="h-auto bg-gray-50 rounded-lg">
+
+        <div class="flex flex-row justify-between px-3 py-2 rounded-t-lg bg-gray-100 border-b-2 border-gray-200">
+          <span class="ml-1 text-2xl font-semibold text-center">密码</span>
+          <button type="submit" @click="updatePassword" :disabled="!hasConfirmPasswordInput || !isPasswordIdentical"
+                  class="inline text-white bg-primary-600 hover:bg-primary-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-3xl text-xs px-4 py-2 text-center disabled:bg-primary-100">更新</button>
+        </div>
+
+        <form class="m-3">
+          <div class="grid grid-cols-2 gap-10 mb-6 mx-4">
+            <div>
+              <label for="password" class="block mb-2 text-sm font-medium text-gray-900">密码</label>
+              <input type="password" id="password" v-model="password"
+                     class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" placeholder="•••••••••" required>
+            </div>
+            <div>
+              <label v-if="!hasConfirmPasswordInput" for="confirm_password" class="block mb-2 text-sm font-medium text-gray-900">确认密码</label>
+              <label v-else-if="!isPasswordIdentical" for="confirm_password" class="block mb-2 text-sm font-medium text-red-600 ">密码不一致</label>
+              <label v-else for="confirm_password" class="block mb-2 text-sm font-medium text-gray-900">确认密码</label>
+
+              <input type="password" id="confirm_password" v-model="confirmPassword"
+                     :class="{'focus:outline-red-600': hasConfirmPasswordInput && !isPasswordIdentical}"
+                     class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" placeholder="•••••••••" required>
+            </div>
+          </div>
+        </form>
+
+      </div>
+
+      <div class="h-full bg-gray-50 rounded-lg flex flex-col">
+
+        <div class="flex flex-row justify-between px-3 py-2 rounded-t-lg bg-gray-100 border-b-2 border-gray-200">
+          <span class="ml-1 text-2xl font-semibold text-center">地址信息</span>
+          <button @click="updateAddress"
+                  class="block text-white bg-primary-600 hover:bg-primary-800 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-3xl text-xs px-4 py-2 text-center" type="button">
+            更新地址
+          </button>
+        </div>
+
+        <div class="p-5 flex flex-col h-full">
+<!--          <label for="address" class="block mt-2 mb-2 text-sm font-medium text-gray-900">默认地址</label>-->
+          <textarea id="address"
+                    v-model="address"
+                    class="overflow-auto block p-3 h-full w-full text-xl text-gray-900 bg-white rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500" placeholder="中华门"></textarea>
+
+        </div>
+
+
+
+      </div>
+
+
+    </div>
+  </div>
+
+
+</template>
+
+<style>
+
+.customMessage {
+  @apply rounded-2xl text-3xl font-bold
+}
+
+.customDialog {
+  @apply w-1/4 space-y-5 text-4xl font-semibold rounded-2xl pb-5 pt-2
+}
+
+</style>
+
+
+<style scoped>
+
+</style>

+ 91 - 0
src/components/DropFile.vue

@@ -0,0 +1,91 @@
+<script setup lang="ts">
+import { ref } from 'vue'
+
+const isDragging = ref(false)
+const files = ref([])
+const fileInputRef = ref(null)
+
+function onChange() {
+  files.value = [...fileInputRef.value.files]
+}
+
+function dragover(e) {
+  e.preventDefault()
+  isDragging.value = true
+}
+
+function dragleave() {
+  isDragging.value = false
+}
+
+function drop(e) {
+  e.preventDefault()
+  fileInputRef.value.files = e.dataTransfer.files
+  onChange()
+  isDragging.value = false
+}
+
+function remove(i) {
+  files.value.splice(i, 1);
+}
+
+function generateURL(file) {
+  let fileSrc = URL.createObjectURL(file);
+  setTimeout(() => {
+    URL.revokeObjectURL(fileSrc);
+  }, 1000);
+  return fileSrc;
+}
+</script>
+
+<template>
+  <div class="main">
+    <div
+        class="dropzone-container"
+        @dragover="dragover"
+        @dragleave="dragleave"
+        @drop="drop"
+    >
+      <input
+          type="file"
+          multiple
+          name="file"
+          id="fileInput"
+          class="hidden-input"
+          @change="onChange"
+          ref="fileInputRef"
+          accept=".pdf,.jpg,.jpeg,.png"
+      />
+
+      <label for="fileInput" class="file-label">
+        <div v-if="isDragging">释放以将文件放到此处。</div>
+        <div v-else>将文件拖到此处或单击此处上传。</div>
+      </label>
+
+      <div class="preview-container mt-4" v-if="files.length">
+        <div v-for="file in files" :key="file.name" class="preview-card">
+          <div>
+            <img class="preview-img" :src="generateURL(file)" />
+            <p>
+              {{ file.name }}
+            </p>
+          </div>
+          <div>
+            <button
+                class="ml-2"
+                type="button"
+                @click="remove(files.indexOf(file))"
+                title="Remove file"
+            >
+              <b>×</b>
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+
+</style>

+ 15 - 0
src/components/Footer.vue

@@ -0,0 +1,15 @@
+<script setup lang="ts">
+
+</script>
+
+<template>
+  <div class="bg-gray-50 min-w-full bottom-0 shadow">
+    <span class="text-sm px-5">
+      蓝鲸购物系统
+    </span>
+  </div>
+</template>
+
+<style scoped>
+
+</style>

+ 103 - 0
src/components/Header.vue

@@ -0,0 +1,103 @@
+<script setup lang="ts">
+import {CirclePlus, UserFilled, SwitchButton} from "@element-plus/icons-vue";
+import {ref} from 'vue';
+import {router} from '../router'
+
+const searchContent = ref('')
+
+function logout() {
+  ElMessageBox.confirm(
+      '是否要退出登录?',
+      '提示',
+      {
+        customClass: "customDialog",
+        confirmButtonText: '是',
+        cancelButtonText: '否',
+        type: "warning",
+        showClose: false,
+        roundButton: true,
+        center: true
+      }
+  ).then(() => {
+    localStorage.setItem('token', '')
+    router.push({path: "/login"})
+  }).catch(() => {
+    // do nothing
+  })
+}
+
+
+function search() {
+  //todo: search
+
+
+}
+
+</script>
+
+<template>
+  <div class="bg-primary-500 min-w-full top-0 shadow-md flex-shrink-0 rounded-b-xl">
+
+    <div class="mx-4 p-4">
+
+      <div class="flex items-center justify-between space-x-4">
+
+        <div class="flex items-center">
+          <router-link to="/home" v-slot="{navigate}">
+            <h1 @click="navigate" class="text-xl text-white"> 蓝鲸在线购物 </h1>
+          </router-link>
+        </div>
+
+        <div class="w-3/4">
+          <form>
+            <div class="relative">
+
+              <!--网上抄的logo-->
+              <div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
+                <svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
+                  <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
+                </svg>
+              </div>
+
+              <input type="search" id="search"
+                     v-model="searchContent"
+                     class="block w-full p-4 ps-10 text-gray-900 border border-gray-300 rounded-3xl bg-gray-50 focus:ring-blue-500 focus:border-blue-500" placeholder="国货好物" required>
+              <button type="submit" @click="search"
+                      class="text-white absolute end-5 bottom-2.5 bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-3xl text-sm px-4 py-2">搜索商品</button>
+            </div>
+          </form>
+        </div>
+
+
+
+        <div class="flex items-center space-x-4 mx-3">
+          <router-link to="/manager/createStore" v-slot="{navigate}">
+            <el-icon @click="navigate" :size="30" color="white" ><CirclePlus /></el-icon>
+          </router-link>
+
+          <router-link to="/dashboard" v-slot="{navigate}">
+            <el-icon @click="navigate" :size="30" color="white" ><UserFilled /></el-icon>
+          </router-link>
+
+          <a @click="logout" class="hover:cursor-pointer">
+            <el-icon :size="30" color="white" ><SwitchButton /></el-icon>
+          </a>
+
+
+        </div>
+
+      </div>
+    </div>
+  </div>
+</template>
+
+<style>
+
+.customDialog {
+  @apply w-1/4 space-y-5 text-4xl font-semibold rounded-2xl pb-5 pt-2
+}
+
+</style>
+
+<style scoped>
+</style>

+ 1 - 1
src/components/HelloWorld.vue

@@ -3,7 +3,7 @@ import { ref } from 'vue'
 
 defineProps<{ msg: string }>()
 
-const count = ref(0)
+const count = ref(10)
 </script>
 
 <template>

+ 157 - 0
src/components/Login.vue

@@ -0,0 +1,157 @@
+<script setup lang="ts">
+  import {ElForm, ElFormItem } from "element-plus";
+  import { ref, computed } from 'vue';
+  import {router} from '../router'
+  import {userLogin} from "../api/user.ts";
+
+
+  // 输入框值
+  const tel = ref('')
+  const password = ref('')
+
+  // 用于前端阻拦不合法输入
+  // const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+  const hasTelInput = computed(() => tel.value != '');
+  const hasPasswordInput = computed(() => password.value != '');
+
+  const chinaMobileRegex = /^1(3[0-9]|4[579]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[189])\d{8}$/;
+  const telLegal = computed(() => chinaMobileRegex.test(tel.value));
+
+  // 密码有就行
+  const passwordLegal = hasPasswordInput;
+
+  const loginDisabled = computed(() => {
+    return !(telLegal.value && passwordLegal.value);
+  });
+
+  async function handleLogin() {
+    const payload = {
+      phone: tel.value,
+      password: password.value,
+    }
+
+    userLogin(payload)
+        .then(res => {
+          console.log(res)
+          // // token塞入饼干中
+          // const token = res.result;
+          // document.cookie = `token=${token}`
+
+          // token塞入localStorage中
+          const token = res.result
+          localStorage.setItem('token', token)
+
+          // 路由跳转
+          router.push({ path: "/home" })
+        })
+        .catch(e => {
+          console.log(e.message)
+          ElMessageBox.alert(
+              e.message,
+              '错误提示',
+              {
+                customClass: "customDialog",
+                confirmButtonText: '好的',
+                type: "error",
+                showClose: false,
+                roundButton: true,
+                center: true
+              }
+          ).then(() => {
+            // 清空用户密码
+            password.value = ''
+          })
+        })
+  }
+
+</script>
+
+
+<template>
+
+  <div  class="bgimage h-full w-full flex flex-col items-center justify-center px-6 py-6 space-y-10">
+
+    <span class="flex items-center text-2xl font-semibold text-gray-900">
+      Blue Whale Shopping Online
+    </span>
+
+    <el-card class="w-full bg-white rounded-lg shadow-md max-w-lg">
+
+      <div class="p-6 space-y-4 h-full overflow-scroll">
+
+        <h1 class="text-2xl font-bold leading-tight tracking-tight text-gray-900">
+          登入您的账户
+        </h1>
+
+        <el-form class="space-y-6">
+
+          <el-form-item>
+            <label v-if="!hasTelInput" for="tel" class="inline-block mb-2 text-sm font-medium text-gray-900">注册手机号</label>
+            <label v-else-if="!telLegal" for="tel" class="inline-block mb-2 font-medium text-red-500 text-sm">手机号不合法</label>
+            <label v-else for="tel" class="inline-block mb-2 text-sm font-medium text-gray-900">注册手机号</label>
+
+            <input id="tel" type="tel" v-model="tel"
+                   required
+                   class="bg-gray-50 border border-gray-300 text-gray-900 text-sm focus:outline-primary-600 rounded-lg block w-full p-2.5"
+                   :class="{'focus:outline-red-600':hasTelInput && !telLegal}"
+                   placeholder="请输入手机号">
+          </el-form-item>
+
+          <el-form-item>
+<!--            因为密码目前设定是有就行,所以也没有做条件渲染-->
+            <label for="password" class="block mb-2 text-sm font-medium text-gray-900">账户密码</label>
+            <input id="password" type="password" v-model="password"
+                   required
+                   placeholder="••••••••"
+                   class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" >
+          </el-form-item>
+
+<!--          <el-form-item class="pb-2">-->
+<!--            <a href="#" class="text-sm font-medium text-primary-600 hover:underline">-->
+<!--              忘记密码?-->
+<!--            </a>-->
+<!--          </el-form-item>-->
+
+          <el-form-item>
+              <button @click.prevent="handleLogin"
+                      :disabled="loginDisabled"
+                      class="block my-3 w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-3 text-center disabled:bg-primary-100">
+                登入
+              </button>
+          </el-form-item>
+
+          <p class="text-sm font-light text-gray-5000">
+            还没有账户?
+            <router-link to="/register" v-slot="{navigate}">
+              <span @click="navigate" class="font-medium text-primary-600 hover:underline">
+                点击注册
+              </span>
+            </router-link> 开启国货购物新体验
+          </p>
+
+        </el-form>
+
+      </div>
+
+
+    </el-card>
+  </div>
+</template>
+
+<style>
+
+.customDialog {
+  @apply w-1/4 space-y-5 text-4xl font-semibold rounded-2xl pb-5 pt-2
+}
+
+</style>
+
+
+<style scoped>
+
+.bgimage {
+  background-image: url("../assets/shopping-1s-1084px.svg");
+}
+
+
+</style>

+ 250 - 0
src/components/Register.vue

@@ -0,0 +1,250 @@
+<script setup lang="ts">
+  import { ref, computed } from 'vue';
+  import {router} from '../router'
+  import {userRegister} from "../api/user.ts";
+
+
+  const name = ref('')
+  const identity = ref('')
+  const tel = ref('')
+  const address = ref('')
+  const password = ref('')
+  const confirmPassword = ref('')
+
+
+  // const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+  const chinaMobileRegex = /^1(3[0-9]|4[579]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[189])\d{8}$/;
+
+  const hasTelInput = computed(() => tel.value != '');
+  const hasPasswordInput = computed(() => password.value != '');
+  const hasConfirmPasswordInput = computed(() => confirmPassword.value != '');
+  const hasAddressInput = computed(() => address.value != '');
+  const hasIdentityChosen = computed(() => identity.value != '')
+
+
+  const telLegal = computed(() => chinaMobileRegex.test(tel.value));
+  const isPasswordIdentical = computed(() => password.value == confirmPassword.value);
+
+
+  const registerDisabled = computed(() => {
+    return !(telLegal.value && hasPasswordInput.value && isPasswordIdentical.value && hasAddressInput.value && hasIdentityChosen.value)
+  });
+
+
+  async function handleRegister() {
+    const payload = {
+      role: identity.value,
+      name: name.value,
+      phone: tel.value,
+      password: password.value,
+      address: address.value,
+      createTime: new Date().getTime()
+    }
+
+    userRegister(payload)
+        .then(res => {
+          ElMessageBox.alert(
+              `${name.value},感谢您的注册!`,
+              '注册成功',
+              {
+                customClass: "customDialog",
+                confirmButtonText: '跳转到登录',
+                type: "success",
+                showClose: false,
+                roundButton: true,
+                center: true
+              }
+          ).then(() => {
+            // 跳转去登录
+            router.push({ path: "/login" })
+          })
+        })
+        .catch(e => {
+          console.log(e)
+          ElMessageBox.alert(
+              e.message,
+              '错误提示',
+              {
+                customClass: "customDialog",
+                confirmButtonText: '好的',
+                type: "error",
+                showClose: false,
+                roundButton: true,
+                center: true
+              }
+          )
+        })
+  }
+
+
+</script>
+
+<template>
+
+  <div class="bgimage h-full w-full flex flex-col items-center justify-center px-6 py-6 space-y-10">
+
+
+    <span class="flex items-center text-2xl font-semibold text-gray-900">
+      Blue Whale Shopping Online
+    </span>
+
+    <el-card class="w-3/5 min-w-max max-w-xl bg-white rounded-lg shadow-md overflow-y-scroll">
+
+      <div class="p-6 space-y-4 overflow-auto min-h-max">
+
+        <h1 class="text-2xl font-bold leading-tight tracking-tight text-gray-900">
+          创建一个新的账户
+        </h1>
+
+        <el-form class="space-y-6 h-full">
+
+          <el-row class="flex flex-row justify-between">
+
+            <el-col :span="15">
+              <el-form-item class="mb-0">
+                <label for="name" class="inline-block mb-2 text-sm font-medium text-gray-900">昵称</label>
+                <input id="name"
+                       v-model="name"
+                       class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5"
+                       placeholder="小明">
+              </el-form-item>
+            </el-col>
+
+
+            <el-col :span="8">
+              <el-form-item class="mb-0">
+                <label for="identity" class="block mb-2 text-sm font-medium text-gray-900">身份</label>
+                <select id="identity"
+                        v-model="identity"
+                        class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
+                  <option disabled value="" selected>请选择</option>
+                  <option value="CUSTOMER">顾客</option>
+                  <option value="STAFF">商家</option>
+                </select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+
+
+          <el-row class="flex flex-row justify-between">
+            <el-col :span="8">
+              <el-form-item>
+                <!--            <label for="tel" class="block mb-2 text-sm font-medium text-gray-900">手机号</label>-->
+
+                <label v-if="!hasTelInput" for="tel" class="inline-block mb-2 text-sm font-medium text-gray-900">
+                  注册手机号
+                </label>
+                <label v-else-if="!telLegal" for="tel"
+                       class="inline-block mb-2 font-medium text-red-500 text-sm">
+                  手机号不合法
+                </label>
+                <label v-else for="tel" class="inline-block mb-2 text-sm font-medium text-gray-900">
+                  注册手机号
+                </label>
+
+                <input type="tel" id="tel"
+                       v-model="tel"
+                       class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5"
+                       :class="{'focus:outline-red-600':hasTelInput && !telLegal}"
+                       placeholder="请输入手机号">
+              </el-form-item>
+            </el-col>
+
+            <el-col :span="15">
+              <el-form-item>
+                <!--            <label for="tel" class="block mb-2 text-sm font-medium text-gray-900">手机号</label>-->
+
+                <label for="address" class="inline-block mb-2 text-sm font-medium text-gray-900">
+                  地址
+                </label>
+
+                <input id="address"
+                       v-model="address"
+                       class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5"
+                       placeholder="请输入地址">
+              </el-form-item>
+            </el-col>
+
+          </el-row>
+
+
+
+
+          <el-form-item>
+            <label for="password" class="block mb-2 text-sm font-medium text-gray-900">密码</label>
+            <input type="password" id="password"
+                   v-model="password"
+                   class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5"
+                   placeholder="••••••••">
+          </el-form-item>
+
+          <el-form-item>
+            <label v-if="!hasConfirmPasswordInput" class="inline-block mb-2 text-sm font-medium text-gray-900">确认密码</label>
+            <label v-else-if="!isPasswordIdentical" class="inline-block mb-2 text-sm font-medium text-red-600">密码不一致</label>
+            <label v-else class="inline-block mb-2 text-sm font-medium text-gray-900">确认密码</label>
+
+            <input type="password" id="confirm-password"
+                   v-model="confirmPassword"
+                   class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5"
+                   :class="{'focus:outline-red-600': hasConfirmPasswordInput && !isPasswordIdentical}"
+                   placeholder="••••••••">
+          </el-form-item>
+
+<!--          <el-form-item class="flex items-start">-->
+
+<!--            <div class="flex items-center h-5">-->
+<!--              <input id="terms" aria-describedby="terms" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-primary-300"-->
+<!--                     required>-->
+<!--            </div>-->
+
+<!--            <div class="ml-3 text-sm">-->
+<!--              <label for="terms" class="font-light text-gray-500">-->
+<!--                我接受-->
+<!--                <a class="font-medium text-primary-600 hover:underline dark:text-primary-500" href="#">恶魔的条款</a>-->
+<!--              </label>-->
+<!--            </div>-->
+
+<!--          </el-form-item>-->
+
+
+          <el-form-item>
+            <button @click.prevent="handleRegister"
+                    :disabled="registerDisabled"
+                    class="my-3 w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center disabled:bg-primary-100">
+              创建账户
+            </button>
+          </el-form-item>
+
+
+          <p class="text-sm font-light text-gray-500 ">
+            已经有账户了?
+            <router-link to="/login" v-slot="{navigate}">
+              <a @click="navigate" class="font-medium text-primary-600 hover:underline">
+                点击登陆
+              </a>
+            </router-link>
+          </p>
+
+        </el-form>
+      </div>
+
+    </el-card>
+
+  </div>
+</template>
+
+<style>
+
+.customDialog {
+  @apply w-1/4 space-y-5 text-4xl font-semibold rounded-2xl pb-5 pt-2
+}
+
+</style>
+
+
+<style scoped>
+.bgimage {
+  background-image: url("../assets/shopping-1s-1084px.svg");
+}
+
+</style>

+ 7 - 7
src/main.ts

@@ -1,13 +1,13 @@
 import { createApp } from 'vue'
-import './style.css'
+import {router} from './router'
 import App from './App.vue'
+import './style.css'
+import axios from 'axios'
 import ElementPlus from 'element-plus'
 import 'element-plus/dist/index.css'
 import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 
-const app = createApp(App)
-app.use(ElementPlus)
-for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
-  app.component(key, component)
-}
-app.mount('#app')
+axios.defaults.baseURL = ("http://localhost:8080")
+axios.defaults.timeout = 30000;
+
+createApp(App).use(router).mount('#app')

+ 29 - 3
src/router/index.ts

@@ -1,16 +1,42 @@
-import { createRouter, createWebHistory } from "vue-router";
+import {createRouter, createWebHistory} from "vue-router";
 import SingleStore from "../components/SingleStore.vue";
 import AllStore from "../components/AllStore.vue";
+import Login from "../components/Login.vue"
 
 const router = createRouter({
     history: createWebHistory(),
     routes: [{
+        path: '/',
+        component: Login,
+    }, {
+        path: '/login',
+        component: Login,
+    }, {
+        path: '/register',
+        component: () => import('../components/Register.vue'),
+    }, {
+        path: '/home',
+        component: () => import('../views/Home.vue'),
+        children: [
+            {
+                path: '/dashboard',
+                name: 'Dashboard',
+                component: () => import('../components/Dashboard.vue'),
+                meta: {
+                    title: '个人信息'
+                }
+            },
+        ]
+    }, {
+        path: '/manager/createStore',
+        component: () => import('../views/MANAGER/createStore.vue'),
+    }, {
         path: '/singlestore',
         component: SingleStore,
-    },{
+    }, {
         path: '/allstore',
         component: AllStore,
     }]
 })
 
-export {router}
+export {router};

+ 5 - 0
src/shim-vue.d.ts

@@ -0,0 +1,5 @@
+declare module '*.vue' {
+    import { defineComponent } from 'vue'
+    constcomponent: ReturnType<typeof defineComponent>
+    export default component
+}

+ 45 - 67
src/style.css

@@ -1,79 +1,57 @@
-:root {
-  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
-  line-height: 1.5;
-  font-weight: 400;
-
-  color-scheme: light dark;
-  color: rgba(255, 255, 255, 0.87);
-  background-color: #242424;
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
 
-  font-synthesis: none;
-  text-rendering: optimizeLegibility;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
+:root {
+    font-family: sans-serif;
+    line-height: 1.5;
+    font-weight: 400;
 }
 
-a {
-  font-weight: 500;
-  color: #646cff;
-  text-decoration: inherit;
-}
-a:hover {
-  color: #535bf2;
+html, body {
+    margin: 0;
+    padding: 0;
+    height: 100%;
 }
 
-body {
-  margin: 0;
-  display: flex;
-  place-items: center;
-  min-width: 320px;
-  min-height: 100vh;
-}
+#app {
+    margin: 0;
+    padding: 0;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
 
-h1 {
-  font-size: 3.2em;
-  line-height: 1.1;
+    @apply min-h-screen
 }
 
-button {
-  border-radius: 8px;
-  border: 1px solid transparent;
-  padding: 0.6em 1.2em;
-  font-size: 1em;
-  font-weight: 500;
-  font-family: inherit;
-  background-color: #1a1a1a;
-  cursor: pointer;
-  transition: border-color 0.25s;
-}
-button:hover {
-  border-color: #646cff;
-}
-button:focus,
-button:focus-visible {
-  outline: 4px auto -webkit-focus-ring-color;
-}
+@layer components {
+    .main {
+        @apply flex grow items-center justify-center h-screen text-center;
+        height: auto;
+    }
 
-.card {
-  padding: 2em;
-}
+    .dropzone-container {
+        @apply p-16 bg-[#f7fafc] border border-solid border-[#e2e8f0];
+    }
 
-#app {
-  max-width: 100%;
-  margin: 0 auto;
-  padding: 2rem;
-  text-align: center;
-}
+    .hidden-input {
+        @apply opacity-0 overflow-hidden absolute w-1 h-2;
+    }
+
+    .file-label {
+        @apply text-xl block cursor-pointer;
+        font-size: 10pt;
+    }
+
+    .preview-container {
+        @apply flex mt-8;
+    }
+
+    .preview-card {
+        @apply flex border border-solid border-[#a2a2a2] p-2 ml-2;
+    }
 
-@media (prefers-color-scheme: light) {
-  :root {
-    color: #213547;
-    background-color: #ffffff;
-  }
-  a:hover {
-    color: #747bff;
-  }
-  button {
-    background-color: #f9f9f9;
-  }
+    .preview-img {
+        @apply w-14 h-14 rounded-md border border-solid border-[#a2a2a2] bg-[#a2a2a2];
+    }
 }

+ 0 - 2
src/utils/request.ts

@@ -8,8 +8,6 @@ function hasToken() {
     return !(localStorage.getItem('token') == '')
 }
 
-
-
 service.interceptors.request.use(
     config => {
         if(hasToken()) {

+ 16 - 0
src/views/Home.vue

@@ -0,0 +1,16 @@
+<script setup lang="ts">
+  import Header from "../components/Header.vue"
+  // import Footer from "../components/Footer.vue";
+</script>
+
+<template>
+
+  <Header/>
+  <router-view />
+<!--  <Footer />-->
+
+</template>
+
+<style scoped>
+
+</style>

+ 188 - 0
src/views/MANAGER/createStore.vue

@@ -0,0 +1,188 @@
+<script setup lang="ts">
+import {computed, ref} from 'vue'
+import {createStore} from "../../api/store.ts"
+import {uploadImage} from "../../api/image.ts"
+
+// 输入框值
+const name = ref('')
+const location = ref('')
+let logoUrl = ref('')
+
+// 用于前端阻拦不合法输入
+const hasNameInput = computed(() => name.value != '')
+const hasLocationInput = computed(() => location.value != '')
+
+const nameLegal = hasNameInput
+const locationLegal = hasLocationInput
+
+const isDragging = ref(false)
+const files = ref([])
+const fileInputRef = ref(null)
+
+function onChange() {
+  files.value = [...fileInputRef.value.files]
+}
+
+function dragover(e) {
+  e.preventDefault()
+  isDragging.value = true
+}
+
+function dragleave() {
+  isDragging.value = false
+}
+
+function drop(e) {
+  e.preventDefault()
+  fileInputRef.value.files = e.dataTransfer.files
+  onChange()
+  isDragging.value = false
+}
+
+function generateURL(file) {
+  let formData = new FormData();
+  formData.append('file', file);
+  uploadImage(formData).then(res => {
+    logoUrl = res.result;
+  });
+
+  let fileSrc = URL.createObjectURL(file);
+  setTimeout(() => {
+    URL.revokeObjectURL(fileSrc);
+  }, 1000);
+
+  return fileSrc;
+}
+
+function remove(i) {
+  files.value.splice(i, 1);
+  console.log(files.value)
+}
+
+const createDisabled = computed(() => {
+  return !(nameLegal.value && locationLegal.value && files.value);
+})
+
+function handleCreateStore() {
+  const payload = {
+    name: name.value,
+    location: location.value,
+    logoUrl: logoUrl
+  };
+  createStore(payload).then(res => {
+    console.log(res);
+    if (res.code === '000') {
+      ElMessage({
+        message: '添加商户成功!',
+        type: 'success',
+        center: true,
+      })
+    } else if (res.code === '400') {
+      ElMessage({
+        message: res.msg,
+        type: 'error',
+        center: true,
+      })
+    }
+  });
+}
+</script>
+
+<template>
+  <div class="flex flex-col items-center justify-center px-6 py-6 space-y-10">
+
+    <span class="flex items-center text-2xl font-semibold text-gray-900">
+      Blue Whale Shopping Online
+    </span>
+
+    <el-card class="w-full bg-white rounded-lg shadow-md md:mt-0 sm:max-w-md xl:p-0">
+
+      <div class="p-6 space-y-4 md:space-y-6 sm:p-8">
+
+        <h1 class="text-2xl font-bold leading-tight tracking-tight text-gray-900">
+          添加商店
+        </h1>
+
+        <el-form class="space-y-4 md:space-y-6">
+
+          <el-form-item>
+            <label for="name" class="inline-block mb-2 text-sm font-medium text-gray-900">商店名</label>
+            <input id="name" v-model="name"
+                   required
+                   class="bg-gray-50 border border-gray-300 text-gray-900 focus:outline-primary-600 sm:text-sm rounded-lg block w-full p-2.5"
+                   placeholder="请输入商店名">
+          </el-form-item>
+
+          <el-form-item>
+            <label for="location" class="inline-block mb-2 text-sm font-medium text-gray-900">商店地址</label>
+            <input id="location" v-model="location"
+                   required
+                   class="bg-gray-50 border border-gray-300 text-gray-900 focus:outline-primary-600 sm:text-sm rounded-lg block w-full p-2.5"
+                   placeholder="楼层-门牌号 如:3楼-305">
+          </el-form-item>
+
+          <el-form-item>
+            <label for="logoUrl" class="inline-block mb-2 text-sm font-medium text-gray-900">商店Logo</label>
+            <div class="main">
+              <div
+                  class="dropzone-container"
+                  @dragover="dragover"
+                  @dragleave="dragleave"
+                  @drop="drop"
+              >
+                <input
+                    type="file"
+                    multiple
+                    name="file"
+                    id="fileInput"
+                    class="hidden-input"
+                    @change="onChange"
+                    ref="fileInputRef"
+                    accept=".pdf,.jpg,.jpeg,.png"
+                />
+
+                <label for="fileInput" class="file-label">
+                  <div v-if="isDragging">释放以将文件放到此处。</div>
+                  <div v-else>将文件拖到此处或单击此处上传。</div>
+                </label>
+
+                <div class="preview-container mt-4" v-if="files.length">
+                  <div v-for="file in files" :key="file.name" class="preview-card">
+                    <div>
+                      <img class="preview-img" :src="generateURL(file)"/>
+                      <p>
+                        {{ file.name }}
+                      </p>
+                    </div>
+                    <div>
+                      <button
+                          class="ml-2"
+                          type="button"
+                          @click="remove(files.indexOf(file))"
+                          title="Remove file"
+                      >
+                        <b>×</b>
+                      </button>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </el-form-item>
+
+          <el-form-item>
+            <button @click.prevent="handleCreateStore()"
+                    :disabled="createDisabled"
+                    class="block my-3 w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-3 text-center disabled:bg-primary-100">
+              添加
+            </button>
+          </el-form-item>
+        </el-form>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<style scoped>
+
+</style>

+ 16 - 0
tailwind.config.ts

@@ -0,0 +1,16 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+  content: [
+      './src/**/*.{vue,js,ts,jsx,tsx}',
+      "./index.html",
+  ],
+  theme: {
+      extend: {
+          colors: {
+              primary: {"50":"#eff6ff","100":"#dbeafe","200":"#bfdbfe","300":"#93c5fd","400":"#60a5fa","500":"#3b82f6","600":"#2563eb","700":"#1d4ed8","800":"#1e40af","900":"#1e3a8a","950":"#172554"}
+          }
+      },
+  },
+  plugins: [],
+}
+

+ 6 - 1
tsconfig.json

@@ -20,6 +20,11 @@
     "noUnusedParameters": true,
     "noFallthroughCasesInSwitch": true
   },
-  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
+  "include": [
+    "src/**/*.ts",
+    "src/**/*.tsx",
+    "src/**/*.vue",
+    "auto-imports.d.ts"
+  ],
   "references": [{ "path": "./tsconfig.node.json" }]
 }

+ 26 - 2
vite.config.ts

@@ -1,7 +1,31 @@
-import { defineConfig } from 'vite'
+import {defineConfig} from 'vite'
 import vue from '@vitejs/plugin-vue'
+import tailwindcss from 'tailwindcss'
+
+// Element UI 自动导入支持
+import AutoImport from 'unplugin-auto-import/vite'
+import Components from 'unplugin-vue-components/vite'
+import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'
 
 // https://vitejs.dev/config/
 export default defineConfig({
-  plugins: [vue()],
+    plugins: [
+        vue(),
+        AutoImport({
+            resolvers: [ElementPlusResolver()],
+        }),
+        Components({
+            resolvers: [ElementPlusResolver()],
+        })],
+    css: {
+        postcss: {
+            plugins: [tailwindcss],
+        }
+    },
+    devServer: {
+        port: 3000,
+        open: true,
+        hot: true,//自动保存
+    },
+
 })

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.