chenfuhao 1 день назад
Родитель
Сommit
eee5060ece

+ 3 - 3
pages.config.ts

@@ -46,9 +46,9 @@ export default defineUniPages({
         iconType: 'local',
       },
       {
-        icon: '/static/tabbar/community.png',
-        pagePath: 'pages/community/community',
-        text: 'tabbar.community',
+        icon: '/static/tabbar/message.png',
+        pagePath: 'pages/message/message',
+        text: 'tabbar.message',
         iconType: 'local',
       },
       {

+ 17 - 0
src/components/commonPopup/index.vue

@@ -0,0 +1,17 @@
+<template>
+  <view>
+    <wd-message-box></wd-message-box>
+    <wd-button @click="alert">alert</wd-button>
+  </view>
+</template>
+
+<script setup lang="ts">
+  import { useMessage } from 'wot-design-uni'
+  const message = useMessage()
+
+  function alert() {
+    message.alert('操作成功')
+  }
+</script>
+
+<style lang="scss" scoped></style>

+ 17 - 15
src/components/shop-tabbar/shop-tabbar.vue

@@ -34,14 +34,14 @@
     </view>
     <view
       class="tabbar-item"
-      @click="selectTabBar(community.path, 3)"
+      @click="selectTabBar(message.path, 3)"
       :class="{ 'tabbar-active': tabbarStore.curIdx === 3 }"
     >
       <view class="tabbar-item-wrapper">
         <view class="tabbar-item-wrapper-active-bg"></view>
-        <image :src="community.icon" class="tabbar-item-img" mode="widthFix"></image>
+        <image :src="message.icon" class="tabbar-item-img" mode="widthFix"></image>
       </view>
-      <view class="tabbar-item-text">{{ community.text }}</view>
+      <view class="tabbar-item-text">{{ message.text }}</view>
     </view>
     <view
       class="tabbar-item"
@@ -65,13 +65,14 @@
   const tabbarStore = useTabbarStore()
 
   /** tabbarList 里面的 path 从 pages.config.ts 得到 */
-  const [home, category, cate, community, person] = tabBar.list.map((item) => ({
+  const [home, category, cate, message, person] = tabBar.list.map((item) => ({
     ...item,
     path: `/${item.pagePath}`,
     text: t(item.text),
   }))
 
   function selectTabBar(url: string, index: number) {
+    console.log(url)
     tabbarStore.setCurIdx(index)
     uni.switchTab({ url })
   }
@@ -129,29 +130,29 @@
         font-size: 20rpx;
         font-style: normal;
         font-weight: 600;
-        line-height: 30rpx; /* 180% */
+        line-height: 36rpx; /* 180% */
         color: var(--333, #333);
       }
 
       .tabbar-item-wrapper {
         position: relative;
-        width: 60rpx;
-        height: 60rpx;
+        width: 64rpx;
+        height: 64rpx;
         margin: 0 auto;
         border-radius: 50%;
 
         .tabbar-item-wrapper-active-bg {
           position: absolute;
-          top: 10rpx;
-          left: 10rpx;
+          top: 15rpx;
+          left: 22rpx;
           display: none;
-          width: 40rpx;
-          height: 40rpx;
+          width: 22rpx;
+          height: 50rpx;
           border-radius: 50%;
         }
 
         .tabbar-item-img {
-          width: 56rpx;
+          width: 64rpx;
           margin: 5rpx auto;
         }
       }
@@ -190,11 +191,12 @@
         justify-content: center;
         width: 96rpx;
         height: 96rpx;
-        background-color: #f2f2f2;
+        background: var(--fff, $shop-white);
+        // background-color: #f2f2f2;
         border-radius: 24rpx;
-
+        box-shadow: 0rpx 8rpx 32rpx 0rpx rgba(0, 0, 0, 0.15);
         .home-item-img {
-          width: 80rpx;
+          width: 76rpx;
           margin-top: 10rpx;
         }
       }

+ 18 - 11
src/directives/format.ts

@@ -13,16 +13,23 @@ export type FormatDirective = Directive<HTMLElement, FormatType>
  * value: number 格式化小数点 string: 格式化日期
  */
 export const vFormat: Directive<HTMLElement, FormatType> = {
-  mounted(el, { value, modifiers }) {
-    const { textContent } = el
-    if (modifiers.date) {
-      el.textContent = new Date(textContent).toLocaleDateString()
-    }
-    if (modifiers.price || modifiers.rate) {
-      if (!Number.isNaN(textContent)) {
-        const decimal = typeof value === 'number' ? value : modifiers.price ? 2 : 1
-        el.textContent = Number(textContent).toFixed(decimal)
-      }
-    }
+  mounted(el, binding) {
+    formatText(el, binding)
+  },
+  updated(el, binding) {
+    formatText(el, binding)
   },
 }
+
+function formatText(el, { value, modifiers }) {
+  const { textContent } = el
+  if (modifiers.date) {
+    el.textContent = new Date(textContent).toLocaleDateString()
+  }
+  if (modifiers.price || modifiers.rate) {
+    if (!Number.isNaN(Number(textContent))) {
+      const decimal = typeof value === 'number' ? value : modifiers.price ? 2 : 1
+      el.textContent = Number(textContent).toFixed(decimal)
+    }
+  }
+}

+ 5 - 2
src/locale/en.json

@@ -2,7 +2,7 @@
   "tabbar.home": "Home",
   "tabbar.category": "Category",
   "tabbar.cart": "Cart",
-  "tabbar.community": "Community",
+  "tabbar.message": "Message",
   "tabbar.person": "Person",
   "app.name": "En Title",
   "weight": "{heavy}KG",
@@ -16,8 +16,11 @@
   "product.details": "Product Details",
   "cart.homeName": "Home1",
   "cart.manageCart": "Manage",
+  "cart.done": "Done",
   "cart.settlement": "Settlement",
   "cart.priceUint": "$",
   "cart.totalPrice": "Total Price",
-  "cart.selectAll": "Select All"
+  "cart.selectAll": "Select All",
+  "cart.collect": "Collect",
+  "cart.delete": "Delete"
 }

+ 5 - 2
src/locale/zh-Hans.json

@@ -2,7 +2,7 @@
   "tabbar.home": "主页",
   "tabbar.category": "分类",
   "tabbar.cart": "购物车",
-  "tabbar.community": "社区",
+  "tabbar.message": "消息",
   "tabbar.person": "我的",
   "app.name": "中文标题",
   "weight": "{heavy}公斤",
@@ -16,8 +16,11 @@
   "product.details": "商品详情",
   "cart.homeName": "地址1",
   "cart.manageCart": "管理",
+  "cart.done": "完成",
   "cart.settlement": "支付",
   "cart.priceUint": "¥",
   "cart.totalPrice": "总价",
-  "cart.selectAll": "全选"
+  "cart.selectAll": "全选",
+  "cart.collect": "收藏",
+  "cart.delete": "删除"
 }

+ 3 - 0
src/main.ts

@@ -7,9 +7,12 @@ import { prototypeInterceptor, requestInterceptor } from './interceptors'
 import i18n from './locale/index'
 import store from './store'
 import Directives from './directives'
+import CommonPopup from './components/commonPopup/index.vue'
 
 export function createApp() {
   const app = createSSRApp(App)
+  // 注册全局组件
+  app.component('CommonPopup', CommonPopup)
   app.use(store)
   app.use(i18n)
   app.use(requestInterceptor)

+ 347 - 14
src/pages/cart/cart.vue

@@ -8,9 +8,115 @@
 }
 </route>
 <script setup lang="ts">
+  import { useToast, useQueue } from 'wot-design-uni'
   import { t } from '@/locale'
   const { safeAreaInsets } = uni.getSystemInfoSync()
-  const selectAll = ref(true)
+  const selectAll = ref(false) // 是否全选
+  const isOperate = ref(false) // 是否操作
+  const skuList = ref([
+    {
+      id: 1,
+      num: 1,
+      skuName: '小米手机',
+      sku: ' 玄武绿 512GB',
+      price: 6999,
+      isSelect: false,
+      isShowNum: false,
+    },
+    {
+      id: 2,
+      num: 1,
+      skuName: '红米手机 K70 Pro',
+      price: 999.22,
+      isSelect: false,
+      isShowNum: false,
+    },
+    {
+      id: 3,
+      num: 1,
+      skuName: 'Huawei手机',
+      sku: ' 玄武绿 512GB',
+      price: 7999,
+      isSelect: false,
+      isShowNum: false,
+    },
+    {
+      id: 4,
+      num: 1,
+      skuName: '小米手机',
+      sku: ' 玄武绿 512GB',
+      price: 6999,
+      isSelect: false,
+      isShowNum: false,
+    },
+    {
+      id: 5,
+      num: 1,
+      skuName: '红米手机 K70 Pro',
+      price: 999.22,
+      isSelect: false,
+      isShowNum: false,
+    },
+    {
+      id: 6,
+      num: 1,
+      skuName: 'Huawei手机',
+      sku: ' 玄武绿 512GB',
+      price: 7999,
+      isSelect: false,
+      isShowNum: false,
+    },
+  ])
+  const { closeOutside } = useQueue()
+  const handleAction = (s: string) => {
+    console.log(s)
+  }
+  const handleChange = () => {}
+  const showSku = () => {}
+  // sku选中
+  const handleSKuSelect = (id) => {
+    const item = skuList.value.find((item) => item.id === id)
+    if (item) {
+      item.isSelect = !item.isSelect
+    }
+    selectAll.value = skuList.value.every((item) => item.isSelect)
+  }
+  // 全选
+  const handleSelectAll = () => {
+    selectAll.value = !selectAll.value
+    skuList.value.forEach((item) => (item.isSelect = selectAll.value))
+  }
+  // 计算总价
+  const total = computed(() => {
+    return (
+      skuList.value.reduce(
+        (sum, item) => (item.isSelect ? sum + item.price * item.num * 100 : sum),
+        0,
+      ) / 100
+    ).toFixed(2)
+  })
+  // 删除sku
+  const handleDelete = (id) => {
+    if (id !== 'batch') {
+      skuList.value = skuList.value.filter((item) => item.id !== id)
+    } else {
+      // 返回选中的sku的id
+      const ids = skuList.value.filter((item) => item.isSelect).map((item) => item.id)
+      skuList.value = skuList.value.filter((item) => !ids.includes(item.id))
+    }
+  }
+  // 是否显示计数器
+  const showNum = (id) => {
+    const item = skuList.value.find((item) => item.id === id)
+    if (item) {
+      item.isShowNum = !item.isShowNum
+    }
+  }
+  // 滚动关闭计数器显示以及展开
+  onscroll = () => {
+    closeOutside()
+    skuList.value.forEach((item) => (item.isShowNum = false))
+  }
 </script>
 <template>
   <view
@@ -27,27 +133,89 @@
             <image class="w-36rpx" src="@/static/images/cart/right.svg" mode="widthFix"></image>
           </view>
         </view>
-        <view class="manage-cart">{{ t('cart.manageCart') }}</view>
+        <view @click.stop="isOperate = !isOperate" v-if="!isOperate" class="manage-cart">
+          {{ t('cart.manageCart') }}
+        </view>
+        <view @click.stop="isOperate = !isOperate" v-else class="manage-cart">
+          {{ t('cart.done') }}
+        </view>
       </view>
     </wd-sticky>
 
-    <view class="cart-list"></view>
+    <view class="cart-list" @click.stop="closeOutside">
+      <view class="cart-item" v-for="item in skuList" :key="item.id">
+        <wd-swipe-action>
+          <template #default>
+            <view class="card-container">
+              <view class="selection-img">
+                <wd-img
+                  @click.stop="handleSKuSelect(item.id)"
+                  v-if="item.isSelect"
+                  width="42rpx"
+                  height="42rpx"
+                  :src="item.isSelect ? '/static/images/cart/select.svg' : ''"
+                />
+                <view @click.stop="handleSKuSelect(item.id)" v-else class="card-icon"></view>
+              </view>
+              <view class="card-img">
+                <wd-img width="160rpx" height="160rpx" src="/static/images/cart/sku-img.png" />
+              </view>
+              <view class="card-content">
+                <view class="sku-name">{{ item.skuName }}</view>
+                <view v-if="item.sku" class="sku-isSelect" @click.stop="showSku">
+                  {{ item.sku }}
+                  <wd-img width="32rpx" height="32rpx" src="/static/images/cart/chevron-down.svg" />
+                </view>
+                <view class="price-num-box">
+                  <view class="sku-price">
+                    UDP
+                    <text v-format.price>{{ item.price }}</text>
+                  </view>
+                  <view class="num">
+                    <view v-show="!item.isShowNum" @click.stop="showNum(item.id)" class="show-num">
+                      ×{{ item.num }}
+                    </view>
+                    <wd-input-number
+                      v-show="item.isShowNum"
+                      v-model="item.num"
+                      @change="handleChange"
+                    />
+                  </view>
+                </view>
+              </view>
+            </view>
+          </template>
+          <template #right>
+            <view class="action">
+              <view class="item-button" @click="handleAction('操作1')">
+                {{ t('cart.collect') }}
+              </view>
+              <view class="item-button item-button-delete" @click="handleDelete(item.id)">
+                {{ t('cart.delete') }}
+              </view>
+            </view>
+          </template>
+        </wd-swipe-action>
+      </view>
+    </view>
     <view class="cart-bottom-area pb-tab">
       <view class="cart-control">
-        <view
-          class="icon"
-          @click="selectAll = !selectAll"
-          :class="{ 'select-icon': selectAll }"
-        ></view>
-        <view @click="selectAll = !selectAll" class="text">{{ t('cart.selectAll') }}</view>
-        <view class="price">
+        <view class="cart-select-all">
+          <view class="icon" @click="handleSelectAll" :class="{ 'select-icon': selectAll }"></view>
+          <view @click="handleSelectAll" class="text">{{ t('cart.selectAll') }}</view>
+        </view>
+        <view class="price" v-if="!isOperate">
           {{ t('cart.totalPrice') }}
           <view class="price-text flex">
             {{ t('cart.priceUint') }}
-            <view v-format.price class="price-num">83.97</view>
+            <view class="price-num">{{ total }}</view>
           </view>
         </view>
-        <view class="button">{{ t('cart.settlement') }}</view>
+        <view v-if="!isOperate" class="button">{{ t('cart.settlement') }}</view>
+        <view @click.stop="handleDelete('batch')" v-else class="button">
+          {{ t('cart.delete') }}
+        </view>
+        <!-- <commonPopup /> -->
       </view>
     </view>
   </view>
@@ -56,10 +224,171 @@
   :deep(.wd-sticky__container) {
     top: 0 !important;
   }
-
+  :deep(.wd-swipe-action__right) {
+    border-left: 1rpx solid #fff;
+  }
+  :deep(.wd-input-number) {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    .wd-input-number__action {
+      box-sizing: border-box;
+      flex-direction: column;
+      gap: 20rpx;
+      align-items: center;
+      justify-content: center;
+      width: 64rpx;
+      height: 64rpx;
+      padding: 10rpx 20rpx;
+      background: var(--ff-4-c-1-b, $shop-primary);
+      border-radius: 12rpx;
+    }
+    .is-disabled {
+      background: var(--f-2-f-2-f-2, $shop-bg-line);
+    }
+    .wd-input-number__inner {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: 64rpx;
+      height: 64rpx;
+      margin: 0 24rpx;
+      // background: var(--f-2-f-2-f-2, $shop-bg-line);
+      border: 2rpx solid var(--f-2-f-2-f-2, $shop-bg-line);
+      border-radius: 12rpx;
+    }
+  }
   .cart-list {
+    width: 100vw;
     height: 100vh;
     margin-bottom: 130rpx;
+    .cart-item {
+      display: flex;
+      gap: 16rpx;
+      align-items: center;
+      width: 100%;
+      min-height: 160rpx;
+      margin-bottom: 32rpx;
+
+      .card-container {
+        box-sizing: border-box;
+        display: flex;
+        align-items: center;
+        width: 100%;
+        min-height: 160rpx;
+        padding: 0 32rpx;
+        .card-img {
+          width: 160rpx;
+          height: 160rpx;
+          margin: 0 16rpx;
+        }
+        .card-icon {
+          box-sizing: border-box;
+          width: 42rpx;
+          height: 42rpx;
+          border: 4rpx solid $shop-bg-line;
+          border-radius: 50%;
+        }
+        .card-content {
+          box-sizing: border-box;
+          display: flex;
+          flex-direction: column;
+          justify-content: space-between;
+          min-width: 452rpx;
+          min-height: 160rpx;
+          padding-bottom: 16rpx;
+          border-bottom: 2rpx solid var(--f-2-f-2-f-2, $shop-bg-line);
+          .sku-name {
+            font-family: 'PingFang SC';
+            font-size: 28rpx;
+            font-style: normal;
+            font-weight: 400;
+            line-height: 40rpx; /* 142.857% */
+            color: var(--333, #333);
+            text-align: justify;
+          }
+          .sku-isSelect {
+            box-sizing: border-box;
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            width: 200rpx;
+            height: 48rpx;
+            padding: 8rpx 16rpx;
+            margin: 16rpx 0;
+            /* Body/ExtraSmall */
+            font-family: 'PingFang SC';
+            font-size: 20rpx;
+            font-style: normal;
+            font-weight: 400;
+            color: var(--666, $shop-text-6);
+            background: var(--f-2-f-2-f-2, $shop-bg-line);
+            border-radius: 4rpx;
+          }
+          .price-num-box {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            .sku-price {
+              font-family: 'PingFang SC';
+              font-size: 28rpx;
+              font-style: normal;
+              font-weight: 600;
+              line-height: 44rpx; /* 157.143% */
+              color: var(--ff-4-c-1-b, $shop-primary);
+            }
+            .num {
+              display: flex;
+              align-items: center;
+              .show-num {
+                box-sizing: border-box;
+                display: flex;
+                flex-direction: column;
+                gap: 20rpx;
+                align-items: center;
+                justify-content: center;
+                min-width: 64rpx;
+                height: 64rpx;
+                padding: 10rpx 20rpx;
+                border: 2rpx solid var(--f-2-f-2-f-2, $shop-bg-line);
+                border-radius: 12rpx;
+              }
+            }
+          }
+        }
+      }
+      .wd-swipe-action {
+        box-sizing: border-box;
+        width: 100%;
+        min-height: 160rpx;
+        // background-color: #333333;
+      }
+
+      .action {
+        display: flex;
+        height: 100%;
+      }
+      .item-button {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 150rpx;
+        min-height: 160rpx;
+        font-family: 'PingFang SC';
+        font-size: 28rpx;
+        font-style: normal;
+        font-weight: 600;
+        line-height: 44rpx; /* 157.143% */
+        color: $shop-white;
+        text-align: center;
+        text-transform: capitalize;
+        letter-spacing: 1rpx;
+        background: var(--fc-7-b-1-c, $shop-help);
+      }
+      .item-button-delete {
+        background: var(--ff-4-c-1-b, $shop-primary);
+      }
+    }
   }
 
   .cart-bottom-area {
@@ -73,11 +402,15 @@
       box-sizing: border-box;
       display: flex;
       align-items: center;
+      justify-content: space-between;
       width: 750rpx;
       height: 120rpx;
       padding: 16rpx 32rpx;
       border-bottom: 2rpx solid var(--f-2-f-2-f-2, $shop-bg-line);
-
+      .cart-select-all {
+        display: flex;
+        align-items: center;
+      }
       .icon {
         box-sizing: border-box;
         width: 42rpx;

+ 1 - 1
src/pages/community/community.vue → src/pages/message/message.vue

@@ -2,7 +2,7 @@
 {
   layout: 'tabbar',
   style: {
-    navigationBarTitleText: '%tabbar.community%',
+    navigationBarTitleText: '%tabbar.message%',
   },
 }
 </route>

+ 5 - 0
src/static/images/cart/chevron-down.svg

@@ -0,0 +1,5 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="chevron-down">
+<path id="union" d="M3.54028 6.45964L4.45952 5.54041L7.9999 9.08079L11.5403 5.54041L12.4595 6.45964L7.9999 10.9193L3.54028 6.45964Z" fill="#999999"/>
+</g>
+</svg>

BIN
src/static/tabbar/cart.png


BIN
src/static/tabbar/category.png


BIN
src/static/tabbar/community.png


BIN
src/static/tabbar/home.png


BIN
src/static/tabbar/home1.png


BIN
src/static/tabbar/message.png


BIN
src/static/tabbar/person.png