Bläddra i källkod

结构大变化,更改为按模块组织界面文件。完成了用户/商店/商品模块的修改。

chillqi 2 år sedan
förälder
incheckning
71c0a57570

+ 4 - 2
components.d.ts

@@ -33,12 +33,14 @@ declare module 'vue' {
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
     ElTag: typeof import('element-plus/es')['ElTag']
     ElTag: typeof import('element-plus/es')['ElTag']
+    ElUpload: typeof import('element-plus/es')['ElUpload']
     Footer: typeof import('./src/components/Footer.vue')['default']
     Footer: typeof import('./src/components/Footer.vue')['default']
     Header: typeof import('./src/components/Header.vue')['default']
     Header: typeof import('./src/components/Header.vue')['default']
-    Login: typeof import('./src/components/Login.vue')['default']
     NotFound: typeof import('./src/components/NotFound.vue')['default']
     NotFound: typeof import('./src/components/NotFound.vue')['default']
-    Register: typeof import('./src/components/Register.vue')['default']
+    OrderItem: typeof import('./src/components/OrderItem.vue')['default']
+    ProductItem: typeof import('./src/components/ProductItem.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
     RouterView: typeof import('vue-router')['RouterView']
+    StoreItem: typeof import('./src/components/StoreItem.vue')['default']
   }
   }
 }
 }

+ 2 - 15
src/components/Header.vue

@@ -1,13 +1,7 @@
 <script setup lang="ts">
 <script setup lang="ts">
-import {CirclePlus, UserFilled, Discount, SwitchButton, Document} from "@element-plus/icons-vue";
+import {UserFilled, Discount, SwitchButton, Document} from "@element-plus/icons-vue";
 import {router} from '../router'
 import {router} from '../router'
 
 
-// const searchContent = ref('')
-
-// const userName = ref()
-// userName.value = sessionStorage.getItem("name")
-
-
 function logout() {
 function logout() {
   ElMessageBox.confirm(
   ElMessageBox.confirm(
       '是否要退出登录?',
       '是否要退出登录?',
@@ -55,15 +49,8 @@ const currentIdentity = sessionStorage.getItem('role')
 
 
       </el-col>
       </el-col>
 
 
-
-      <el-col :span="1" class="header-icon" v-if="currentIdentity === 'MANAGER'">
-        <router-link to="/manager/createStore" v-slot="{navigate}">
-          <el-icon @click="navigate" :size="35" color="white" ><CirclePlus /></el-icon>
-        </router-link>
-      </el-col>
-
       <el-col :span="1" class="header-icon">
       <el-col :span="1" class="header-icon">
-        <router-link to="/customer/allOrder" v-slot="{navigate}" v-if="currentIdentity === 'CUSTOMER'">
+        <router-link to="/allOrder" v-slot="{navigate}">
           <el-icon @click="navigate" :size="35" color="white" ><Document /></el-icon>
           <el-icon @click="navigate" :size="35" color="white" ><Document /></el-icon>
         </router-link>
         </router-link>
       </el-col>
       </el-col>

+ 0 - 177
src/components/Login.vue

@@ -1,177 +0,0 @@
-<script setup lang="ts">
-  import {ElForm, ElFormItem } from "element-plus";
-  import { ref, computed } from 'vue';
-  import {router} from '../router'
-  import {userInfo, 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 => {
-          const token = res.result
-          sessionStorage.setItem('token', token)
-
-          userInfo().then(res => {
-            sessionStorage.setItem('name', res.result.name)
-            sessionStorage.setItem('role', res.result.role)
-            router.push({path: "/allStore"})
-          }).catch(e => {
-            console.log(e.message)
-            ElMessageBox.alert(
-                e.message,
-                '错误提示',
-                {
-                  confirmButtonText: '好的',
-                  type: "error",
-                  showClose: false,
-                  roundButton: true,
-                  center: true
-                }
-            ).then(() => {
-              // 清空用户密码
-              password.value = ''
-            })
-          })
-
-        })
-        .catch(e => {
-          console.log(e.message)
-          ElMessageBox.alert(
-              e.message,
-              '错误提示',
-              {
-                confirmButtonText: '好的',
-                type: "error",
-                showClose: false,
-                roundButton: true,
-                center: true
-              }
-          ).then(() => {
-            // 清空用户密码
-            password.value = ''
-          })
-        })
-  }
-
-</script>
-
-
-<template>
-
-  <el-main class="main-frame bgimage">
-
-    <el-card class="login-card">
-
-      <div>
-
-        <h1>登入您的账户</h1>
-
-        <el-form>
-
-          <el-form-item>
-            <label v-if="!hasTelInput" for="tel">注册手机号</label>
-            <label v-else-if="!telLegal" for="tel" class="error-warn">手机号不合法</label>
-            <label v-else for="tel">注册手机号</label>
-
-            <el-input id="tel" type="text" v-model="tel"
-                   required :class="{'error-warn-input' :(hasTelInput && !telLegal)}"
-                   placeholder="请输入手机号" />
-          </el-form-item>
-
-          <el-form-item>
-            <label for="password">账户密码</label>
-            <el-input id="password" type="password" v-model="password"
-                   required
-                   placeholder="••••••••"/>
-          </el-form-item>
-
-          <span class="button-group">
-
-              <el-button @click.prevent="handleLogin"
-                         :disabled="loginDisabled"
-                         type="primary">
-                登入
-              </el-button>
-
-              <router-link to="/register" v-slot="{navigate}">
-                <el-button @click="navigate">
-                  注册
-                </el-button>
-              </router-link>
-
-          </span>
-
-        </el-form>
-
-      </div>
-
-
-    </el-card>
-  </el-main>
-</template>
-
-
-
-<style scoped>
-
-.bgimage {
-  background-image: url("../assets/shopping-1s-1084px.svg");
-}
-
-.main-frame {
-  width: 100%;
-  height: 100%;
-
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.login-card {
-  width: 60%;
-  padding: 10px;
-}
-
-.error-warn {
-  color: red;
-}
-
-.error-warn-input {
-  --el-input-focus-border-color: red;
-}
-
-.button-group {
-  padding-top: 10px;
-  display: flex;
-  flex-direction: row;
-  gap: 30px;
-  align-items: center;
-  justify-content: right;
-}
-
-
-</style>

+ 3 - 2
src/views/common/OrderItem.vue → src/components/OrderItem.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 <script setup lang="ts">
-import {deliverOrder, getOrder, getOrderById, payOrder, commentOrder} from "../../api/order.ts";
+import {deliverOrder, getOrder, getOrderById, payOrder, commentOrder} from "../api/order.ts";
 import {ref, computed} from "vue";
 import {ref, computed} from "vue";
-import {getAvailableCouponsByStoreId} from "../../api/coupon.ts";
+import {getAvailableCouponsByStoreId} from "../api/coupon.ts";
 import {ElTable} from "element-plus";
 import {ElTable} from "element-plus";
 
 
 const props = defineProps({
 const props = defineProps({
@@ -11,6 +11,7 @@ const props = defineProps({
   }
   }
 })
 })
 
 
+const role = sessionStorage.getItem("role")
 const orderDialogVisible = ref(false)
 const orderDialogVisible = ref(false)
 const commentDialogVisible = ref(false)
 const commentDialogVisible = ref(false)
 
 

+ 110 - 0
src/components/ProductItem.vue

@@ -0,0 +1,110 @@
+<script setup lang="ts">
+import {ref} from "vue";
+import {getProductById} from "../api/product.ts";
+
+const props = defineProps({
+  productId: {
+    type: Number,
+    required: true
+  }
+})
+
+const storeId = ref(0)
+const name = ref('')
+const photoUrlList = ref([])
+const rating = ref(0)
+const number = ref(0)
+const salesAmount = ref(0)
+const stock = ref(0)
+const category = ref()
+const price = ref()
+const photoUrl = ref('')
+
+getProductDetail()
+
+function getProductDetail() {
+  getProductById(props.productId).then(res => {
+    storeId.value = res.result.storeId
+    name.value = res.result.name
+    photoUrlList.value = res.result.photoUrlList
+    rating.value = res.result.rating
+    number.value = res.result.number
+    salesAmount.value = res.result.salesAmount
+    stock.value = res.result.stock
+    category.value = res.result.category
+    price.value = res.result.price
+
+    photoUrl.value = photoUrlList.value[0]
+  })
+}
+
+function parseCategory(category) {
+  if (category === 'FOOD') {
+    return "食品"
+  } else if (category === 'CLOTHES') {
+    return "服饰"
+  } else if (category === 'FURNITURE') {
+    return "家具"
+  } else if (category === 'ELECTRONICS') {
+    return "电子产品"
+  } else if (category === 'ENTERTAINMENT') {
+    return "娱乐"
+  } else if (category === 'SPORTS') {
+    return "体育产品"
+  } else if (category === 'LUXURY') {
+    return "奢侈品"
+  }
+}
+</script>
+
+<template>
+  <el-card class="product-item-card" shadow="hover">
+    <div class="photo-div">
+      <el-image class="photo-image" :src="photoUrl"/>
+    </div>
+    <div style="padding: 14px">
+      <h1> {{ name }} </h1>
+      <el-descriptions :column="1">
+        <el-descriptions-item style="font-size: 10px" label="品类">
+          {{ parseCategory(category) }}
+        </el-descriptions-item>
+        <el-descriptions-item style="font-size: 10px" label="价格">
+          {{ price }} 元
+        </el-descriptions-item>
+        <el-descriptions-item style="font-size: 10px" label="评分">
+          <el-rate
+              v-model="rating"
+              disabled
+              show-score
+              text-color="#ff9900"
+              score-template="{value} 分"
+              size="small"
+          />(共 {{ number }} 人打分)
+        </el-descriptions-item>
+        <el-descriptions-item style="font-size: 10px" label="销量">
+          {{ salesAmount }} 件
+        </el-descriptions-item>
+        <el-descriptions-item style="font-size: 10px" label="库存">
+          {{ stock }} 件
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+  </el-card>
+</template>
+
+<style scoped>
+.product-item-card {
+  margin: 10px;
+  border-radius: 8px;
+  min-width: 31%;
+}
+
+.photo-div {
+  display: flex;
+  justify-content: center;
+}
+
+.photo-image {
+  height: 200px;
+}
+</style>

+ 3 - 9
src/views/common/StoreItem.vue → src/components/StoreItem.vue

@@ -1,6 +1,6 @@
 <script setup lang="ts">
 <script setup lang="ts">
 import {ref} from "vue"
 import {ref} from "vue"
-import {getStoreById} from "../../api/store.ts"
+import {getStoreById} from "../api/store.ts"
 
 
 const props = defineProps({
 const props = defineProps({
   storeId: {
   storeId: {
@@ -35,13 +35,8 @@ function getStoreDetail() {
     <el-image class="logo-image" :src="logoUrl"/>
     <el-image class="logo-image" :src="logoUrl"/>
 
 
     <div style="padding: 14px">
     <div style="padding: 14px">
-
       <h1> {{name}} </h1>
       <h1> {{name}} </h1>
-
       <el-descriptions :column="1">
       <el-descriptions :column="1">
-        <el-descriptions-item style="font-size: 10px" label="评分人数">
-          {{ number }}
-        </el-descriptions-item>
         <el-descriptions-item style="font-size: 10px" label="评分">
         <el-descriptions-item style="font-size: 10px" label="评分">
           <el-rate
           <el-rate
               v-model="rating"
               v-model="rating"
@@ -51,14 +46,13 @@ function getStoreDetail() {
               score-template="{value} 分"
               score-template="{value} 分"
               size="small"
               size="small"
           />
           />
+          (共 {{ number }} 人打分)
         </el-descriptions-item>
         </el-descriptions-item>
         <el-descriptions-item style="font-size: 10px" label="地址">
         <el-descriptions-item style="font-size: 10px" label="地址">
           {{ location }}
           {{ location }}
         </el-descriptions-item>
         </el-descriptions-item>
       </el-descriptions>
       </el-descriptions>
-
     </div>
     </div>
-
   </el-card>
   </el-card>
 </template>
 </template>
 
 
@@ -71,7 +65,7 @@ function getStoreDetail() {
 }
 }
 
 
 .logo-image {
 .logo-image {
-  width: 100%;
+  height: 250px;
   display: block;
   display: block;
 }
 }
 </style>
 </style>

+ 14 - 23
src/router/index.ts

@@ -7,24 +7,24 @@ const router = createRouter({
         redirect: '/login',
         redirect: '/login',
     }, {
     }, {
         path: '/login',
         path: '/login',
-        component: () => import('../components/Login.vue'),
+        component: () => import('../views/user/Login.vue'),
     }, {
     }, {
         path: '/register',
         path: '/register',
-        component: () => import('../components/Register.vue'),
+        component: () => import('../views/user/Register.vue'),
     }, {
     }, {
         path: '/home',
         path: '/home',
         redirect: '/allStore',
         redirect: '/allStore',
-        component: () => import('../views/portal/Home.vue'),
+        component: () => import('../views/Home.vue'),
         children: [
         children: [
             {
             {
                 path: '/allStore',
                 path: '/allStore',
                 name: 'allStore',
                 name: 'allStore',
-                component: () => import('../views/portal/AllStore.vue'),
+                component: () => import('../views/store/AllStore.vue'),
             },
             },
             {
             {
                 path: '/dashboard',
                 path: '/dashboard',
                 name: 'Dashboard',
                 name: 'Dashboard',
-                component: () => import('../views/portal/Dashboard.vue'),
+                component: () => import('../views/user/Dashboard.vue'),
                 meta: {
                 meta: {
                     title: '个人信息'
                     title: '个人信息'
                 }
                 }
@@ -32,15 +32,15 @@ const router = createRouter({
             {
             {
                 path: '/storeDetail/:storeId',
                 path: '/storeDetail/:storeId',
                 name: 'storeDetail',
                 name: 'storeDetail',
-                component: () => import('../views/portal/StoreDetail.vue'),
+                component: () => import('../views/store/StoreDetail.vue'),
                 meta: {
                 meta: {
                     title: '店铺详情'
                     title: '店铺详情'
                 }
                 }
             },
             },
             {
             {
-                path: '/staff/createProduct/:storeId',
+                path: '/createProduct/:storeId',
                 name: 'createProduct',
                 name: 'createProduct',
-                component: () => import('../views/staff/CreateProduct.vue'),
+                component: () => import('../views/product/CreateProduct.vue'),
                 meta: {
                 meta: {
                     title: '创建商品',
                     title: '创建商品',
                     permission: ['STAFF', 'MANAGER']
                     permission: ['STAFF', 'MANAGER']
@@ -67,33 +67,24 @@ const router = createRouter({
             {
             {
                 path: '/productDetail/:productId',
                 path: '/productDetail/:productId',
                 name: 'productDetail',
                 name: 'productDetail',
-                component: () => import('../views/portal/ProductDetail.vue'),
+                component: () => import('../views/product/ProductDetail.vue'),
                 meta: {
                 meta: {
                     title: '商品详情'
                     title: '商品详情'
                 }
                 }
             },
             },
             {
             {
-                path: '/customer/buyProduct/:productId',
-                name: 'buyProduct',
-                component: () => import('../views/customer/BuyProduct.vue'),
-                meta: {
-                    title: '购买商品',
-                    permission: ['CUSTOMER']
-                }
-            },
-            {
-                path: '/customer/allOrder',
+                path: '/allOrder',
                 name: 'allOrder',
                 name: 'allOrder',
-                component: () => import('../views/customer/AllOrder.vue'),
+                component: () => import('../views/order/AllOrder.vue'),
                 meta: {
                 meta: {
                     title: '全部订单',
                     title: '全部订单',
-                    permission: ['CUSTOMER']
+                    permission: ['CUSTOMER', 'STAFF', 'MANAGER', 'CEO']
                 }
                 }
             },
             },
             {
             {
-                path: '/manager/createStore',
+                path: '/createStore',
                 name: 'createStore',
                 name: 'createStore',
-                component: () => import('../views/manager/CreateStore.vue'),
+                component: () => import('../views/store/CreateStore.vue'),
                 meta: {
                 meta: {
                     title: '创建店铺',
                     title: '创建店铺',
                     permission: ['MANAGER']
                     permission: ['MANAGER']

+ 1 - 1
src/views/portal/Home.vue → src/views/Home.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
 <script setup lang="ts">
-import Header from "../../components/Header.vue"
+import Header from "../components/Header.vue"
 
 
 </script>
 </script>
 
 

+ 0 - 82
src/views/common/ProductItem.vue

@@ -1,82 +0,0 @@
-<script setup lang="ts">
-import {ref} from "vue";
-import {getProductById} from "../../api/product.ts";
-
-const props = defineProps({
-  productId: {
-    type: Number,
-    required: true
-  }
-})
-
-const storeId = ref(0)
-const name = ref('')
-const photoUrlList = ref([])
-const rating = ref(0)
-const number = ref(0)
-const salesAmount = ref(0)
-const stock = ref(0)
-const category = ref('')
-const price = ref(0)
-
-getProductDetail()
-
-function getProductDetail() {
-  getProductById(props.productId).then(res => {
-    storeId.value = res.result.storeId
-    name.value = res.result.name
-    photoUrlList.value = res.result.photoUrlList
-    rating.value = res.result.rating
-    number.value = res.result.number
-    salesAmount.value = res.result.salesAmount
-    stock.value = res.result.stock
-    category.value = res.result.category
-    price.value = res.result.price
-  })
-}
-</script>
-
-<template>
-  <el-card class="product-item-card" shadow="hover">
-    <el-row style="font-size: 20px">
-      {{ name }}
-    </el-row>
-    <el-row style="font-size: 10px">
-      评分人数: {{ number }}
-    </el-row>
-    <el-row style="font-size: 10px">
-      评分:
-      <el-rate
-          v-model="rating"
-          disabled
-          show-score
-          text-color="#ff9900"
-          score-template="{value} 分"
-          size="small"
-      />
-    </el-row>
-    <el-row style="font-size: 10px">
-      销量: {{ salesAmount }}
-    </el-row>
-    <el-row style="font-size: 10px">
-      品类: {{ category }}
-    </el-row>
-    <el-row style="font-size: 10px">
-      价格: {{ price }}
-    </el-row>
-    <el-row style="font-size: 10px">
-      库存: {{ stock }}
-    </el-row>
-
-  </el-card>
-</template>
-
-<style scoped>
-.product-item-card {
-  width: 30%;
-  height: 30%;
-  border-radius: 8px;
-  margin-bottom: 20px;
-  margin-right: auto;
-}
-</style>

+ 0 - 227
src/views/customer/BuyProduct.vue

@@ -1,227 +0,0 @@
-<script setup lang="ts">
-import {useRouter} from "vue-router";
-import {ref, reactive, toRefs, toRaw} from "vue";
-import {getProductById} from "../../api/product.ts";
-import {createOrder, payOrder} from "../../api/order.ts";
-import {getAvailableCouponsByStoreId} from "../../api/coupon.ts";
-import {ElTable} from "element-plus";
-
-const router = useRouter();
-const productId = router.currentRoute.value.params.productId;
-const productVO = ref()
-let storeId = ref(0)
-let name = ref('')
-let photoUrlList = ref([])
-let rating = ref(0)
-let number = ref(0)
-let salesAmount = ref(0)
-let stock = ref(0)
-let category = ref('')
-let price = ref(0)
-
-getProductDetail()
-
-function getProductDetail() {
-  getProductById(productId).then(res => {
-    productVO.value = res.result
-    storeId.value = productVO.value.storeId
-    name.value = productVO.value.name
-    photoUrlList.value = productVO.value.photoUrlList
-    rating.value = productVO.value.rating
-    number.value = productVO.value.number
-    salesAmount.value = productVO.value.salesAmount
-    stock.value = productVO.value.stock
-    category.value = productVO.value.category
-    price.value = productVO.value.price
-  })
-}
-
-const amount = ref(1)
-const totalPrice = ref(price)
-const handleChange = () => {
-  totalPrice.value = amount.value * price.value
-}
-const type = ref()
-let orderDialogVisible = ref(false)
-const orderId = ref()
-const couponId = ref(0)
-
-function handleCreateOrder() {
-  const payload = {
-    productId: productId,
-    amount: amount.value,
-    type: type.value
-  };
-  createOrder(payload).then(res => {
-    if (res.code === '000') {
-      ElMessage({
-        message: '创建订单成功!',
-        type: 'success',
-        center: true,
-      })
-      getAvailableCouponList()
-      orderId.value = res.result.id
-      orderDialogVisible.value = true
-    } else if (res.code === '400') {
-      ElMessage({
-        message: res.msg,
-        type: 'error',
-        center: true,
-      })
-    }
-  })
-}
-
-function handleConfirmOrder() {
-  console.log(couponId.value)
-  payOrder(orderId.value, couponId.value).then(res => {
-    console.log(res.code)
-    if (res.code === '000') {
-      ElMessage({
-        message: '订单支付成功!',
-        type: 'success',
-        center: true,
-      })
-      orderDialogVisible.value = false
-      amount.value = 1
-      type.value = ''
-      totalPrice.value = price.value
-    } else if (res.code === '400') {
-      ElMessage({
-        message: res.msg,
-        type: 'error',
-        center: true,
-      })
-    }
-  })
-}
-
-function handleOrderType(type) {
-  if (type === "PICKUP") {
-    return "到店自提"
-  } else if (type === "DELIVERY") {
-    return "快递到家"
-  }
-}
-
-function handleCouponType(type) {
-  if (type === "FULL_REDUCTION") {
-    return "满减"
-  } else if (type === "SPECIAL") {
-    return "折扣"
-  }
-}
-
-// 处理优惠券
-interface Coupon {
-  id: number
-  groupId: number
-  type: string
-  satisfaction: number
-  minus: number
-}
-
-const availableCouponList = ref<Coupon[]>([])
-const currentRow = ref()
-const singleTableRef = ref<InstanceType<typeof ElTable>>()
-
-function getAvailableCouponList() {
-  getAvailableCouponsByStoreId(storeId.value).then(res => {
-    availableCouponList.value = res.result
-  })
-}
-
-const setCurrent = (row?: User) => {
-  singleTableRef.value!.setCurrentRow(row)
-}
-
-const handleCurrentChange = (val: Coupon | undefined) => {
-  if (val) {
-    couponId.value = val.id
-  } else {
-    couponId.value = 0
-  }
-  currentRow.value = val
-}
-
-</script>
-
-<template>
-  <div>
-    <el-form class="buy-form-main">
-      <el-form-item>
-        <label for="amount">商品单价:</label>
-        {{ price }}
-      </el-form-item>
-      <el-form-item>
-        <label for="amount">购买数量:</label>
-        <el-input-number v-model="amount" :min="1" :max="stock + 10" @change="handleChange"/>
-      </el-form-item>
-      <el-form-item>
-        <label for="type">提货方式:</label>
-        <el-select id="type"
-                   v-model="type"
-                   placeholder="请选择"
-        >
-          <el-option value="PICKUP" label="到店自提"/>
-          <el-option value="DELIVERY" label="快递到家"/>
-        </el-select>
-      </el-form-item>
-      <el-form-item>
-        <label for="discountPrice">折扣前总价:</label>
-        {{ totalPrice }} 元
-      </el-form-item>
-    </el-form>
-    <el-button class="buy-button"
-               @click="handleCreateOrder">创建订单
-    </el-button>
-
-    <el-dialog v-model="orderDialogVisible">
-      <el-form>
-        <el-form-item>
-          <label for="amount">购买数量:</label>
-          {{ amount }}
-        </el-form-item>
-        <el-form-item>
-          <label for="discountPrice">折扣前总价:</label>
-          {{ totalPrice }}
-        </el-form-item>
-        <el-form-item>
-          <label for="type">提货方式:</label>
-          {{ handleOrderType(type) }}
-        </el-form-item>
-        <el-form-item>
-          <label for="type">优惠券:</label> <el-button @click="setCurrent()">不使用优惠券</el-button>
-          <el-table
-              ref="singleTableRef"
-              :data="availableCouponList"
-              highlight-current-row
-              style="width: 100%"
-              @current-change="handleCurrentChange"
-          >
-            <el-table-column type="index" width="50"/>
-            <el-table-column prop="type" label="类型" width="180">
-              <template #default="scope">
-                {{ handleCouponType(scope.row.type) }}
-              </template>
-            </el-table-column>
-            <el-table-column prop="satisfaction" label="满(元)" width="180"/>
-            <el-table-column prop="minus" label="减(元)" width="180"/>
-          </el-table>
-        </el-form-item>
-      </el-form>
-      <el-button @click="handleConfirmOrder" type="primary" plain>确认支付</el-button>
-    </el-dialog>
-  </div>
-</template>
-
-<style scoped>
-.buy-form-main {
-  margin-top: 50px;
-  margin-left: 30px;
-}
-
-.buy-button {
-  margin-left: 30px;
-}
-</style>

+ 0 - 201
src/views/manager/CreateStore.vue

@@ -1,201 +0,0 @@
-<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.value = 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.value
-  };
-  createStore(payload).then(res => {
-    if (res.code === '000') {
-      ElMessage({
-        message: '添加商户成功!',
-        type: 'success',
-        center: true,
-      })
-      name.value = '';
-      location.value = '';
-      logoUrl.value = '';
-      files.value.splice(0);
-    } else if (res.code === '400') {
-      ElMessage({
-        message: res.msg,
-        type: 'error',
-        center: true,
-      })
-    }
-  });
-}
-</script>
-
-<template>
-
-  <el-main class="main-frame">
-
-
-    <el-card class="current-card">
-
-      <div>
-
-        <h1 >
-          添加商店
-        </h1>
-
-        <el-form>
-
-          <el-form-item>
-            <label for="name">商店名</label>
-            <el-input id="name" v-model="name"
-                   required
-                   placeholder="请输入商店名"/>
-          </el-form-item>
-
-          <el-form-item>
-            <label for="location">商店地址</label>
-            <el-input id="location" v-model="location"
-                   required
-                   placeholder="楼层-门牌号 如:3楼-305" />
-          </el-form-item>
-
-          <el-form-item>
-            <label for="logoUrl">商店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>
-  </el-main>
-</template>
-
-<style scoped>
-
-.main-frame {
-  width: 100%;
-  height: 100%;
-
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.current-card {
-  width: 60%;
-  padding: 10px;
-}
-
-</style>

+ 1 - 1
src/views/customer/AllOrder.vue → src/views/order/AllOrder.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 <script setup lang="ts">
 import {ref} from "vue";
 import {ref} from "vue";
 import {getAllOrder} from "../../api/order.ts";
 import {getAllOrder} from "../../api/order.ts";
-import OrderItem from "../common/OrderItem.vue";
+import OrderItem from "../../components/OrderItem.vue";
 
 
 let orderList = ref()
 let orderList = ref()
 
 

+ 0 - 42
src/views/portal/AllStore.vue

@@ -1,42 +0,0 @@
-<script setup lang="ts">
-
-import StoreItem from "../common/StoreItem.vue"
-import {ref} from "vue"
-import {getAllStore} from "../../api/store.ts"
-import {useRouter} from "vue-router"
-
-const router = useRouter()
-
-let storeList = ref()
-
-getStoreList();
-
-function getStoreList() {
-  getAllStore().then(res => {
-    storeList.value = res.result;
-  })
-}
-
-function toStoreDetailPage(storeId: Number) {
-  router.push("/storeDetail/" + storeId);
-}
-
-</script>
-
-<template>
-  <el-main class="store-item-list">
-    <StoreItem
-               v-for="storeVO in storeList" :storeId="storeVO.id"
-               @click="toStoreDetailPage(storeVO.id)" />
-  </el-main>
-</template>
-
-<style scoped>
-.store-item-list {
-  display: flex;
-  padding: 2px;
-  flex-flow: wrap;
-  justify-content: center;
-  align-content: start;
-}
-</style>

+ 0 - 131
src/views/portal/ProductDetail.vue

@@ -1,131 +0,0 @@
-<script setup lang="ts">
-import {useRouter} from "vue-router";
-import {ref} from "vue";
-import {addStock, getProductById} from "../../api/product.ts";
-
-const router = useRouter();
-const productId = router.currentRoute.value.params.productId;
-const productVO = ref()
-const storeId = ref(0)
-const name = ref('')
-const photoUrlList = ref([])
-const rating = ref(0)
-const number = ref(0)
-const salesAmount = ref(0)
-const stock = ref(0)
-const category = ref('')
-const price = ref(0)
-
-getProductDetail()
-
-function getProductDetail() {
-  getProductById(productId).then(res => {
-    productVO.value = res.result
-    storeId.value = productVO.value.storeId
-    name.value = productVO.value.name
-    photoUrlList.value = productVO.value.photoUrlList
-    rating.value = productVO.value.rating
-    number.value = productVO.value.number
-    salesAmount.value = productVO.value.salesAmount
-    stock.value = productVO.value.stock
-    category.value = productVO.value.category
-    price.value = productVO.value.price
-  })
-}
-
-const addStockNumber = ref(null)
-
-function AddStock() {
-  if (addStockNumber.value === null) {
-    ElMessage({
-      message: "请输入添加库存数!",
-      type: 'error',
-      center: true,
-    })
-    return
-  }
-  addStock(productId, addStockNumber.value).then(res => {
-    if (res.code === '000') {
-      ElMessage({
-        message: '添加库存成功!',
-        type: 'success',
-        center: true,
-      })
-      addStockNumber.value = null
-      getProductDetail()
-    } else if (res.code === '400') {
-      ElMessage({
-        message: res.msg,
-        type: 'error',
-        center: true,
-      })
-    }
-
-  })
-}
-
-function toBuyProduct() {
-  router.push("/customer/buyProduct/" + productId)
-}
-
-</script>
-
-<template>
-  <div>
-    <div class="product-detail-main">
-      <el-row style="font-size: 20px">
-        {{ name }}
-      </el-row>
-      <el-row style="font-size: 10px">
-        评分人数:{{ number }}
-      </el-row>
-      <el-row style="font-size: 10px">
-        评分:{{ rating }}
-      </el-row>
-      <el-row style="font-size: 10px">
-        销量:{{ salesAmount }} 件
-      </el-row>
-      <el-row style="font-size: 10px">
-        品类:{{ category }}
-      </el-row>
-      <el-row style="font-size: 10px">
-        价格:{{ price }} 元
-      </el-row>
-      <el-row style="font-size: 10px">
-          库存:{{ stock }} 件
-      </el-row>
-      <el-row>
-        <el-input size="small" class="addStockNumber-input"
-                  v-model="addStockNumber" placeholder="请输入添加库存数" />
-        <el-button class="add-stock-button" type="primary" plain
-                   @click="AddStock">确认添加
-        </el-button>
-      </el-row>
-      <el-button class="buy-product-button" type="primary" plain
-                 @click="toBuyProduct">购买商品
-      </el-button>
-    </div>
-
-
-  </div>
-</template>
-
-<style scoped>
-.product-detail-main {
-  margin-left: 50px;
-}
-
-.addStockNumber-input {
-  width: 120px;
-  margin-top: 10px;
-}
-
-.add-stock-button {
-  margin-top: 10px;
-  margin-left: 10px;
-}
-
-.buy-product-button {
-  margin-top: 20px;
-}
-</style>

+ 147 - 0
src/views/product/CreateProduct.vue

@@ -0,0 +1,147 @@
+<script setup lang="ts">
+import {useRouter} from "vue-router";
+import {ref, computed} from "vue";
+import {createProduct} from "../../api/product.ts";
+import {uploadImage} from "../../api/image.ts";
+import {Back, UploadFilled} from "@element-plus/icons-vue";
+
+const router = useRouter();
+const storeId = router.currentRoute.value.params.storeId;
+const name = ref('')
+const category = ref('')
+const price = ref()
+const photoUrlList = ref([])
+
+const hasNameInput = computed(() => name.value != '')
+const hasCategoryInput = computed(() => category.value != '')
+const hasPriceInput = computed(() => price.value != '')
+const nameLegal = hasNameInput
+const categoryLegal = hasCategoryInput
+const priceLegal = hasPriceInput
+
+const imageFileList = ref([])
+
+const createDisabled = computed(() => {
+  return !(nameLegal.value && categoryLegal.value && priceLegal.value && photoUrlList.value);
+})
+
+function handleCreateProduct() {
+  const payload = {
+    storeId: storeId,
+    name: name.value,
+    category: category.value,
+    price: price.value,
+    photoUrlList: photoUrlList.value
+  };
+  createProduct(payload).then(res => {
+    if (res.code === '000') {
+      ElMessage({
+        message: '添加商品成功!',
+        type: 'success',
+        center: true,
+      })
+      name.value = '';
+      category.value = '';
+      price.value = '';
+      photoUrlList.value = [];
+      imageFileList.value = [];
+    } else if (res.code === '400') {
+      ElMessage({
+        message: res.msg,
+        type: 'error',
+        center: true,
+      })
+    }
+  })
+}
+
+function toBackPage() {
+  router.push("/storeDetail/" + storeId)
+}
+
+function handleChange(file, fileList) {
+  imageFileList.value = fileList
+  let formData = new FormData();
+  formData.append('file', file.raw);
+  uploadImage(formData).then(res => {
+    photoUrlList.value.push(res.result);
+  });
+}
+
+function uploadHttpRequest() {
+}
+</script>
+
+<template>
+  <el-main class="main-frame">
+    <div>
+      <el-button @click="toBackPage()"
+                 type="primary" circle plain>
+        <el-icon><Back/></el-icon>
+      </el-button>
+
+      <h1>添加商品</h1>
+
+      <el-form label-position="left" label-width="90px" size="large" class="create-store-form">
+
+        <el-form-item label="商品名">
+          <el-input id="name" v-model="name"
+                    required
+                    placeholder="请输入商品名"/>
+        </el-form-item>
+
+        <el-form-item label="品类">
+          <el-select id="category" v-model="category" placeholder="请选择">
+            <el-option value="FOOD" label="食品" />
+            <el-option value="CLOTHES" label="服饰" />
+            <el-option value="FURNITURE" label="家具" />
+            <el-option value="ELECTRONICS" label="电子产品" />
+            <el-option value="ENTERTAINMENT" label="娱乐" />
+            <el-option value="SPORTS" label="体育产品" />
+            <el-option value="LUXURY" label="奢侈品" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="单价">
+          <el-input id="price" v-model="price"
+                    required
+                    placeholder="请输入商品单价,单位(元)"/>
+        </el-form-item>
+
+        <el-form-item label="商品照片">
+          <el-upload
+              v-model:file-list="imageFileList"
+              :on-change="handleChange"
+              :on-remove="handleChange"
+              class="upload-demo"
+              list-type="picture"
+              :http-request="uploadHttpRequest"
+              drag>
+            <el-icon class="el-icon--upload">
+              <upload-filled/>
+            </el-icon>
+            <div class="el-upload__text">
+              将文件拖到此处或单击此处上传。
+            </div>
+          </el-upload>
+
+        </el-form-item>
+
+        <el-form-item>
+          <el-button @click.prevent="handleCreateProduct()"
+                     :disabled="createDisabled"
+                     type="primary" plain>
+            创建商品
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+  </el-main>
+</template>
+
+<style scoped>
+.create-store-form {
+  margin-left: 30px;
+  width: 50%;
+}
+</style>

+ 394 - 0
src/views/product/ProductDetail.vue

@@ -0,0 +1,394 @@
+<script setup lang="ts">
+import {useRouter} from "vue-router";
+import {ref, onMounted} from "vue";
+import {addStock, getProductById} from "../../api/product.ts";
+import {Back} from "@element-plus/icons-vue";
+import {ElTable} from "element-plus";
+import {createOrder, payOrder} from "../../api/order.ts";
+import {getAvailableCouponsByStoreId} from "../../api/coupon.ts";
+
+const router = useRouter();
+const role = sessionStorage.getItem("role")
+const productId = router.currentRoute.value.params.productId;
+const productVO = ref()
+const storeId = ref(0)
+const name = ref('')
+const photoUrlList = ref([])
+const rating = ref(0)
+const number = ref(0)
+const salesAmount = ref(0)
+const stock = ref(0)
+const category = ref('')
+const price = ref(0)
+
+const photoUrl = ref(0)
+
+const amount = ref(1)
+const totalPrice = ref(price.value)
+const handleChange = () => {
+  totalPrice.value = amount.value * price.value
+}
+const type = ref()
+let orderDialogVisible = ref(false)
+const orderId = ref()
+const couponId = ref(0)
+
+getProductDetail()
+
+function getProductDetail() {
+  getProductById(productId).then(res => {
+    productVO.value = res.result
+    storeId.value = productVO.value.storeId
+    name.value = productVO.value.name
+    photoUrlList.value = productVO.value.photoUrlList
+    rating.value = productVO.value.rating
+    number.value = productVO.value.number
+    salesAmount.value = productVO.value.salesAmount
+    stock.value = productVO.value.stock
+    category.value = productVO.value.category
+    price.value = productVO.value.price
+
+    photoUrl.value = photoUrlList.value[0]
+    totalPrice.value = price.value
+  })
+}
+
+function parseCategory(category) {
+  if (category === 'FOOD') {
+    return "食品"
+  } else if (category === 'CLOTHES') {
+    return "服饰"
+  } else if (category === 'FURNITURE') {
+    return "家具"
+  } else if (category === 'ELECTRONICS') {
+    return "电子产品"
+  } else if (category === 'ENTERTAINMENT') {
+    return "娱乐"
+  } else if (category === 'SPORTS') {
+    return "体育产品"
+  } else if (category === 'LUXURY') {
+    return "奢侈品"
+  }
+}
+
+const addStockNumber = ref(null)
+
+function AddStock() {
+  if (addStockNumber.value === null) {
+    ElMessage({
+      message: "请输入添加库存数!",
+      type: 'error',
+      center: true,
+    })
+    return
+  }
+  addStock(productId, addStockNumber.value).then(res => {
+    if (res.code === '000') {
+      ElMessage({
+        message: '添加库存成功!',
+        type: 'success',
+        center: true,
+      })
+      addStockNumber.value = null
+      getProductDetail()
+    } else if (res.code === '400') {
+      ElMessage({
+        message: res.msg,
+        type: 'error',
+        center: true,
+      })
+    }
+  })
+}
+
+function handleCreateOrder() {
+  const payload = {
+    productId: productId,
+    amount: amount.value,
+    type: type.value
+  };
+  createOrder(payload).then(res => {
+    if (res.code === '000') {
+      ElMessage({
+        message: '创建订单成功!',
+        type: 'success',
+        center: true,
+      })
+      getAvailableCouponList()
+      orderId.value = res.result.id
+      orderDialogVisible.value = true
+    } else if (res.code === '400') {
+      ElMessage({
+        message: res.msg,
+        type: 'error',
+        center: true,
+      })
+    }
+  })
+}
+
+const showForm = ref(false);
+
+function handleConfirmOrder() {
+  console.log(couponId.value)
+  payOrder(orderId.value, couponId.value).then(res => {
+    console.log(res)
+    const paymentWindow = window.open('', '_blank');
+    paymentWindow.document.write(res);
+    paymentWindow.document.forms[0].submit(); // 提交表单
+    showForm.value = false; // 隐藏表单
+    if (res.code === '000') {
+      ElMessage({
+        message: '订单支付成功!',
+        type: 'success',
+        center: true,
+      })
+      orderDialogVisible.value = false
+      amount.value = 1
+      type.value = ''
+      totalPrice.value = price.value
+    } else if (res.code === '400') {
+      ElMessage({
+        message: res.msg,
+        type: 'error',
+        center: true,
+      })
+    }
+  })
+}
+
+onMounted(() => {
+  showForm.value = true; // 组件加载后显示表单
+});
+
+function parseOrderType(type) {
+  if (type === "PICKUP") {
+    return "到店自提"
+  } else if (type === "DELIVERY") {
+    return "快递到家"
+  }
+}
+
+function handleCouponType(type) {
+  if (type === "FULL_REDUCTION") {
+    return "满减"
+  } else if (type === "SPECIAL") {
+    return "折扣"
+  }
+}
+
+// 处理优惠券
+interface Coupon {
+  id: number
+  groupId: number
+  type: string
+  satisfaction: number
+  minus: number
+}
+
+const availableCouponList = ref<Coupon[]>([])
+const currentRow = ref()
+const singleTableRef = ref<InstanceType<typeof ElTable>>()
+
+function getAvailableCouponList() {
+  getAvailableCouponsByStoreId(storeId.value).then(res => {
+    availableCouponList.value = res.result
+  })
+}
+
+const setCurrent = (row?: User) => {
+  singleTableRef.value!.setCurrentRow(row)
+}
+
+const handleCurrentChange = (val: Coupon | undefined) => {
+  if (val) {
+    couponId.value = val.id
+  } else {
+    couponId.value = 0
+  }
+  currentRow.value = val
+}
+
+function toBackPage() {
+  router.push("/storeDetail/" + storeId.value)
+}
+
+</script>
+
+<template>
+  <el-container>
+    <el-aside width="25%" class="page-aside">
+      <div class="product-detail-aside">
+        <el-button @click="toBackPage()"
+                   type="primary" circle plain class="back-button">
+          <el-icon>
+            <Back/>
+          </el-icon>
+        </el-button>
+
+        <el-image class="logo-image" :src="photoUrl"/>
+
+        <span class="product-title">{{ name }}</span>
+
+        <el-descriptions :column="1">
+          <el-descriptions-item style="font-size: 10px" label="品类">
+            {{ parseCategory(category) }}
+          </el-descriptions-item>
+
+          <el-descriptions-item style="font-size: 10px" label="价格">
+            {{ price }} 元
+          </el-descriptions-item>
+
+          <el-descriptions-item style="font-size: 10px" label="评分">
+            <el-rate
+                v-model="rating"
+                disabled
+                show-score
+                text-color="#ff9900"
+                score-template="{value} 分"
+                size="small"
+            />
+            (共 {{ number }} 人打分)
+          </el-descriptions-item>
+
+          <el-descriptions-item style="font-size: 10px" label="销量">
+            {{ salesAmount }} 件
+          </el-descriptions-item>
+
+          <el-descriptions-item style="font-size: 10px" label="库存">
+            {{ stock }} 件
+          </el-descriptions-item>
+        </el-descriptions>
+
+        <el-input class="add-stock-number-input" v-if="role === 'STAFF'"
+                  v-model="addStockNumber" placeholder="请输入添加库存数" />
+        <el-button class="add-stock-button" @click="AddStock" v-if="role === 'STAFF'"
+                   type="primary" plain>
+          新增库存
+        </el-button>
+      </div>
+    </el-aside>
+
+    <el-main>
+      <div>
+        <span class="buy-product-title">购买商品</span>
+      </div>
+      <el-form ref="form" v-if="showForm">
+      </el-form>
+      <el-form class="buy-form-main">
+        <el-form-item>
+          <label for="amount">购买数量:</label>
+          <el-input-number v-model="amount" :min="1" :max="stock + 10" @change="handleChange"/>
+        </el-form-item>
+        <el-form-item>
+          <label for="type">提货方式:</label>
+          <el-select id="type"
+                     v-model="type"
+                     placeholder="请选择"
+          >
+            <el-option value="PICKUP" label="到店自提"/>
+            <el-option value="DELIVERY" label="快递到家"/>
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <label for="discountPrice">折扣前总价:</label>
+          {{ totalPrice }} 元
+        </el-form-item>
+      </el-form>
+      <el-button class="buy-button"
+                 @click="handleCreateOrder">创建订单
+      </el-button>
+
+      <el-dialog v-model="orderDialogVisible">
+        <el-form>
+          <el-form-item>
+            <label for="amount">购买数量:</label>
+            {{ amount }} 件
+          </el-form-item>
+          <el-form-item>
+            <label for="discountPrice">折扣前总价:</label>
+            {{ totalPrice }} 元
+          </el-form-item>
+          <el-form-item>
+            <label for="type">提货方式:</label>
+            {{ parseOrderType(type) }}
+          </el-form-item>
+          <el-form-item>
+            <label for="type">优惠券:</label> <el-button @click="setCurrent()">不使用优惠券</el-button>
+            <el-table
+                ref="singleTableRef"
+                :data="availableCouponList"
+                highlight-current-row
+                style="width: 100%"
+                @current-change="handleCurrentChange"
+            >
+              <el-table-column type="index" width="50"/>
+              <el-table-column prop="type" label="类型" width="180">
+                <template #default="scope">
+                  {{ handleCouponType(scope.row.type) }}
+                </template>
+              </el-table-column>
+              <el-table-column prop="satisfaction" label="满(元)" width="180"/>
+              <el-table-column prop="minus" label="减(元)" width="180"/>
+            </el-table>
+          </el-form-item>
+        </el-form>
+        <el-button @click="handleConfirmOrder" type="primary" plain>确认支付</el-button>
+      </el-dialog>
+
+    </el-main>
+  </el-container>
+</template>
+
+<style scoped>
+.page-aside {
+  border-right: lightgrey solid 1px;
+}
+
+.back-button {
+  margin-top: 20px;
+  margin-bottom: 20px;
+}
+
+.product-title {
+  font-size: 30px;
+  margin-bottom: 20px;
+}
+
+.product-detail-aside {
+  width: 80%;
+  margin-left: 10%;
+  display: flex;
+  flex-direction: column;
+  justify-content: start;
+}
+
+.logo-image {
+  width: 100%;
+}
+
+.add-stock-number-input {
+  margin-top: 15px;
+  margin-bottom: 10px;
+}
+
+.add-stock-button {
+  margin-bottom: 40px;
+}
+
+.buy-product-title {
+  font-size: 30px;
+  margin-top: 20px;
+  margin-bottom: 20px;
+  margin-left: 20px;
+}
+
+.buy-form-main {
+  margin-top: 20px;
+  margin-left: 30px;
+}
+
+.buy-button {
+  margin-left: 30px;
+}
+</style>

+ 0 - 210
src/views/staff/CreateProduct.vue

@@ -1,210 +0,0 @@
-<script setup lang="ts">
-import {useRouter} from "vue-router";
-import {ref, computed} from "vue";
-import {createProduct} from "../../api/product.ts";
-import {uploadImage} from "../../api/image.ts";
-
-const router = useRouter();
-const storeId = router.currentRoute.value.params.storeId;
-const name = ref('')
-const category = ref('')
-const price = ref()
-const photoUrlList = ref([])
-
-const hasNameInput = computed(() => name.value != '')
-const hasCategoryInput = computed(() => category.value != '')
-const hasPriceInput = computed(() => price.value != '')
-const nameLegal = hasNameInput
-const categoryLegal = hasCategoryInput
-const priceLegal = hasPriceInput
-
-const isDragging = ref(false)
-const files = ref([])
-const fileInputRef = ref(null)
-
-function onChange() {
-  files.value.push([...fileInputRef.value.files][0])
-  let formData = new FormData();
-  formData.append('file', [...fileInputRef.value.files][0]);
-  uploadImage(formData).then(res => {
-    photoUrlList.value.push(res.result);
-  });
-}
-
-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 fileSrc = URL.createObjectURL(file);
-  setTimeout(() => {
-    URL.revokeObjectURL(fileSrc);
-  }, 1000);
-
-  return fileSrc;
-}
-
-function remove(i) {
-  files.value.splice(i, 1);
-  photoUrlList.value.pop();
-}
-
-const createDisabled = computed(() => {
-  return !(nameLegal.value && categoryLegal.value && priceLegal.value && files.value);
-})
-
-function handleCreateProduct() {
-  const payload = {
-    storeId: storeId,
-    name: name.value,
-    category: category.value,
-    price: price.value,
-    photoUrlList: photoUrlList.value
-  };
-  createProduct(payload).then(res => {
-    if (res.code === '000') {
-      ElMessage({
-        message: '添加商品成功!',
-        type: 'success',
-        center: true,
-      })
-      name.value = '';
-      category.value = '';
-      price.value = '';
-      photoUrlList.value = [];
-      files.value.splice(i, 1);
-    } 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">
-
-    <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 class="mb-0">
-            <label for="category" class="block mb-2 text-sm font-medium text-gray-900">品类</label>
-            <select id="category"
-                    v-model="category"
-                    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="FOOD">食品</option>
-              <option value="CLOTHES">服饰</option>
-              <option value="FURNITURE">家具</option>
-              <option value="ELECTRONICS">电子产品</option>
-              <option value="ENTERTAINMENT">娱乐</option>
-              <option value="SPORTS">体育产品</option>
-              <option value="LUXURY">奢侈品</option>
-            </select>
-          </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="price"
-                   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="logoUrl" class="inline-block mb-2 text-sm font-medium text-gray-900">商品图片</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>
-                      <el-image 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="handleCreateProduct()"
-                    :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>

+ 61 - 0
src/views/store/AllStore.vue

@@ -0,0 +1,61 @@
+<script setup lang="ts">
+
+import StoreItem from "../../components/StoreItem.vue"
+import {ref} from "vue"
+import {getAllStore} from "../../api/store.ts"
+import {useRouter} from "vue-router"
+
+const role = sessionStorage.getItem("role")
+const router = useRouter()
+
+let storeList = ref()
+
+getStoreList();
+
+function getStoreList() {
+  getAllStore().then(res => {
+    storeList.value = res.result;
+  })
+}
+
+function toCreateStorePage() {
+  router.push("/createStore");
+}
+
+function toStoreDetailPage(storeId: Number) {
+  router.push("/storeDetail/" + storeId);
+}
+
+</script>
+
+<template>
+  <el-main>
+    <div v-if="role === 'MANAGER' || role === 'CEO'">
+      <el-button class="add-store-button" type="primary" plain
+                 @click="toCreateStorePage()">
+        添加商店
+      </el-button>
+    </div>
+
+    <div class="store-item-list">
+      <StoreItem
+          v-for="storeVO in storeList" :storeId="storeVO.id"
+          @click="toStoreDetailPage(storeVO.id)" />
+    </div>
+  </el-main>
+
+</template>
+
+<style scoped>
+.add-store-button {
+
+}
+
+.store-item-list {
+  display: flex;
+  padding: 2px;
+  flex-flow: wrap;
+  justify-content: center;
+  align-content: start;
+}
+</style>

+ 136 - 0
src/views/store/CreateStore.vue

@@ -0,0 +1,136 @@
+<script setup lang="ts">
+import {computed, ref} from 'vue'
+import {createStore} from "../../api/store.ts"
+import {uploadImage} from "../../api/image.ts"
+import {router} from '../../router'
+import {Back, UploadFilled} from "@element-plus/icons-vue";
+
+// 输入框值
+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 imageFileList = ref([])
+
+const createDisabled = computed(() => {
+  return !(nameLegal.value && locationLegal.value && imageFileList.value);
+})
+
+function handleCreateStore() {
+  const payload = {
+    name: name.value,
+    location: location.value,
+    logoUrl: logoUrl.value
+  };
+  createStore(payload).then(res => {
+    if (res.code === '000') {
+      ElMessage({
+        message: '添加商户成功!',
+        type: 'success',
+        center: true,
+      })
+      name.value = '';
+      location.value = '';
+      logoUrl.value = '';
+      imageFileList.value.splice(0);
+    } else if (res.code === '400') {
+      ElMessage({
+        message: res.msg,
+        type: 'error',
+        center: true,
+      })
+    }
+  });
+}
+
+function toBackPage() {
+  router.push("/allStore")
+}
+
+function handleChange(file, fileList) {
+  imageFileList.value = fileList
+  let formData = new FormData();
+  formData.append('file', file.raw);
+  uploadImage(formData).then(res => {
+    logoUrl.value = res.result;
+  });
+  console.log(imageFileList.value)
+}
+
+function handleExceed() {
+  ElMessage.warning(`当前限制选择 1 个文件`);
+}
+
+function uploadHttpRequest() {
+}
+</script>
+
+<template>
+  <el-main class="main-frame">
+    <div>
+      <el-button @click="toBackPage()"
+                 type="primary" circle plain>
+        <el-icon><Back/></el-icon>
+      </el-button>
+
+      <h1>新建商店</h1>
+
+      <el-form label-position="left" label-width="90px" size="large" class="create-store-form">
+
+        <el-form-item label="商店名">
+          <el-input id="name" v-model="name"
+                    required
+                    placeholder="请输入商店名"/>
+        </el-form-item>
+
+        <el-form-item label="商店地址">
+          <el-input id="location" v-model="location"
+                    required
+                    placeholder="楼层-门牌号 如:3楼-305"/>
+        </el-form-item>
+
+        <el-form-item label="商店Logo">
+          <el-upload
+              v-model:file-list="imageFileList"
+              :limit="1"
+              :on-change="handleChange"
+              :on-exceed="handleExceed"
+              :on-remove="handleChange"
+              class="upload-demo"
+              list-type="picture"
+              :http-request="uploadHttpRequest"
+              drag>
+            <el-icon class="el-icon--upload">
+              <upload-filled/>
+            </el-icon>
+            <div class="el-upload__text">
+              将文件拖到此处或单击此处上传。
+            </div>
+          </el-upload>
+
+        </el-form-item>
+
+        <el-form-item>
+          <el-button @click.prevent="handleCreateStore()" :disabled="createDisabled"
+                     type="primary" plain>
+            创建商店
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+  </el-main>
+</template>
+
+<style scoped>
+.create-store-form {
+  margin-left: 30px;
+  width: 50%;
+}
+</style>

+ 45 - 22
src/views/portal/StoreDetail.vue → src/views/store/StoreDetail.vue

@@ -4,9 +4,11 @@ import {getStoreById} from "../../api/store.ts";
 import {useRouter} from "vue-router";
 import {useRouter} from "vue-router";
 import {ref} from "vue";
 import {ref} from "vue";
 import {getProductsByStoreId} from "../../api/product.ts";
 import {getProductsByStoreId} from "../../api/product.ts";
-import ProductItem from "../common/ProductItem.vue";
+import ProductItem from "../../components/ProductItem.vue";
+import {Back} from "@element-plus/icons-vue";
 
 
 const router = useRouter();
 const router = useRouter();
+const role = sessionStorage.getItem("role")
 const storeId = router.currentRoute.value.params.storeId;
 const storeId = router.currentRoute.value.params.storeId;
 const storeVO = ref()
 const storeVO = ref()
 const name = ref('')
 const name = ref('')
@@ -30,8 +32,8 @@ function getStoreDetail() {
   })
   })
 }
 }
 
 
-function toCreateProduct() {
-  router.push("/staff/createProduct/" + storeId)
+function toCreateProductPage() {
+  router.push("/createProduct/" + storeId)
 }
 }
 
 
 function getAllProduct() {
 function getAllProduct() {
@@ -43,28 +45,28 @@ function getAllProduct() {
 function toProductDetailPage(productId) {
 function toProductDetailPage(productId) {
   router.push("/productDetail/" + productId);
   router.push("/productDetail/" + productId);
 }
 }
+
+function toBackPage() {
+  router.push("/allStore")
+}
 </script>
 </script>
 
 
 <template>
 <template>
   <el-container>
   <el-container>
     <el-aside width="25%" class="page-aside">
     <el-aside width="25%" class="page-aside">
       <div class="store-detail-main">
       <div class="store-detail-main">
-
-        <el-button class="create-product-button" type="primary" plain>
-          返回
+        <el-button @click="toBackPage()"
+                   type="primary" circle plain class="back-button">
+          <el-icon>
+            <Back/>
+          </el-icon>
         </el-button>
         </el-button>
 
 
         <el-image class="logo-image" :src="logoUrl"/>
         <el-image class="logo-image" :src="logoUrl"/>
 
 
-        <span style="font-size: 30px">
-          {{ name }}
-        </span>
-
+        <span class="store-title">{{ name }}</span>
 
 
         <el-descriptions :column="1">
         <el-descriptions :column="1">
-          <el-descriptions-item style="font-size: 10px" label="评分人数">
-            {{ number }}
-          </el-descriptions-item>
           <el-descriptions-item style="font-size: 10px" label="评分">
           <el-descriptions-item style="font-size: 10px" label="评分">
             <el-rate
             <el-rate
                 v-model="rating"
                 v-model="rating"
@@ -74,24 +76,28 @@ function toProductDetailPage(productId) {
                 score-template="{value} 分"
                 score-template="{value} 分"
                 size="small"
                 size="small"
             />
             />
+            (共 {{ number }} 人打分)
           </el-descriptions-item>
           </el-descriptions-item>
           <el-descriptions-item style="font-size: 10px" label="地址">
           <el-descriptions-item style="font-size: 10px" label="地址">
             {{ location }}
             {{ location }}
           </el-descriptions-item>
           </el-descriptions-item>
         </el-descriptions>
         </el-descriptions>
 
 
-
         <el-button class="create-product-button" type="primary" plain
         <el-button class="create-product-button" type="primary" plain
-                   @click="toCreateProduct()">新增商品
+                   @click="toCreateProductPage()" v-if="role === 'STAFF' || role === 'MANAGER'">新增商品
         </el-button>
         </el-button>
-
       </div>
       </div>
     </el-aside>
     </el-aside>
 
 
-    <el-main class="all-product-main">
-      <productItem class="product-item-list"
-                   v-for="productVO in productList" :productId="productVO.id"
-                   @click="toProductDetailPage(productVO.id)" />
+    <el-main>
+      <div>
+        <span class="product-list-title">商品列表</span>
+      </div>
+      <div class="all-product-main">
+        <productItem class="product-item-list"
+                     v-for="productVO in productList" :productId="productVO.id"
+                     @click="toProductDetailPage(productVO.id)"/>
+      </div>
     </el-main>
     </el-main>
   </el-container>
   </el-container>
 
 
@@ -102,9 +108,19 @@ function toProductDetailPage(productId) {
   border-right: lightgrey solid 1px;
   border-right: lightgrey solid 1px;
 }
 }
 
 
+.back-button {
+  margin-top: 20px;
+  margin-bottom: 20px;
+}
+
+.store-title {
+  font-size: 30px;
+  margin-bottom: 20px;
+}
+
 .store-detail-main {
 .store-detail-main {
-  height: 100%;
-  width: 100%;
+  width: 80%;
+  margin-left: 10%;
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
   justify-content: start;
   justify-content: start;
@@ -118,6 +134,13 @@ function toProductDetailPage(productId) {
   margin-top: 20px;
   margin-top: 20px;
 }
 }
 
 
+.product-list-title {
+  font-size: 30px;
+  margin-top: 20px;
+  margin-bottom: 20px;
+  margin-left: 20px;
+}
+
 .all-product-main {
 .all-product-main {
   display: flex;
   display: flex;
   flex-direction: row;
   flex-direction: row;

+ 16 - 21
src/views/portal/Dashboard.vue → src/views/user/Dashboard.vue

@@ -4,6 +4,7 @@ import {userInfo, userInfoUpdate} from '../../api/user.ts'
 import {router} from '../../router'
 import {router} from '../../router'
 import {UserFilled} from "@element-plus/icons-vue";
 import {UserFilled} from "@element-plus/icons-vue";
 
 
+const role = sessionStorage.getItem("role")
 const name = ref('')
 const name = ref('')
 const tel = ref('')
 const tel = ref('')
 const address = ref('')
 const address = ref('')
@@ -18,20 +19,12 @@ const hasPasswordInput = computed(() => password.value != '');
 const hasConfirmPasswordInput = computed(() => confirmPassword.value != '');
 const hasConfirmPasswordInput = computed(() => confirmPassword.value != '');
 const isPasswordIdentical = computed(() => password.value == confirmPassword.value);
 const isPasswordIdentical = computed(() => password.value == confirmPassword.value);
 
 
-const isCustomer = ref('true')
-const isStaff = ref('false')
-
 userInfo()
 userInfo()
     .then(res => {
     .then(res => {
-      console.log(res)
-
       name.value = res.result.name
       name.value = res.result.name
       tel.value = res.result.phone
       tel.value = res.result.phone
       address.value = res.result.address
       address.value = res.result.address
       regTime.value = parseTime(res.result.createTime)
       regTime.value = parseTime(res.result.createTime)
-
-      isCustomer.value = (res.result.role == 'CUSTOMER')
-      isStaff.value = (res.result.role == 'STAFF')
     })
     })
     .catch(e => {
     .catch(e => {
       console.error(e.message)
       console.error(e.message)
@@ -108,6 +101,18 @@ function updatePassword() {
       })
       })
 }
 }
 
 
+function parseRole() {
+  if (role === 'MANAGER') {
+    return "管理员"
+  } else if (role === 'CUSTOMER') {
+    return "顾客"
+  } else if (role === 'STAFF') {
+    return "商家"
+  } else if (role === 'CEO') {
+    return "CEO"
+  }
+}
+
 function parseTime(time) {
 function parseTime(time) {
   let times = time.split(/T|\./)
   let times = time.split(/T|\./)
   return times[0] + " " + times[1]
   return times[0] + " " + times[1]
@@ -139,30 +144,24 @@ function parseTime(time) {
         </template>
         </template>
 
 
         <el-descriptions-item label="身份">
         <el-descriptions-item label="身份">
-          <el-tag v-if="isCustomer">顾客</el-tag>
-          <el-tag v-else-if="isStaff">工作人员</el-tag>
+          <el-tag>{{ parseRole() }}</el-tag>
         </el-descriptions-item>
         </el-descriptions-item>
 
 
         <el-descriptions-item label="联系电话">
         <el-descriptions-item label="联系电话">
           {{ tel }}
           {{ tel }}
         </el-descriptions-item>
         </el-descriptions-item>
 
 
-        <el-descriptions-item label="地址">
+        <el-descriptions-item label="地址" v-if="role === 'CUSTOMER' || role === 'STAFF'">
           {{ address }}
           {{ address }}
         </el-descriptions-item>
         </el-descriptions-item>
 
 
         <el-descriptions-item label="注册时间">
         <el-descriptions-item label="注册时间">
           {{ regTime }}
           {{ regTime }}
         </el-descriptions-item>
         </el-descriptions-item>
-
       </el-descriptions>
       </el-descriptions>
-
-
     </el-card>
     </el-card>
 
 
-
     <el-card v-if="displayInfoCard" class="change-card">
     <el-card v-if="displayInfoCard" class="change-card">
-
       <template #header>
       <template #header>
         <div class="card-header">
         <div class="card-header">
           <span>个人信息</span>
           <span>个人信息</span>
@@ -170,7 +169,6 @@ function parseTime(time) {
         </div>
         </div>
       </template>
       </template>
 
 
-
       <el-form>
       <el-form>
         <el-form-item>
         <el-form-item>
           <label for="name">昵称</label>
           <label for="name">昵称</label>
@@ -182,19 +180,16 @@ function parseTime(time) {
           <el-input id="phone" v-model="tel" disabled/>
           <el-input id="phone" v-model="tel" disabled/>
         </el-form-item>
         </el-form-item>
 
 
-        <el-form-item>
+        <el-form-item v-if="role === 'CUSTOMER' || role === 'STAFF'">
           <label for="address">收货地址</label>
           <label for="address">收货地址</label>
           <el-input id="address" type="textarea"
           <el-input id="address" type="textarea"
                     rows="4"
                     rows="4"
                     v-model="address" placeholder="中华门"></el-input>
                     v-model="address" placeholder="中华门"></el-input>
-
         </el-form-item>
         </el-form-item>
       </el-form>
       </el-form>
     </el-card>
     </el-card>
 
 
-
     <el-card v-if="!displayInfoCard" class="change-card">
     <el-card v-if="!displayInfoCard" class="change-card">
-
       <template #header>
       <template #header>
         <div class="card-header">
         <div class="card-header">
           <span>修改密码</span>
           <span>修改密码</span>

+ 151 - 0
src/views/user/Login.vue

@@ -0,0 +1,151 @@
+<script setup lang="ts">
+import {ElForm, ElFormItem} from "element-plus";
+import {ref, computed} from 'vue';
+import {router} from '../../router'
+import {userInfo, userLogin} from "../../api/user.ts";
+
+// 输入框值
+const tel = ref('')
+const password = ref('')
+
+// 用于前端阻拦不合法输入
+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 => {
+        const token = res.result
+        sessionStorage.setItem('token', token)
+
+        userInfo().then(res => {
+          sessionStorage.setItem('name', res.result.name)
+          sessionStorage.setItem('role', res.result.role)
+          router.push({path: "/allStore"})
+        }).catch(e => {
+          console.log(e.message)
+          ElMessageBox.alert(
+              e.message,
+              '错误提示',
+              {
+                confirmButtonText: '好的',
+                type: "error",
+                showClose: false,
+                roundButton: true,
+                center: true
+              }
+          ).then(() => {
+            // 清空用户密码
+            password.value = ''
+          })
+        })
+
+      })
+      .catch(e => {
+        console.log(e.message)
+        ElMessageBox.alert(
+            e.message,
+            '错误提示',
+            {
+              confirmButtonText: '好的',
+              type: "error",
+              showClose: false,
+              roundButton: true,
+              center: true
+            }
+        ).then(() => {
+          // 清空用户密码
+          password.value = ''
+        })
+      })
+}
+
+</script>
+
+
+<template>
+  <el-main class="main-frame bgimage">
+    <el-card class="login-card">
+      <div>
+        <h1>登入您的账户</h1>
+        <el-form>
+          <el-form-item>
+            <label v-if="!hasTelInput" for="tel">注册手机号</label>
+            <label v-else-if="!telLegal" for="tel" class="error-warn">手机号不合法</label>
+            <label v-else for="tel">注册手机号</label>
+            <el-input id="tel" type="text" v-model="tel"
+                      required :class="{'error-warn-input' :(hasTelInput && !telLegal)}"
+                      placeholder="请输入手机号"/>
+          </el-form-item>
+
+          <el-form-item>
+            <label for="password">账户密码</label>
+            <el-input id="password" type="password" v-model="password"
+                      required
+                      placeholder="••••••••"/>
+          </el-form-item>
+
+          <span class="button-group">
+              <el-button @click.prevent="handleLogin" :disabled="loginDisabled"
+                         type="primary">登入</el-button>
+              <router-link to="/register" v-slot="{navigate}">
+                <el-button @click="navigate">去注册</el-button>
+              </router-link>
+          </span>
+        </el-form>
+      </div>
+    </el-card>
+  </el-main>
+</template>
+
+<style scoped>
+.bgimage {
+  background-image: url("../../assets/shopping-1s-1084px.svg");
+}
+
+.main-frame {
+  width: 100%;
+  height: 100%;
+
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.login-card {
+  width: 60%;
+  padding: 10px;
+}
+
+.error-warn {
+  color: red;
+}
+
+.error-warn-input {
+  --el-input-focus-border-color: red;
+}
+
+.button-group {
+  padding-top: 10px;
+  display: flex;
+  flex-direction: row;
+  gap: 30px;
+  align-items: center;
+  justify-content: right;
+}
+</style>

+ 21 - 0
src/views/user/NotFound.vue

@@ -0,0 +1,21 @@
+<script setup lang="ts">
+
+</script>
+
+<template>
+  <el-result
+      icon="error"
+      title="错误"
+      sub-title="您访问的页面不存在"
+    >
+    <template #extra>
+      <router-link to="/home">
+        <el-button type="primary">回到主页</el-button>
+      </router-link>
+    </template>
+  </el-result>
+</template>
+
+<style scoped>
+
+</style>

+ 5 - 37
src/components/Register.vue → src/views/user/Register.vue

@@ -1,8 +1,7 @@
 <script setup lang="ts">
 <script setup lang="ts">
 import {ref, computed} from 'vue';
 import {ref, computed} from 'vue';
-import {router} from '../router'
-import {userRegister} from "../api/user.ts";
-
+import {router} from '../../router'
+import {userRegister} from "../../api/user.ts";
 
 
 const name = ref('')
 const name = ref('')
 const identity = ref('')
 const identity = ref('')
@@ -11,8 +10,6 @@ const address = ref('')
 const password = ref('')
 const password = ref('')
 const confirmPassword = 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 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 hasTelInput = computed(() => tel.value != '');
@@ -30,7 +27,6 @@ const registerDisabled = computed(() => {
   return !(telLegal.value && hasPasswordInput.value && isPasswordIdentical.value && hasAddressInput.value && hasIdentityChosen.value)
   return !(telLegal.value && hasPasswordInput.value && isPasswordIdentical.value && hasAddressInput.value && hasIdentityChosen.value)
 });
 });
 
 
-
 async function handleRegister() {
 async function handleRegister() {
   const payload = {
   const payload = {
     role: identity.value,
     role: identity.value,
@@ -78,21 +74,13 @@ async function handleRegister() {
 </script>
 </script>
 
 
 <template>
 <template>
-
   <el-main class="main-frame bgimage">
   <el-main class="main-frame bgimage">
-
     <el-card class="login-card">
     <el-card class="login-card">
-
       <div>
       <div>
-
-        <h1>
-          创建一个新的账户
-        </h1>
+        <h1>创建一个新的账户</h1>
 
 
         <el-form>
         <el-form>
-
           <el-row>
           <el-row>
-
             <el-col :span="15">
             <el-col :span="15">
               <el-form-item>
               <el-form-item>
                 <label for="name">昵称</label>
                 <label for="name">昵称</label>
@@ -119,7 +107,6 @@ async function handleRegister() {
             </el-col>
             </el-col>
           </el-row>
           </el-row>
 
 
-
           <el-row>
           <el-row>
             <el-col :span="8">
             <el-col :span="8">
               <el-form-item>
               <el-form-item>
@@ -148,7 +135,6 @@ async function handleRegister() {
                 <label for="address">
                 <label for="address">
                   地址
                   地址
                 </label>
                 </label>
-
                 <el-input id="address"
                 <el-input id="address"
                           v-model="address"
                           v-model="address"
                           placeholder="请输入地址"/>
                           placeholder="请输入地址"/>
@@ -157,7 +143,6 @@ async function handleRegister() {
 
 
           </el-row>
           </el-row>
 
 
-
           <el-form-item>
           <el-form-item>
             <label for="password">密码</label>
             <label for="password">密码</label>
             <el-input type="password" id="password"
             <el-input type="password" id="password"
@@ -176,23 +161,6 @@ async function handleRegister() {
                       placeholder="••••••••"/>
                       placeholder="••••••••"/>
           </el-form-item>
           </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>-->
-
-
           <span class="button-group">
           <span class="button-group">
             <el-button @click.prevent="handleRegister"
             <el-button @click.prevent="handleRegister"
                        :disabled="registerDisabled"
                        :disabled="registerDisabled"
@@ -202,7 +170,7 @@ async function handleRegister() {
 
 
             <router-link to="/login" v-slot="{navigate}">
             <router-link to="/login" v-slot="{navigate}">
               <el-button @click="navigate">
               <el-button @click="navigate">
-                返回登录
+                登录
               </el-button>
               </el-button>
             </router-link>
             </router-link>
           </span>
           </span>
@@ -219,7 +187,7 @@ async function handleRegister() {
 <style scoped>
 <style scoped>
 
 
 .bgimage {
 .bgimage {
-  background-image: url("../assets/shopping-1s-1084px.svg");
+  background-image: url("../../assets/shopping-1s-1084px.svg");
 }
 }
 
 
 .main-frame {
 .main-frame {