Browse Source

添加商城

lex-xin 2 years ago
parent
commit
7a34a3d0b8

+ 12 - 0
src/helpers/utils.ts

@@ -1,3 +1,5 @@
+import dayjs from 'dayjs'
+import numeral from 'numeral'
 import { Toast } from 'vant'
 import { state as helpState } from './helpState'
 
@@ -109,3 +111,13 @@ export const formatterDate = (type: string, val: any) => {
   }
   return val
 }
+
+export const moneyFormat = (value: number) => {
+  return numeral(value).format('0,0.00')
+}
+export const dateFormat = (
+  value: string | Date,
+  format = 'YYYY-MM-DD HH:mm:ss'
+) => {
+  return dayjs(value).format(format)
+}

+ 8 - 0
src/router/routes-common.ts

@@ -95,6 +95,14 @@ export const router = [
     meta: {
       title: '商城'
     }
+  },
+  {
+    path: '/goodsDetail',
+    name: 'goodsDetail',
+    component: () => import('@/views/shop-mall/goods-detail'),
+    meta: {
+      title: '商品详情'
+    }
   }
 ]
 

+ 2 - 0
src/views/shop-mall/components/goods/index.module.less

@@ -3,6 +3,8 @@
   background: #ffffff;
   border-radius: 8px;
   overflow: hidden;
+
+  --van-image-placeholder-background-color: #f1f1f1;
 }
 
 .goodsImg {

+ 29 - 12
src/views/shop-mall/components/goods/index.tsx

@@ -3,26 +3,43 @@ import { defineComponent } from 'vue'
 import styles from './index.module.less'
 
 import iconAddCart from '../../images/icon-add-cart.png'
+import { moneyFormat } from '@/helpers/utils'
 
 export default defineComponent({
   name: 'goods',
+  props: {
+    item: {
+      type: Object,
+      default: {}
+    },
+    onItemClick: {
+      type: Function,
+      default: (item: any) => {}
+    },
+    onBuyClick: {
+      type: Function,
+      default: (item: any) => {}
+    }
+  },
   render() {
+    const item = this.item
     return (
-      <div class={styles.goods}>
-        <Image
-          src="https://daya.ks3-cn-beijing.ksyun.com/202204/T3unJeA.png"
-          fit="cover"
-          class={styles.goodsImg}
-        />
+      <div class={styles.goods} onClick={() => this.onItemClick(item)}>
+        <Image src={item.pic} fit="cover" class={styles.goodsImg} />
         <div class={styles.goodsInfo}>
-          <div class={[styles.goodsName, 'van-ellipsis']}>
-            圆号 S.E.SHIRES圆号 S.E.SHIRES
-          </div>
+          <div class={[styles.goodsName, 'van-ellipsis']}>{item.name}</div>
           <div class={styles.goodsBuy}>
-            <p class={styles.goodsSale}>已售152</p>
-            <p class={styles.goodsPrice}>¥21800</p>
+            <del class={styles.goodsSale}>
+              ¥{moneyFormat(item.originalPrice)}
+            </del>
+            <p class={styles.goodsPrice}>¥{moneyFormat(item.price)}</p>
 
-            <Icon class={styles.addCart} name={iconAddCart} size={22} />
+            <Icon
+              class={styles.addCart}
+              name={iconAddCart}
+              size={22}
+              onClick={() => this.onBuyClick(item)}
+            />
           </div>
         </div>
       </div>

+ 15 - 21
src/views/shop-mall/components/hot-goods/index.tsx

@@ -10,6 +10,12 @@ export const getAssetsHomeFile = (fileName: string) => {
 
 export default defineComponent({
   name: 'hot-goods',
+  props: {
+    hotProductList: {
+      type: Array,
+      default: []
+    }
+  },
   render() {
     return (
       <div class={styles.hotGoods}>
@@ -36,27 +42,15 @@ export default defineComponent({
           </Col>
         </Row>
         <div class={styles.hotGoodsSection}>
-          <div class={styles.hotGoodsItem}>
-            <Image
-              class={styles.hotGoodsItemImg}
-              src="https://ks3-cn-beijing.ksyuncs.com/daya/1651803185953%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20220506100738.jpg"
-              fit="cover"
-            />
-          </div>
-          <div class={styles.hotGoodsItem}>
-            <Image
-              class={styles.hotGoodsItemImg}
-              src="https://ks3-cn-beijing.ksyuncs.com/daya/1651803185953%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20220506100738.jpg"
-              fit="cover"
-            />
-          </div>
-          <div class={styles.hotGoodsItem}>
-            <Image
-              class={styles.hotGoodsItemImg}
-              src="https://ks3-cn-beijing.ksyuncs.com/daya/1651803185953%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20220506100738.jpg"
-              fit="cover"
-            />
-          </div>
+          {this.hotProductList.map((item: any) => (
+            <div class={styles.hotGoodsItem}>
+              <Image
+                class={styles.hotGoodsItemImg}
+                src={item.pic}
+                fit="cover"
+              />
+            </div>
+          ))}
         </div>
       </div>
     )

+ 5 - 2
src/views/shop-mall/components/menu-list/index.module.less

@@ -1,6 +1,6 @@
 .swipeType {
   margin: 14px 14px 0;
-  padding-bottom: 30px;
+  padding-bottom: 14px;
   :global {
     .van-swipe__indicators {
       bottom: 12px;
@@ -16,6 +16,9 @@
     }
   }
 }
+.swipeTypeShow {
+  padding-bottom: 30px;
+}
 
 .typeSection {
   display: flex;
@@ -23,7 +26,7 @@
 }
 
 .typeItem {
-  width: 25%;
+  width: 20%;
   text-align: center;
   .swipeTypeImg {
     width: 47px;

+ 25 - 25
src/views/shop-mall/components/menu-list/index.tsx

@@ -4,37 +4,37 @@ import styles from './index.module.less'
 
 export default defineComponent({
   name: 'menu-list',
+  props: {
+    productList: {
+      type: Array,
+      default: []
+    }
+  },
   render() {
     return (
       <Swipe
-        class={styles.swipeType}
+        class={[
+          styles.swipeType,
+          this.productList.length > 1 && styles.swipeTypeShow
+        ]}
         indicator-color="var(--van-primary)"
         loop={false}
+        showIndicators={this.productList.length > 1}
       >
-        <SwipeItem class={styles.typeSection}>
-          {['美食', '甜点饮品', '鲜花', '美容美发', '家居生活'].map(item => (
-            <div class={styles.typeItem}>
-              <Image
-                class={styles.swipeTypeImg}
-                src={'https://daya.ks3-cn-beijing.ksyun.com/202204/T3unJeA.png'}
-                fit="cover"
-              />
-              <p class={styles.typeName}>{item}</p>
-            </div>
-          ))}
-        </SwipeItem>
-        <SwipeItem class={styles.typeSection}>
-          {['美食', '甜点饮品', '鲜花', '美容美发', '家居生活'].map(item => (
-            <div class={styles.typeItem}>
-              <Image
-                class={styles.swipeTypeImg}
-                src={'https://daya.ks3-cn-beijing.ksyun.com/202204/T3unJeA.png'}
-                fit="cover"
-              />
-              <p class={styles.typeName}>{item}</p>
-            </div>
-          ))}
-        </SwipeItem>
+        {this.productList.map((product: any) => (
+          <SwipeItem class={styles.typeSection}>
+            {product.map((item: any) => (
+              <div class={styles.typeItem}>
+                <Image
+                  class={styles.swipeTypeImg}
+                  src={item.icon}
+                  fit="cover"
+                />
+                <p class={styles.typeName}>{item.name}</p>
+              </div>
+            ))}
+          </SwipeItem>
+        ))}
       </Swipe>
     )
   }

+ 63 - 36
src/views/shop-mall/components/tab-list/index.tsx

@@ -1,12 +1,19 @@
 import ColResult from '@/components/col-result'
 import request from '@/helpers/request'
-import { List, Tab, Tabs } from 'vant'
+import { List, Popup } from 'vant'
 import { defineComponent } from 'vue'
+import AddGoodsCart from '../../modal/add-goods-cart'
 import Goods from '../goods'
 import styles from './index.module.less'
 
 export default defineComponent({
   name: 'tab-list',
+  props: {
+    typeId: {
+      type: Number,
+      default: 0
+    }
+  },
   data() {
     return {
       list: [],
@@ -14,49 +21,54 @@ export default defineComponent({
       loading: false,
       finished: false,
       params: {
-        page: 1,
-        rows: 20
-      }
+        productCategoryId: null as any,
+        pageNum: 1,
+        pageSize: 20
+      },
+      addGoodsShow: false,
+      selectGoodsItem: {} as any
     }
   },
   mounted() {
-    // this.getList()
+    this.getList()
   },
   methods: {
-    onSearch(value: string) {
+    onSearch() {
       this.dataShow = true
       this.loading = false
       this.finished = false
       this.list = []
-      this.params.page = 1
+      this.params.pageNum = 1
       this.getList()
     },
     async getList() {
-      // try {
-      //   let params = this.params
-      //   const res: any = { data: {} }
-      //   // await request.post(
-      //   //   '/api-student/courseGroup/queryPageCourseGroup',
-      //   //   {
-      //   //     data: {
-      //   //       ...params
-      //   //     }
-      //   //   }
-      //   // )
-      //   this.loading = false
-      //   const result = res.data || {}
-      //   // 处理重复请求数据
-      //   if (this.list.length > 0 && result.pageNo === 1) {
-      //     return
-      //   }
-      //   this.list = this.list.concat(result.rows || [])
-      //   this.finished = result.pageNo >= result.totalPage
-      //   this.params.page = result.pageNo + 1
-      //   this.dataShow = this.list.length > 0
-      // } catch {
-      //   this.dataShow = false
-      //   this.finished = true
-      // }
+      try {
+        let params = this.params
+        this.typeId && (params.productCategoryId = this.typeId)
+        const res = await request.post('/api-mall-portal/product/search', {
+          data: {
+            ...params
+          }
+        })
+        this.loading = false
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (this.list.length > 0 && result.pageNum === 1) {
+          return
+        }
+        this.list = this.list.concat(result.list || [])
+        this.finished = result.pageNum >= result.totalPage
+        this.params.pageNum = result.pageNum + 1
+        this.dataShow = this.list.length > 0
+      } catch {
+        this.dataShow = false
+        this.finished = true
+      }
+    },
+    onDetailClick(item: any) {},
+    onBuyClick(item: any) {
+      this.selectGoodsItem = item
+      this.addGoodsShow = true
     }
   },
   render() {
@@ -71,15 +83,30 @@ export default defineComponent({
             class={[styles.goodsList, 'mb12']}
             onLoad={this.getList}
           >
-            {/* {this.list.map((item: any) => (
-            ))} */}
-            {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((item: any) => (
-              <Goods style={{ marginTop: '12px' }} />
+            {this.list.map((item: any) => (
+              <Goods
+                style={{ marginTop: '12px' }}
+                item={item}
+                onItemClick={this.onDetailClick}
+                onBuyClick={this.onBuyClick}
+              />
             ))}
           </List>
         ) : (
           <ColResult btnStatus={false} classImgSize="SMALL" tips="暂无商品" />
         )}
+
+        <Popup
+          show={this.addGoodsShow}
+          closeable
+          position="bottom"
+          round
+          onClose={() => {
+            this.addGoodsShow = false
+          }}
+        >
+          <AddGoodsCart item={this.selectGoodsItem} />
+        </Popup>
       </div>
     )
   }

+ 0 - 0
src/views/shop-mall/goods-detail/index.module.less


+ 8 - 0
src/views/shop-mall/goods-detail/index.tsx

@@ -0,0 +1,8 @@
+import { defineComponent } from 'vue'
+
+export default defineComponent({
+  name: 'goods-detail',
+  render() {
+    return <>商品详情</>
+  }
+})

+ 44 - 42
src/views/shop-mall/index.tsx

@@ -7,17 +7,40 @@ import TabList from './components/tab-list'
 import styles from './index.module.less'
 import { useElementSize } from '@vueuse/core'
 import iconShopCart from './images/icon-shop-cart.png'
+import request from '@/helpers/request'
 
 export default defineComponent({
   name: 'shop-mall',
   data() {
     return {
-      height: 'auto' as any
+      height: 'auto' as any,
+      count: 0, // 购买车数量
+      advertiseList: [], // 广告列表
+      productList: [], // 商品分类
+      hotProductList: [], // 热门商品列表
+      productCategoryList: [] // 商品分类列表
     }
   },
-  mounted() {
+  async mounted() {
     const { height } = useElementSize((this as any).$refs.headers)
     this.height = height
+
+    try {
+      const res = await request.get('/api-mall-portal/home/content')
+      console.log(res)
+      const result = res.data || {}
+      this.count = result.count
+      this.advertiseList = result.advertiseList
+      const category = result.productCategoryList || []
+      const categoryResult: any = []
+      while (category.length > 0) {
+        const chunk = category.splice(0, 5)
+        categoryResult.push(chunk)
+      }
+      this.productList = categoryResult
+      this.hotProductList = result.hotProductList || []
+      this.productCategoryList = result.productAttributeCategoryList || []
+    } catch {}
   },
   methods: {
     onSearch() {}
@@ -32,7 +55,9 @@ export default defineComponent({
             v-slots={{
               right: () => (
                 <div class={styles['icon-shop-cart']}>
-                  <span class={styles.dot}>12</span>
+                  {this.count > 0 && (
+                    <span class={styles.dot}>{this.count}</span>
+                  )}
                   <Icon name={iconShopCart} size={24} />
                 </div>
               )
@@ -45,25 +70,18 @@ export default defineComponent({
           showIndicators={false}
           lazyRender
         >
-          <SwipeItem>
-            <Image
-              class={styles.swipeItemImg}
-              src={'https://daya.ks3-cn-beijing.ksyun.com/202204/T3unJeA.png'}
-              fit="cover"
-            />
-          </SwipeItem>
-          <SwipeItem>
-            <Image
-              class={styles.swipeItemImg}
-              src={'https://daya.ks3-cn-beijing.ksyun.com/202204/T3unJeA.png'}
-              fit="cover"
-            />
-          </SwipeItem>
+          {this.advertiseList.map((item: any) => (
+            <SwipeItem>
+              <Image class={styles.swipeItemImg} src={item.pic} fit="cover" />
+            </SwipeItem>
+          ))}
         </Swipe>
 
-        <MenuList />
+        <MenuList productList={this.productList} />
 
-        <HotGoods />
+        {this.hotProductList.length === 3 && (
+          <HotGoods hotProductList={this.hotProductList} />
+        )}
 
         <Tabs
           shrink
@@ -75,30 +93,14 @@ export default defineComponent({
           offsetTop={this.height}
           lazyRender
         >
-          <Tab title="全部商品">
-            <TabList />
-          </Tab>
-          <Tab title="全部商品">
-            <TabList />
-          </Tab>
-          <Tab title="全部商品">
-            <TabList />
-          </Tab>
-          <Tab title="全部商品">
-            <TabList />
-          </Tab>
-          <Tab title="全部商品">
-            <TabList />
-          </Tab>
-          <Tab title="全部商品">
-            <TabList />
-          </Tab>
-          <Tab title="全部商品">
-            <TabList />
-          </Tab>
-          <Tab title="全部商品">
-            <TabList />
+          <Tab title="全部分类" name={0}>
+            <TabList typeId={0} />
           </Tab>
+          {this.productCategoryList.map((item: any) => (
+            <Tab title={item.name} name={item.id}>
+              <TabList typeId={item.id} />
+            </Tab>
+          ))}
         </Tabs>
       </div>
     )

+ 73 - 0
src/views/shop-mall/modal/add-goods-cart/index.module.less

@@ -0,0 +1,73 @@
+.addGoodsCart {
+  padding-top: 12px;
+
+  --van-stepper-button-round-theme-color: var(--van-primary);
+
+  :global {
+    .van-stepper__input {
+      background: #f7f8f9 !important;
+      border-radius: 6px;
+      margin: 0 8px;
+    }
+    .van-stepper__minus--disabled {
+      opacity: 0.6 !important;
+      color: #333 !important;
+      background: #f7f8f9 !important;
+      border-color: #f7f8f9 !important;
+    }
+  }
+}
+.goodsImg {
+  width: 100px;
+  height: 100px;
+  background: linear-gradient(180deg, #f0f0f0 0%, #d7d7d7 100%);
+  border-radius: 8px;
+  overflow: hidden;
+}
+
+.goodsPrice {
+  padding-top: 8px;
+  font-size: 18px;
+  color: #ff4e19;
+  line-height: 22px;
+  span {
+    font-size: 16px;
+  }
+}
+.goodsStore {
+  font-size: 14px;
+  color: #999999;
+  line-height: 20px;
+}
+
+.title {
+  font-size: 16px;
+  color: #333333;
+  line-height: 22px;
+}
+
+.radio {
+  :global {
+    .van-radio__icon {
+      display: none;
+    }
+    .van-tag--large {
+      width: 80px;
+      height: 32px;
+      font-size: 16px;
+      text-align: center;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+    .van-tag {
+      box-sizing: border-box;
+    }
+    .van-tag--default {
+      color: var(--van-tag-text-default-color);
+    }
+    .van-tag--primary {
+      background-color: var(--tag-bg-color);
+    }
+  }
+}

+ 90 - 0
src/views/shop-mall/modal/add-goods-cart/index.tsx

@@ -0,0 +1,90 @@
+import { moneyFormat } from '@/helpers/utils'
+import { Button, Cell, Image, Radio, RadioGroup, Stepper, Tag } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'add-goods-cart',
+  props: {
+    item: {
+      type: Object,
+      default: {}
+    }
+  },
+  data() {
+    return {
+      radio: ''
+    }
+  },
+  render() {
+    const item = this.item
+    return (
+      <div class={styles.addGoodsCart}>
+        <Cell
+          titleStyle={{ paddingLeft: '12px' }}
+          v-slots={{
+            icon: () => (
+              <Image src={item.pic} class={styles.goodsImg} fit="cover" />
+            ),
+            title: () => (
+              <div class={styles.goodsInfo}>
+                <p class={styles.goodsPrice}>
+                  <span>¥</span>
+                  {moneyFormat(item.originalPrice)}
+                </p>
+                <p class={styles.goodsStore}>库存:{item.stock}</p>
+              </div>
+            )
+          }}
+        />
+        <Cell
+          v-slots={{
+            title: () => <div class={styles.title}>规格</div>,
+            label: () => (
+              <div>
+                <Tag type="primary" size="large">
+                  标签
+                </Tag>
+              </div>
+              // <RadioGroup
+              //   class={styles['radio-group']}
+              //   modelValue={this.radio}
+              //   onUpdate:modelValue={val => (this.radio = val)}
+              // >
+              //   {Object.keys(classPriceType).map((item: string) => {
+              //     const isActive = item === this.radio
+              //     const type = isActive ? 'primary' : 'default'
+              //     return (
+              //       <Radio class={styles.radio} name={item}>
+              //         <Tag size="large" plain={isActive} type={type} round>
+              //           {classPriceType[item]}
+              //         </Tag>
+              //       </Radio>
+              //     )
+              //   })}
+              // </RadioGroup>
+            )
+          }}
+        />
+        <Cell
+          title="购买数量"
+          style={{ margin: '12px 0' }}
+          border={false}
+          titleClass={styles.title}
+          center
+        >
+          <Stepper
+            inputWidth="50px"
+            theme="round"
+            buttonSize="24px"
+            max={item.stock}
+            min={1}
+          />
+        </Cell>
+        <div class={['btnGroup']} style={{ marginBottom: '8px' }}>
+          <Button block round type="primary" text="确定" />
+        </div>
+      </div>
+    )
+  }
+})

+ 0 - 0
src/views/shop-mall/modal/select-goods/index.module.less


+ 9 - 0
src/views/shop-mall/modal/select-goods/index.tsx

@@ -0,0 +1,9 @@
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'select-goods',
+  render() {
+    return <div class={styles.selectGoods}></div>
+  }
+})

+ 4 - 0
vite.config.ts

@@ -70,6 +70,10 @@ export default defineConfig({
       '/music': {
         target: proxyUrl,
         changeOrigin: true
+      },
+      '/api-mall-portal': {
+        target: proxyUrl,
+        changeOrigin: true
       }
     }
   },