chenfuhao 1 долоо хоног өмнө
parent
commit
c0d569e736

+ 1 - 1
package.json

@@ -49,7 +49,7 @@
     "qs": "6.5.3",
     "vue": "3.4.21",
     "vue-i18n": "9.1.9",
-    "wot-design-uni": "^1.4.0",
+    "wot-design-uni": "^1.7.1",
     "z-paging": "^2.8.4"
   },
   "devDependencies": {

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 291 - 70
pnpm-lock.yaml


+ 72 - 0
src/components/commonIndexBar/index.vue

@@ -0,0 +1,72 @@
+<template>
+  <view v-if="modelValue" class="wrap" @click.stop="close">
+    <view class="wrap-content" @click.stop>
+      <wd-index-bar sticky>
+        <view v-for="item in areaDataC" :key="item.index">
+          <wd-index-anchor :index="item.index" />
+          <wd-cell
+            border
+            clickable
+            v-for="city in item.data"
+            :key="city"
+            :title="city"
+            @click="handleClick(item.index, city)"
+          ></wd-cell>
+        </view>
+      </wd-index-bar>
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+  import { defineProps, defineEmits } from 'vue'
+  // **定义类型**
+  interface AreaItem {
+    index: string
+    data: string[]
+  }
+  // **定义 Props**
+  const props = defineProps<{
+    areaData: AreaItem[]
+    modelValue: boolean
+  }>()
+  // **定义 Emit 事件**
+  const emit = defineEmits(['update:modelValue', 'handleClick'])
+
+  // **点击城市**
+  const handleClick = (index: string, city: string) => {
+    emit('handleClick', { index, city }) // 触发父组件事件
+    emit('update:modelValue', false) // 关闭弹窗
+  }
+  const areaDataC = computed(() => props.areaData)
+  // **点击背景关闭弹窗**
+  const close = () => {
+    emit('update:modelValue', false)
+  }
+</script>
+
+<style lang="scss" scoped>
+  .wrap {
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 9999;
+    display: flex;
+    align-items: flex-start;
+    justify-content: center;
+    width: 100vw;
+    height: 100vh;
+    background: rgba(0, 0, 0, 0.4);
+    .wrap-content {
+      position: relative;
+      top: 176rpx;
+      display: flex;
+      flex-direction: column;
+      width: 100%;
+      height: calc(100vh - var(--window-top) - env(safe-area-inset-bottom));
+      overflow: hidden;
+      background: white;
+      border-radius: 10px;
+    }
+  }
+</style>

+ 77 - 0
src/components/commonMsgBox/index.vue

@@ -0,0 +1,77 @@
+<template>
+  <view>
+    <wd-message-box :selector="selector"></wd-message-box>
+  </view>
+</template>
+
+<script setup lang="ts">
+  defineProps({
+    selector: {
+      type: String,
+      default: 'message-box',
+    },
+  })
+</script>
+
+<style lang="scss" scoped>
+  :deep() {
+    .wd-message-box {
+      .wd-message-box__title {
+        font-family: 'PingFang SC';
+        font-size: 32rpx;
+        font-style: normal;
+        font-weight: 600;
+        line-height: 48rpx; /* 150% */
+        color: var(--text-icon-font-gy-1100, $shop-text-3);
+        text-align: center;
+        text-transform: capitalize;
+      }
+      .wd-message-box__content {
+        font-family: 'PingFang SC';
+        font-size: 28rpx;
+        font-style: normal;
+        font-weight: 400;
+        line-height: 44rpx; /* 157.143% */
+        color: var(--text-icon-font-gy-260, rgba(0, 0, 0, 0.6));
+        text-align: center;
+        text-transform: capitalize;
+      }
+      .wd-message-box__actions-btn:not(:last-child) {
+        margin-right: 0 !important;
+      }
+      .wd-message-box__actions {
+        display: flex;
+        align-items: center;
+        align-self: stretch;
+        justify-content: center;
+        height: 112rpx;
+        padding: 0 !important;
+        margin-top: 48rpx;
+        border-top: 0.5rpx solid var(--Gray-Gray3, #e7e7e7);
+      }
+      .custom-shadow {
+        display: flex;
+        flex: 1 0 0;
+        gap: 8rpx;
+        align-items: center;
+        justify-content: center;
+        height: 112rpx;
+        padding: 32rpx 40rpx;
+        /* Mark/Large */
+        font-family: 'PingFang SC';
+        font-size: 32rpx;
+        font-style: normal;
+        font-weight: 600;
+        line-height: 8rpx;
+        color: var(--text-icon-font-gy-1100, $shop-text-3);
+        text-align: center;
+        background: var(--Gray-White, $shop-white);
+        border-radius: 0;
+      }
+      .custom-shadow-confirm {
+        color: var(--ff-4-c-1-b, $shop-primary);
+        border-left: 1rpx solid var(--Gray-Gray3, #e7e7e7);
+      }
+    }
+  }
+</style>

+ 83 - 7
src/components/commonPopup/index.vue

@@ -1,17 +1,93 @@
 <template>
   <view>
-    <wd-message-box></wd-message-box>
-    <wd-button @click="alert">alert</wd-button>
+    <wd-popup
+      v-model="localShow"
+      position="bottom"
+      custom-style="height: 200px;"
+      @close="handleClose"
+    >
+      <view class="popup-header" v-if="isShowTitle">
+        <view class="popup-title">
+          {{ title }}
+          <view class="close" @click="handleClose"></view>
+        </view>
+      </view>
+      <slot></slot>
+    </wd-popup>
   </view>
 </template>
 
 <script setup lang="ts">
-  import { useMessage } from 'wot-design-uni'
-  const message = useMessage()
+  import { computed } from 'vue'
 
-  function alert() {
-    message.alert('操作成功')
+  const props = defineProps({
+    show: {
+      type: Boolean,
+      default: false,
+    },
+    title: {
+      type: String,
+      default: '标题',
+    },
+    isShowTitle: {
+      type: Boolean,
+      default: true,
+    },
+  })
+  const emit = defineEmits(['update:show', 'close'])
+
+  // 计算属性 `localShow`,用于 v-model 绑定
+  const localShow = computed({
+    get: () => props.show,
+    set: (value) => emit('update:show', value),
+  })
+
+  // 处理关闭事件
+  const handleClose = () => {
+    emit('close')
   }
 </script>
 
-<style lang="scss" scoped></style>
+<style scoped lang="scss">
+  .popup-header {
+    position: sticky;
+    top: 0;
+    z-index: 9999;
+    background-color: $shop-white;
+    .popup-title {
+      position: relative;
+      top: 0;
+      box-sizing: border-box;
+      display: flex;
+      flex-direction: column;
+      gap: 32rpx;
+      align-items: center;
+      width: 100%;
+      padding: 32rpx 0rpx;
+      font-family: 'PingFang SC';
+      font-size: 36rpx;
+      font-style: normal;
+      font-weight: 500;
+      line-height: 52rpx; /* 144.444% */
+      color: var(--text-icon-font-gy-1100, $shop-text-3);
+      text-align: center;
+      text-transform: capitalize;
+      border-bottom: 2rpx solid var(--f-2-f-2-f-2, $shop-bg-line);
+
+      .close {
+        position: absolute;
+        top: 50%;
+        right: 32rpx;
+        width: 48rpx;
+        height: 48rpx;
+        background: url('@/static/images/cart/cart-close.svg') no-repeat 0 0 / cover;
+        transform: translateY(-50%);
+      }
+    }
+  }
+
+  :deep(.wd-popup) {
+    min-height: 70vh;
+    border-radius: 16rpx 16rpx 0px 0px;
+  }
+</style>

+ 236 - 0
src/components/commonPopup/shop-sku-popup.vue

@@ -0,0 +1,236 @@
+<template>
+  <view>
+    <wd-popup
+      v-model="localShow"
+      position="bottom"
+      custom-style="height: 200px;"
+      @close="handleClose"
+    >
+      <view class="popup-header">
+        <view class="popup-title">
+          {{ title }}
+          <view class="close" @click="handleClose"></view>
+        </view>
+      </view>
+      <view class="popup-content">
+        <view class="sku-info">
+          <view class="sku-info-img">
+            <wd-img width="180rpx" height="180rpx" src="/static/images/cart/sku-img1.png" />
+          </view>
+          <view class="sku-info-base">
+            <view class="sku-info-name">Brown Jacket Brown Jacket Br own Jacket</view>
+            <view class="sku-info-price">USD 83.97</view>
+          </view>
+        </view>
+        <view class="sku-list">
+          <view class="sku-list-item" v-for="(item, i) in skuDataC.skuList" :key="i">
+            <view class="sku-title">Select {{ item.skuName }}</view>
+            <view class="sku-list-item-props">
+              <text
+                @click.stop="handleSkuSpecSelect(item.id, it)"
+                :class="[
+                  'sku-list-item-props-item',
+                  item.skuSelect === it ? 'sku-list-item-props-item-active' : '',
+                ]"
+                v-for="it in item.skuProps"
+                :key="it"
+              >
+                {{ it }}
+              </text>
+            </view>
+          </view>
+        </view>
+        <view class="sku-num">
+          <view class="sku-title">Select Qty</view>
+          <SkuInputNumber v-model:num="skuDataC.num" />
+        </view>
+        <view @click="toAddCart" class="sku-footer">Add To Cart</view>
+      </view>
+    </wd-popup>
+  </view>
+</template>
+
+<script setup lang="ts">
+  import { computed } from 'vue'
+  const props = defineProps({
+    show: {
+      type: Boolean,
+      default: false,
+    },
+    title: {
+      type: String,
+      default: '标题',
+    },
+    skuData: {
+      type: Object,
+      default: () => {},
+    },
+  })
+  const emit = defineEmits(['update:show', 'close', 'skuSpecSelect', 'toAddCart', 'update:skuData'])
+
+  // 计算属性 `localShow`,用于 v-model 绑定
+  const localShow = computed({
+    get: () => props.show,
+    set: (value) => emit('update:show', value),
+  })
+  const skuDataC = computed({
+    get: () => props.skuData,
+    set: (value) => emit('update:skuData', value),
+  })
+  // 处理关闭事件
+  const handleClose = () => {
+    emit('close')
+  }
+  const handleSkuSpecSelect = (id, sku) => {
+    emit('skuSpecSelect', id, sku)
+  }
+  const toAddCart = () => {
+    emit('toAddCart')
+  }
+</script>
+
+<style scoped lang="scss">
+  .popup-content {
+    box-sizing: border-box;
+    padding: 32rpx;
+    .sku-info {
+      display: flex;
+      margin-bottom: 32rpx;
+      &-img {
+        width: 180rpx;
+        height: 180rpx;
+        margin-right: 32rpx;
+      }
+      &-base {
+        box-sizing: border-box;
+        display: flex;
+        flex-direction: column;
+        justify-content: space-between;
+        padding: 16rpx 0;
+      }
+      &-name {
+        font-family: 'PingFang SC';
+        font-size: 32rpx;
+        font-style: normal;
+        font-weight: 500;
+        line-height: 48rpx; /* 150% */
+        color: var(--333, $shop-text-3);
+      }
+      &-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);
+      }
+    }
+    .sku-title {
+      margin-bottom: 16rpx;
+      font-family: 'PingFang SC';
+      font-size: 32rpx;
+      font-style: normal;
+      font-weight: 600;
+      line-height: 48rpx; /* 150% */
+      color: var(--333, $shop-text-3);
+    }
+    .sku-footer {
+      box-sizing: border-box;
+      display: flex;
+      flex: 1 0 0;
+      gap: 20rpx;
+      align-items: center;
+      justify-content: center;
+      height: 84rpx;
+      padding: 10rpx 66rpx;
+      margin-top: 32rpx;
+      font-family: 'PingFang SC';
+      font-size: 32rpx;
+      font-style: normal;
+      font-weight: 600; /* 150% */
+      line-height: 48rpx; /* 150% */
+      color: $shop-white;
+      text-align: center;
+      text-transform: capitalize;
+      background: var(
+        --ff-4-c-1-bfc-7-b-1-c,
+        linear-gradient(270deg, $shop-primary 0%, $shop-help 100%)
+      );
+      border-radius: 160rpx;
+    }
+    .sku-list {
+      &-item {
+        margin-bottom: 32rpx;
+        &-props {
+          display: flex;
+          flex-wrap: wrap;
+          gap: 24rpx;
+          &-item {
+            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;
+            font-family: 'PingFang SC';
+            font-size: 28rpx;
+            font-style: normal;
+            font-weight: 600;
+            line-height: 44rpx; /* 157.143% */
+            color: var(--333, $shop-text-3);
+            border: 2rpx solid var(--f-2-f-2-f-2, $shop-bg-line);
+            border-radius: 12rpx;
+          }
+          &-item-active {
+            color: var(--fff, $shop-white);
+            background: var(--ff-4-c-1-b, $shop-primary);
+          }
+        }
+      }
+    }
+  }
+  .popup-header {
+    position: sticky;
+    top: 0;
+    z-index: 9999;
+    background-color: $shop-white;
+    .popup-title {
+      position: relative;
+      top: 0;
+      box-sizing: border-box;
+      display: flex;
+      flex-direction: column;
+      gap: 32rpx;
+      align-items: center;
+      width: 100%;
+      padding: 32rpx 0rpx;
+      font-family: 'PingFang SC';
+      font-size: 36rpx;
+      font-style: normal;
+      font-weight: 500;
+      line-height: 52rpx; /* 144.444% */
+      color: var(--text-icon-font-gy-1100, $shop-text-3);
+      text-align: center;
+      text-transform: capitalize;
+      border-bottom: 2rpx solid var(--f-2-f-2-f-2, $shop-bg-line);
+
+      .close {
+        position: absolute;
+        top: 50%;
+        right: 32rpx;
+        width: 48rpx;
+        height: 48rpx;
+        background: url('@/static/images/cart/cart-close.svg') no-repeat 0 0 / cover;
+        transform: translateY(-50%);
+      }
+    }
+  }
+
+  :deep(.wd-popup) {
+    min-height: 50vh;
+    border-radius: 16rpx 16rpx 0px 0px;
+  }
+</style>

+ 0 - 1
src/components/shop-tabbar/shop-tabbar.vue

@@ -72,7 +72,6 @@
   }))
 
   function selectTabBar(url: string, index: number) {
-    console.log(url)
     tabbarStore.setCurIdx(index)
     uni.switchTab({ url })
   }

+ 54 - 0
src/components/sku-input-number/index.vue

@@ -0,0 +1,54 @@
+<template>
+  <view>
+    <wd-input-number v-model="localNum" />
+  </view>
+</template>
+
+<script setup lang="ts">
+  const props = defineProps({
+    num: {
+      type: Number,
+      default: 1,
+    },
+  })
+  const emit = defineEmits(['update:num'])
+  // 计算属性 `localShow`,用于 v-model 绑定
+  const localNum = computed({
+    get: () => props.num,
+    set: (value) => emit('update:num', value),
+  })
+</script>
+
+<style lang="scss" scoped>
+  :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;
+    }
+  }
+</style>

+ 11 - 1
src/locale/en.json

@@ -22,5 +22,15 @@
   "cart.totalPrice": "Total Price",
   "cart.selectAll": "Select All",
   "cart.collect": "Collect",
-  "cart.delete": "Delete"
+  "cart.delete": "Delete",
+  "cart.empty": "No products available at the moment",
+  "cart.goHome": "Go shopping in the mall",
+  "commonPopup.cancel": "Cancel",
+  "commonPopup.confirm": "Confirm",
+  "login.skip": "Skip",
+  "login.title": "Please select your gender",
+  "login.tip": "You may make modifications after entering",
+  "login.woman": "Woman",
+  "login.man": "Man",
+  "login.resend": "Resend"
 }

+ 11 - 1
src/locale/zh-Hans.json

@@ -22,5 +22,15 @@
   "cart.totalPrice": "总价",
   "cart.selectAll": "全选",
   "cart.collect": "收藏",
-  "cart.delete": "删除"
+  "cart.delete": "删除",
+  "cart.empty": "目前还未添加任何商品噢",
+  "cart.goHome": "去首页逛逛",
+  "commonPopup.cancel": "取消",
+  "commonPopup.confirm": "确定",
+  "login.skip": "跳过",
+  "login-title": "请选择您的性别",
+  "login.tip": "您可以在输入后进行修改",
+  "login.woman": "女",
+  "login.man": "男",
+  "login.resend": "重新发送"
 }

+ 8 - 2
src/main.ts

@@ -7,12 +7,18 @@ 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'
+import commonMsgBox from './components/commonMsgBox/index.vue'
+import commonPopup from './components/commonPopup/index.vue'
+import skuInputNumber from './components/sku-input-number/index.vue'
+import commonIndexBar from './components/commonIndexBar/index.vue'
 
 export function createApp() {
   const app = createSSRApp(App)
   // 注册全局组件
-  app.component('CommonPopup', CommonPopup)
+  app.component('CommonMsgBox', commonMsgBox)
+  app.component('CommonPopup', commonPopup)
+  app.component('SkuInputNumber', skuInputNumber)
+  app.component('CommonIndexBar', commonIndexBar)
   app.use(store)
   app.use(i18n)
   app.use(requestInterceptor)

+ 191 - 74
src/pages/cart/cart.vue

@@ -8,11 +8,14 @@
 }
 </route>
 <script setup lang="ts">
-  import { useToast, useQueue } from 'wot-design-uni'
+  import { useToast, useQueue, useMessage } from 'wot-design-uni'
   import { t } from '@/locale'
+  import shopSkuPopup from '@/components/commonPopup/shop-sku-popup.vue'
   const { safeAreaInsets } = uni.getSystemInfoSync()
   const selectAll = ref(false) // 是否全选
   const isOperate = ref(false) // 是否操作
+  const message = useMessage('cart-message-box') // 删除消息弹窗dom
+  const showCommonPopup = ref(false) // 控制sku弹窗显示
   const skuList = ref([
     {
       id: 1,
@@ -67,12 +70,36 @@
       isShowNum: false,
     },
   ])
+  const skuData = ref({
+    id: 1,
+    num: 1,
+    skuName: '小米手机',
+    sku: ' 玄武绿 512GB',
+    price: 6999,
+    skuList: [
+      {
+        id: 1,
+        skuName: 'Size',
+        skuSelect: '',
+        skuProps: ['8888888GB', '16GB', '32GB', '64GB', '10GB', '128GB', '256GB', '512GB'],
+      },
+      {
+        id: 2,
+        skuName: 'Color',
+        skuSelect: '',
+        skuProps: ['Black', 'White', 'Red', 'Blue', 'Green', 'Yellow', 'Orange'],
+      },
+    ],
+  })
   const { closeOutside } = useQueue()
   const handleAction = (s: string) => {
     console.log(s)
   }
   const handleChange = () => {}
-  const showSku = () => {}
+
+  const showSku = () => {
+    showCommonPopup.value = true
+  }
   // sku选中
   const handleSKuSelect = (id) => {
     const item = skuList.value.find((item) => item.id === id)
@@ -99,10 +126,44 @@
   const handleDelete = (id) => {
     if (id !== 'batch') {
       skuList.value = skuList.value.filter((item) => item.id !== id)
+      if (skuList.value.length === 0) {
+        selectAll.value = false
+        return
+      }
+      // 更新选中状态 如果全部删完了 更新选中状态
+      selectAll.value = skuList.value.every((item) => item.isSelect)
     } 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))
+      if (ids.length === 0) return
+      message
+        .confirm({
+          confirmButtonText: t('commonPopup.confirm'),
+          cancelButtonText: t('commonPopup.cancel'),
+          msg: 'The product cannot be restored after deletion. Are you sure you want to delete the selected product?',
+          title: 'Confirm operation',
+          cancelButtonProps: {
+            type: 'error',
+            customClass: 'custom-shadow',
+          },
+          confirmButtonProps: {
+            type: 'success',
+            customClass: 'custom-shadow custom-shadow-confirm',
+          },
+        })
+        .then(() => {
+          // 返回选中的sku的id
+          skuList.value = skuList.value.filter((item) => !ids.includes(item.id))
+          if (skuList.value.length === 0) {
+            selectAll.value = false
+            return
+          }
+          // 更新选中状态 如果全部删完了 更新选中状态
+          selectAll.value = skuList.value.every((item) => item.isSelect)
+        })
+        .catch((error) => {
+          console.log(error)
+        })
     }
   }
   // 是否显示计数器
@@ -117,6 +178,26 @@
     closeOutside()
     skuList.value.forEach((item) => (item.isShowNum = false))
   }
+
+  // 回到主页
+  const goHome = () => {
+    uni.switchTab({
+      url: '/pages/index/index',
+    })
+  }
+  // 处理选中的sku规格
+  const handleSkuSpecSelect = (id, sku) => {
+    const item = skuData.value.skuList.find((item) => item.id === id)
+    if (!item) return
+    if (item.skuSelect === sku) {
+      item.skuSelect = ''
+      return
+    }
+    item.skuSelect = sku
+  }
+  const addToCart = () => {
+    console.log(skuData.value)
+  }
 </script>
 <template>
   <view
@@ -143,59 +224,71 @@
     </wd-sticky>
 
     <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 v-if="skuList && skuList.length > 0">
+        <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="price-num-box">
-                  <view class="sku-price">
-                    UDP
-                    <text v-format.price>{{ item.price }}</text>
+                <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="num">
-                    <view v-show="!item.isShowNum" @click.stop="showNum(item.id)" class="show-num">
-                      ×{{ item.num }}
+                  <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" /> -->
+                      <SkuInputNumber v-show="item.isShowNum" v-model: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') }}
+            </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>
-            </view>
-          </template>
-        </wd-swipe-action>
+            </template>
+          </wd-swipe-action>
+        </view>
+      </view>
+      <view v-else class="cart-empty">
+        <wd-img width="320rpx" height="320rpx" src="/static/images/cart/empty.svg" />
+        <view class="empty-text">{{ t('cart.empty') }}</view>
+        <view class="empty-button" @click="goHome">{{ t('cart.goHome') }}</view>
       </view>
     </view>
     <view class="cart-bottom-area pb-tab">
@@ -215,9 +308,18 @@
         <view @click.stop="handleDelete('batch')" v-else class="button">
           {{ t('cart.delete') }}
         </view>
-        <!-- <commonPopup /> -->
       </view>
     </view>
+    <CommonMsgBox selector="cart-message-box" />
+    <shopSkuPopup
+      v-if="showCommonPopup"
+      @sku-spec-select="handleSkuSpecSelect"
+      @to-add-cart="addToCart"
+      v-model:skuData="skuData"
+      title="specifications"
+      v-model:show="showCommonPopup"
+      @close="showCommonPopup = false"
+    ></shopSkuPopup>
   </view>
 </template>
 <style lang="scss" scoped>
@@ -227,40 +329,55 @@
   :deep(.wd-swipe-action__right) {
     border-left: 1rpx solid #fff;
   }
-  :deep(.wd-input-number) {
+
+  .cart-empty {
+    position: absolute;
+    top: 50%;
+    left: 50%;
     display: flex;
+    flex-direction: column;
     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);
+    width: 100%;
+    transform: translate(-50%, -50%);
+    .empty-text {
+      margin: 48rpx 0;
+      font-family: 'PingFang SC';
+      font-size: 28rpx;
+      font-style: normal;
+      font-weight: 400;
+      line-height: normal;
+      color: var(--999, $shop-text-9);
+      text-align: center;
+      text-transform: capitalize;
     }
-    .wd-input-number__inner {
+    .empty-button {
       display: flex;
+      gap: 20rpx;
       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;
+      padding: 22rpx 24rpx;
+      font-family: 'PingFang SC';
+      font-size: 32rpx;
+      font-style: normal;
+      font-weight: 600;
+      line-height: 48rpx; /* 150% */
+      color: var(--fff, $shop-white);
+      text-align: center;
+      text-transform: capitalize;
+      background: var(
+        --ff-4-c-1-bfc-7-b-1-c,
+        linear-gradient(270deg, $shop-primary 0%, $shop-help 100%)
+      );
+      border-radius: 200rpx;
     }
   }
   .cart-list {
+    position: relative;
+    box-sizing: border-box;
     width: 100vw;
-    height: 100vh;
+    min-height: calc(100vh - 378rpx);
+    padding-bottom: 30rpx;
     margin-bottom: 130rpx;
     .cart-item {
       display: flex;

+ 54 - 20
src/pages/index/index.vue

@@ -18,15 +18,23 @@
           <image style="width: 36rpx" src="@/static/images/select.svg" mode="widthFix"></image>
         </view>
         <view class="msg-info">
-          <image style="width: 76rpx" src="@/static/images/msg.svg" mode="widthFix"></image>
+          <wd-img width="40rpx" height="40rpx" src="/static/images/home/msg.svg"></wd-img>
         </view>
       </view>
     </wd-sticky>
     <view class="search-container">
-      <view class="search-item">
-        <image style="width: 40rpx" src="@/static/images/search.svg" mode="widthFix"></image>
-        <view class="search-text">{{ t('home.search') }}</view>
-      </view>
+      <wd-search
+        class="search-input"
+        placeholder-left
+        hide-cancel
+        v-model="homeSearch"
+        :placeholder="t('home.search')"
+        @search="search"
+      >
+        <template #prefix>
+          <wd-img width="40rpx" height="40rpx" src="/static/images/home/home-search.svg"></wd-img>
+        </template>
+      </wd-search>
     </view>
     <view class="card-swiper-container">
       <wd-swiper
@@ -61,6 +69,15 @@
       </view>
       <HotTags v-model:current-tag="currentTag"></HotTags>
       <ProductList></ProductList>
+      <shopSkuPopup
+        v-if="showCommonPopup"
+        @sku-spec-select="handleSkuSpecSelect"
+        @to-add-cart="addToCart"
+        v-model:skuData="skuData"
+        title="specifications"
+        v-model:show="showCommonPopup"
+        @close="showCommonPopup = false"
+      ></shopSkuPopup>
     </view>
   </view>
   <tabbar />
@@ -69,6 +86,7 @@
 <script lang="ts" setup>
   import HotTags from '@/components/hot-tags/hot-tags.vue'
   import ProductList from '@/components/product-list/product-list.vue'
+  import shopSkuPopup from '@/components/commonPopup/shop-sku-popup.vue'
   import { t } from '@/locale'
   import CateImg1 from '@/static/temp/cate-1.png'
   import CateImg2 from '@/static/temp/cate-2.png'
@@ -105,6 +123,12 @@
 
   // 获取屏幕边界到安全区域距离
   const { safeAreaInsets } = uni.getSystemInfoSync()
+
+  const showCommonPopup = ref(false)
+  const homeSearch = ref('')
+  const search = () => {
+    console.log(homeSearch.value)
+  }
 </script>
 
 <style lang="scss" scoped>
@@ -240,26 +264,25 @@
   }
   .search-container {
     box-sizing: border-box;
-    padding: 0 16rpx 24rpx;
-
-    .search-item {
+    padding: 0 16rpx;
+    margin-bottom: 24rpx;
+    .search-input {
       box-sizing: border-box;
       display: flex;
       align-items: center;
       height: 84rpx;
       padding: 20rpx 24rpx;
-      border: 2rpx solid $shop-bg-line;
-      border-radius: 42rpx;
-
-      .search-text {
-        margin-left: 16rpx;
-        font-family: 'PingFang SC';
-        font-size: 28rpx;
-        font-style: normal;
-        font-weight: 400;
-        line-height: 44rpx; /* 157.143% */
-        color: var(--999, #999);
-        text-align: center;
+      background-color: #fff;
+      border: 2rpx solid var(--f-2-f-2-f-2, $shop-bg-line);
+      border-radius: 102rpx;
+      :deep(.wd-search__block) {
+        background-color: $shop-white !important;
+      }
+      :deep(.wd-icon) {
+        display: none;
+      }
+      :deep(.wd-search__input) {
+        padding-left: 20rpx;
       }
     }
   }
@@ -287,4 +310,15 @@
       }
     }
   }
+  .msg-info {
+    box-sizing: border-box;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 76rpx;
+    height: 76rpx;
+    padding: 18rpx;
+    background: var(--f-2-f-2-f-2, $shop-bg-line);
+    border-radius: 130rpx;
+  }
 </style>

+ 30 - 0
src/pages/select-country/index.vue

@@ -0,0 +1,30 @@
+<template>
+  <view class="select-country-main-wrap">
+    <view class="top-header"></view>
+    <view class="content"></view>
+    <view class="bottom-footer"></view>
+  </view>
+</template>
+
+<script setup lang="ts"></script>
+
+<style lang="scss" scoped>
+  .select-country-main-wrap {
+    box-sizing: border-box;
+    padding: 0 32rpx;
+    .bottom-footer {
+      box-sizing: border-box;
+      display: flex;
+      flex: 1 0 0;
+      align-items: center;
+      justify-content: center;
+      height: 84rpx;
+      padding: 10rpx 66rpx;
+      background: var(
+        --ff-4-c-1-bfc-7-b-1-c,
+        linear-gradient(270deg, $shop-primary 0%, $shop-help 100%)
+      );
+      border-radius: 160rpx;
+    }
+  }
+</style>

+ 545 - 1
src/pages/user/login.vue

@@ -6,6 +6,550 @@
   },
 }
 </route>
+<script setup lang="ts">
+  import { t } from '@/locale'
+  const loginForm = ref({
+    phone: '',
+    password: '',
+    code: '',
+  })
+  const showArea = ref(false) // 是否显示地区
+  const areaData = ref([
+    {
+      index: 'A',
+      data: ['阿坝', '阿拉善', '阿里', '安康', '安庆', '鞍山', '安顺', '安阳', '澳门'],
+    },
+    {
+      index: 'B',
+      data: [
+        '北京',
+        '白银',
+        '保定',
+        '宝鸡',
+        '保山',
+        '包头',
+        '巴中',
+        '北海',
+        '蚌埠',
+        '本溪',
+        '毕节',
+        '滨州',
+        '百色',
+        '亳州',
+      ],
+    },
+    {
+      index: 'C',
+      data: [
+        '重庆',
+        '成都',
+        '长沙',
+        '长春',
+        '沧州',
+        '常德',
+        '昌都',
+        '长治',
+        '常州',
+        '巢湖',
+        '潮州',
+        '承德',
+        '郴州',
+        '赤峰',
+        '池州',
+        '崇左',
+        '楚雄',
+        '滁州',
+        '朝阳',
+      ],
+    },
+    {
+      index: 'D',
+      data: [
+        '大连',
+        '东莞',
+        '大理',
+        '丹东',
+        '大庆',
+        '大同',
+        '大兴安岭',
+        '德宏',
+        '德阳',
+        '德州',
+        '定西',
+        '迪庆',
+        '东营',
+      ],
+    },
+    {
+      index: 'E',
+      data: ['鄂尔多斯', '恩施', '鄂州'],
+    },
+    {
+      index: 'F',
+      data: ['福州', '防城港', '佛山', '抚顺', '抚州', '阜新', '阜阳'],
+    },
+    {
+      index: 'G',
+      data: ['广州', '桂林', '贵阳', '甘南', '赣州', '甘孜', '广安', '广元', '贵港', '果洛'],
+    },
+    {
+      index: 'H',
+      data: [
+        '杭州',
+        '哈尔滨',
+        '合肥',
+        '海口',
+        '呼和浩特',
+        '海北',
+        '海东',
+        '海南',
+        '海西',
+        '邯郸',
+        '汉中',
+        '鹤壁',
+        '河池',
+        '鹤岗',
+        '黑河',
+        '衡水',
+        '衡阳',
+        '河源',
+        '贺州',
+        '红河',
+        '淮安',
+        '淮北',
+        '怀化',
+        '淮南',
+        '黄冈',
+        '黄南',
+        '黄山',
+        '黄石',
+        '惠州',
+        '葫芦岛',
+        '呼伦贝尔',
+        '湖州',
+        '菏泽',
+      ],
+    },
+  ])
+  const isFirst = ref(false) // 是否第一次登录
+  const selectGender = ref(1) // 选择性别 1男 2女
+  const countdown = ref(0) // 倒计时
+  const isCounting = ref(false) // 控制按钮状态
+  let timer = null // 定时器
+
+  const resetSendCode = () => {
+    // 开始倒计时
+    isCounting.value = true
+    countdown.value = 59
+    timer = setInterval(() => {
+      countdown.value--
+      if (countdown.value <= 0) {
+        clearInterval(timer)
+        isCounting.value = false // 重新启用按钮
+      }
+    }, 1000)
+  }
+  function handleClick(index: string, city: string) {
+    console.log(index, city)
+  }
+
+  const handleLogin = () => {
+    // 去除空白校验未填字段
+    for (const key in loginForm.value) {
+      if (!loginForm.value[key].trim()) {
+        uni.showToast({
+          icon: 'none',
+          title: '请输入' + key,
+        })
+        return
+      }
+    }
+    uni.showToast({
+      icon: 'none',
+      title: '登录成功',
+    })
+    setTimeout(() => {
+      toHome()
+    }, 1000)
+  }
+  const toHome = () => {
+    uni.switchTab({ url: '/pages/index/index' })
+  }
+  onUnload(() => {
+    clearInterval(timer)
+  })
+</script>
 <template>
-  <view>Hello</view>
+  <view v-if="!isFirst" class="login-wrap">
+    <view class="login-header">
+      <view class="login-logo">
+        <wd-img width="84rpx" height="84rpx" src="/static/images/login/login-logo.svg" />
+      </view>
+      <view class="login-title">
+        <wd-img width="200rpx" height="36rpx" src="/static/images/login/login-title.svg"></wd-img>
+      </view>
+    </view>
+    <view class="login-form">
+      <view class="login-form-item">
+        <view class="login-form-item-title">phone number</view>
+        <view class="login-form-item-content">
+          <view class="item-left" @click="showArea = true">
+            <view class="phone-address">+86</view>
+            <wd-img
+              width="48rpx"
+              height="48rpx"
+              src="/static/images/login/login-right.svg"
+            ></wd-img>
+          </view>
+          <wd-input class="item-right" v-model="loginForm.phone" placeholder="enter Your number" />
+        </view>
+      </view>
+      <view class="login-form-item">
+        <view class="login-form-item-title">code</view>
+        <view class="login-form-item-content">
+          <wd-input class="item-right code" v-model="loginForm.password" placeholder="enter code">
+            <template #suffix>
+              <view @click.stop="resetSendCode" v-if="!isCounting" class="code-send">send</view>
+              <view v-else class="code-send base-send">
+                {{ t('login.resend') }}({{ countdown }}s)
+              </view>
+            </template>
+          </wd-input>
+        </view>
+      </view>
+      <view class="login-form-item">
+        <view class="login-form-item-title">graphic</view>
+        <view class="login-form-item-content">
+          <wd-input class="item-right code" v-model="loginForm.code" placeholder="enter">
+            <template #suffix>
+              <view class="verify-code">
+                <wd-img
+                  width="250rpx"
+                  height="78rpx"
+                  src="/static/images/login/test-code.svg"
+                ></wd-img>
+              </view>
+            </template>
+          </wd-input>
+        </view>
+      </view>
+    </view>
+
+    <view @click.stop="handleLogin" class="login-footer">Log in</view>
+    <CommonIndexBar
+      :areaData="areaData"
+      @handleClick="handleClick"
+      v-model="showArea"
+      @close="showArea = false"
+    />
+  </view>
+  <view v-else class="first-select-gender">
+    <view @click.stop="toHome" class="top-header">{{ t('login.skip') }}</view>
+    <view class="gender-content">
+      <view class="gender-title">{{ t('login.title') }}</view>
+      <view class="gender-tip">{{ t('login.tip') }}</view>
+      <view @click="selectGender = 2" :class="['base', selectGender === 2 && 'base-active']">
+        <view class="woman-img"></view>
+        <view v-if="selectGender === 2" class="radio-icon"></view>
+        <view v-else class="base-icon"></view>
+        <view class="base-text">{{ t('login.woman') }}</view>
+      </view>
+      <view
+        @click="selectGender = 1"
+        :class="['base', 'base-man', selectGender === 1 && 'base-active']"
+      >
+        <view v-if="selectGender === 1" class="radio-icon"></view>
+        <view v-else class="base-icon"></view>
+        <view class="base-text">{{ t('login.man') }}</view>
+        <view class="man-img"></view>
+      </view>
+    </view>
+    <view @click="toHome" class="gender-footer">{{ t('cart.done') }}</view>
+  </view>
 </template>
+<style lang="scss" scoped>
+  :deep(.wd-cell.is-hover) {
+    background: #ccc !important;
+  }
+  :deep(.wd-index-bar__index) {
+    font-family: 'PingFang SC';
+    font-size: 32rpx;
+    font-style: normal;
+    font-weight: 400;
+    line-height: 48rpx; /* 150% */
+    color: var(--333, $shop-text-3);
+    text-align: center;
+  }
+  :deep(.wd-index-bar__index.is-active) {
+    color: var(--ff-4-c-1-b, $shop-primary);
+  }
+  :deep(.wd-input__value) {
+    box-sizing: border-box;
+    height: 112rpx !important;
+    padding: 32rpx;
+    .uni-uni-input-placeholder .wd-input__placeholder {
+      font-family: Inter;
+      font-size: 32rpx;
+      font-style: normal;
+      font-weight: 400;
+      line-height: 48rpx; /* 150% */
+      color: var(--999, $shop-text-9);
+      text-transform: capitalize;
+      // .uni-input-input{
+
+      // }
+    }
+  }
+  .login-wrap {
+    box-sizing: border-box;
+    width: 100vw;
+    height: 100vh;
+    padding: 128rpx 32rpx;
+    background-image: url(@/static/images/login/login-bg.svg);
+    background-size: cover;
+
+    .login-header {
+      display: flex;
+      align-items: center;
+      margin-bottom: 48rpx;
+      .login-logo {
+        margin-right: 20rpx;
+      }
+    }
+    .login-form {
+      &-item {
+        margin-bottom: 40rpx;
+        &-title {
+          margin-bottom: 8rpx;
+          font-family: 'PingFang SC';
+          font-size: 28rpx;
+          font-style: normal;
+          font-weight: 400;
+          line-height: 44rpx; /* 157.143% */
+          color: var(--333, $shop-text-3);
+          text-transform: capitalize;
+        }
+        &-content {
+          display: flex;
+          align-items: center;
+          .item-left {
+            box-sizing: border-box;
+            display: flex;
+            gap: 16rpx;
+            align-items: center;
+            padding: 32rpx;
+            margin-right: 32rpx;
+            font-family: Inter;
+            font-size: 32rpx;
+            font-style: normal;
+            font-weight: 400;
+            line-height: 48rpx; /* 150% */
+            color: var(--333, $shop-text-3);
+            text-transform: capitalize;
+            background: $shop-white;
+            border: 1.908rpx solid var(--f-2-f-2-f-2, $shop-bg-line);
+            border-radius: 16rpx;
+            .phone-address {
+              margin-right: 16rpx;
+            }
+          }
+          .code {
+            width: 100% !important;
+          }
+          .item-right {
+            flex: 1;
+            height: 112rpx;
+            background: $shop-white;
+            border: 1.908rpx solid var(--f-2-f-2-f-2, $shop-bg-line);
+            border-radius: 16rpx;
+
+            &::after {
+              height: 0 !important;
+            }
+          }
+          .verify-code {
+            display: flex;
+            align-items: center;
+          }
+          .base-send {
+            color: var(--999, $shop-text-9) !important;
+          }
+          .code-send {
+            position: relative;
+            font-family: Inter;
+            font-size: 28rpx;
+            font-style: normal;
+            font-weight: 400;
+            line-height: 44rpx; /* 157.143% */
+            color: var(--ff-4-c-1-b, $shop-primary);
+            text-transform: capitalize;
+            &::before {
+              position: absolute;
+              top: 50%;
+              left: -16rpx;
+              display: block;
+              width: 2rpx !important;
+              height: 30.534rpx !important;
+              content: '';
+              background: var(--f-2-f-2-f-2, $shop-bg-line);
+              transform: translateY(-50%);
+            }
+          }
+        }
+      }
+    }
+    .login-footer {
+      box-sizing: border-box;
+      display: flex;
+      flex-shrink: 0;
+      align-items: center;
+      justify-content: center;
+      width: 100%;
+      height: 108rpx;
+      padding: 32rpx 0rpx;
+      margin-top: 64rpx;
+      font-family: 'PingFang SC';
+      font-size: 32rpx;
+      font-style: normal;
+      font-weight: 600; /* 150% */ /* 150% */
+      line-height: 48rpx; /* 150% */
+      color: $shop-white;
+      text-align: center;
+      background: var(
+        --ff-4-c-1-bfc-7-b-1-c,
+        linear-gradient(270deg, $shop-primary 0%, $shop-help 100%)
+      );
+      border-radius: 16rpx;
+    }
+  }
+  .first-select-gender {
+    box-sizing: border-box;
+    padding: 0 32rpx;
+    .top-header {
+      height: 88rpx;
+      margin-bottom: 80rpx;
+      font-family: 'PingFang SC';
+      font-size: 28rpx;
+      font-style: normal;
+      font-weight: 400;
+      line-height: 88rpx; /* 157.143% */
+      color: var(--ff-4-c-1-b, $shop-primary);
+      text-align: right;
+      text-transform: capitalize;
+    }
+    .gender-content {
+      font-family: 'PingFang SC';
+      font-style: normal;
+      color: var(--333, $shop-text-3);
+      .gender-title {
+        font-size: 36rpx;
+        font-weight: 600;
+        line-height: 52rpx; /* 144.444% */
+        color: var(--333, $shop-text-3);
+        text-align: center;
+        text-transform: capitalize;
+      }
+      .gender-tip {
+        margin-top: 16rpx;
+        margin-bottom: 120rpx;
+        font-size: 28rpx;
+        font-weight: 400;
+        line-height: 44rpx; /* 157.143% */
+        color: var(--666, $shop-text-6);
+        text-align: center;
+        text-transform: capitalize;
+      }
+      .base-man {
+        justify-content: flex-end;
+      }
+      .base {
+        box-sizing: border-box;
+        display: flex;
+        align-items: center;
+        width: 600rpx;
+        height: 220rpx;
+        padding: 0rpx 32rpx;
+        margin: 0 auto;
+        margin-bottom: 40rpx;
+        font-family: 'PingFang SC';
+        font-size: 48rpx;
+        font-style: normal;
+        font-weight: 600;
+        line-height: 64rpx; /* 133.333% */
+        color: var(--999, $shop-text-9);
+        background: var(--f-2-f-2-f-2, $shop-bg-line);
+        border-radius: 114rpx;
+
+        .woman-img {
+          flex-shrink: 0;
+          width: 216rpx;
+          height: 216rpx;
+          margin-right: 52rpx;
+          background-image: url(@/static/images/login/woman.svg);
+          background-size: cover;
+        }
+        .radio-icon {
+          flex-shrink: 0;
+          width: 44rpx;
+          height: 44rpx;
+          background-image: url(@/static/images/cart/select.svg);
+          background-size: cover;
+          border-radius: 50%;
+        }
+        .base-icon {
+          box-sizing: border-box;
+          flex-shrink: 0;
+          width: 42rpx;
+          height: 42rpx;
+          border: 4rpx solid #bbb;
+          border-radius: 50%;
+        }
+        .man-img {
+          flex-shrink: 0;
+          width: 216rpx;
+          height: 216rpx;
+          margin-left: 90rpx;
+          background-image: url(@/static/images/login/man.svg);
+          background-size: cover;
+        }
+        .base-text {
+          margin-left: 16rpx;
+          font-family: 'PingFang SC';
+          font-size: 48rpx;
+          font-style: normal;
+          font-weight: 600;
+          line-height: 64rpx; /* 133.333% */
+          color: var(--ff-4-c-1-b, $shop-primary);
+        }
+      }
+      .base-active {
+        background: #ffe6e0;
+        border: 2rpx solid var(--ff-4-c-1-b, $shop-primary);
+      }
+    }
+    .gender-footer {
+      box-sizing: border-box;
+      display: flex;
+      flex-shrink: 0;
+      align-items: center;
+      justify-content: center;
+      width: 100%;
+      height: 84rpx;
+      padding: 10rpx 66rpx;
+      margin-top: 220rpx;
+      font-family: 'PingFang SC';
+      font-size: 32rpx;
+      font-style: normal;
+      font-weight: 600;
+      line-height: 48rpx; /* 150% */
+      color: $shop-white;
+      text-align: center;
+      text-transform: capitalize;
+      background: var(
+        --ff-4-c-1-bfc-7-b-1-c,
+        linear-gradient(270deg, $shop-primary 0%, $shop-help 100%)
+      );
+      border-radius: 160rpx;
+    }
+  }
+</style>

+ 130 - 0
src/sku.json

@@ -0,0 +1,130 @@
+{
+  "id": "1898983477722800130",
+  "productName": "旗舰智能手机 X200",
+  "mainImageUrl": "https://v-xiaoyaotravel.obs.cn-north-4.myhuaweicloud.com:443/%2Fservice%2Fchunk%2Fcca8a94f-8ff5-40eb-9d23-81dd52cb63f81741405918609%2FA.png",
+  "images": null,
+  "description": "6.7英寸AMOLED屏幕 | 骁龙8 Gen3处理器 | 1亿像素三摄 | 5000mAh大电池",
+  "defaultPrice": 6999.0,
+  "specSortVoList": [
+    {
+      "id": "1898983478557544448",
+      "specName": "颜色",
+      "sortOrder": 0,
+      "list": [
+        {
+          "id": "1898983478557544449",
+          "specValue": "曜石黑",
+          "sortOrder": 0
+        },
+        {
+          "id": "1898983478557544450",
+          "specValue": "深海蓝",
+          "sortOrder": 0
+        }
+      ]
+    },
+    {
+      "id": "1898983478557544451",
+      "specName": "存储容量",
+      "sortOrder": 0,
+      "list": [
+        {
+          "id": "1898983478557544452",
+          "specValue": "128GB",
+          "sortOrder": 0
+        },
+        {
+          "id": "1898983478557544453",
+          "specValue": "256GB",
+          "sortOrder": 0
+        }
+      ]
+    }
+  ],
+  "skuList": [
+    {
+      "id": "1898983481057271810",
+      "skuId": "55555555555",
+      "productId": "1898983477722800130",
+      "productName": "旗舰智能手机 X200",
+      "description": "6.7英寸AMOLED屏幕 | 骁龙8 Gen3处理器 | 1亿像素三摄 | 5000mAh大电池",
+      "warehouseId": "111",
+      "inventory": 48,
+      "status": 0,
+      "price": 6999.0,
+      "imageUrls": [
+        {
+          "filePath": "1111111",
+          "fileType": "png"
+        },
+        {
+          "filePath": "1111111",
+          "fileType": "png"
+        }
+      ],
+      "salesVolume": 300,
+      "barcodes": 123,
+      "costPrice": 5500.0,
+      "specValue": [
+        {
+          "id": "1898983480738504706",
+          "skuId": "55555555555",
+          "specValueId": "1898983478557544449",
+          "specValue": "曜石黑",
+          "specId": "1898983478557544448",
+          "specName": "颜色"
+        },
+        {
+          "id": "1898983480738504707",
+          "skuId": "55555555555",
+          "specValueId": "1898983478557544452",
+          "specValue": "128GB",
+          "specId": "1898983478557544451",
+          "specName": "存储容量"
+        }
+      ]
+    },
+    {
+      "id": "1898983481120186370",
+      "skuId": "666666666",
+      "productId": "1898983477722800130",
+      "productName": "旗舰智能手机 X200",
+      "description": "6.7英寸AMOLED屏幕 | 骁龙8 Gen3处理器 | 1亿像素三摄 | 5000mAh大电池",
+      "warehouseId": "111",
+      "inventory": 28,
+      "status": 0,
+      "price": 7699.0,
+      "imageUrls": [
+        {
+          "filePath": "1111111",
+          "fileType": "png"
+        },
+        {
+          "filePath": "1111111",
+          "fileType": "png"
+        }
+      ],
+      "salesVolume": 150,
+      "barcodes": 333,
+      "costPrice": 6200.0,
+      "specValue": [
+        {
+          "id": "1898983480738504708",
+          "skuId": "666666666",
+          "specValueId": "1898983478557544450",
+          "specValue": "深海蓝",
+          "specId": "1898983478557544448",
+          "specName": "颜色"
+        },
+        {
+          "id": "1898983480738504709",
+          "skuId": "666666666",
+          "specValueId": "1898983478557544453",
+          "specValue": "256GB",
+          "specId": "1898983478557544451",
+          "specName": "存储容量"
+        }
+      ]
+    }
+  ]
+}

+ 5 - 0
src/static/images/cart/cart-close.svg

@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="close">
+<path id="union" d="M12 13.3805L16.6195 18L18 16.6194L13.3806 12L18 7.38057L16.6195 6.00004L12 10.6195L7.38053 6L6 7.38053L10.6195 12L6 16.6195L7.38053 18L12 13.3805Z" fill="#999999"/>
+</g>
+</svg>

+ 118 - 0
src/static/images/cart/empty.svg

@@ -0,0 +1,118 @@
+<svg width="131" height="126" viewBox="0 0 131 126" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="img">
+<path id="&#232;&#183;&#175;&#229;&#190;&#132; 98" fill-rule="evenodd" clip-rule="evenodd" d="M92.4465 0.300415C97.8923 0.300415 135.878 9.83084 125.939 73.7809C115.999 137.731 24.8722 137.475 5.18611 99.4817C-14.5 61.4887 33.7535 48.7705 46.9256 38.673C66.8373 23.4089 76.1789 0.300415 92.4465 0.300415Z" fill="url(#paint0_linear_4369_1881)"/>
+<g id="&#231;&#155;&#146;&#229;&#173;&#144;">
+<path id="&#232;&#183;&#175;&#229;&#190;&#132;" fill-rule="evenodd" clip-rule="evenodd" d="M116.739 105.498L85.063 117.815L77.5906 114.532V49L121.448 59.7383L116.739 105.498Z" fill="#FD5E33"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132;_2" fill-rule="evenodd" clip-rule="evenodd" d="M77.5905 114.532L46.6497 104.381L42.4014 59.0942L77.5905 49V114.532Z" fill="url(#paint1_linear_4369_1881)"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132; 30" fill-rule="evenodd" clip-rule="evenodd" d="M42.4014 59.0942V107.317L85.063 118.736V71.8816L42.4014 59.0942Z" fill="url(#paint2_linear_4369_1881)"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132; 46" opacity="0.8" d="M52.5134 87.1506V90.0497L68.9391 95.7047V92.7804L52.5134 87.1506Z" fill="#FFF7F4"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132; 47" opacity="0.8" d="M52.5134 97.5757V100.441L66.2032 106V102.858L52.5134 97.5757Z" fill="#FFF7F4"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132; 31" fill-rule="evenodd" clip-rule="evenodd" d="M85.063 71.8816V118.736L121.448 105.767V59.7383L85.063 71.8816Z" fill="#FE815F"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132;_3" fill-rule="evenodd" clip-rule="evenodd" d="M85.063 71.8816L91.5841 93.3652L121.448 82.2297V59.7383L85.063 71.8816Z" fill="#F84615"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132;_4" fill-rule="evenodd" clip-rule="evenodd" d="M85.063 71.8816L93.9559 89.9004L130.32 74.8194L121.448 59.7383L85.063 71.8816Z" fill="url(#paint3_linear_4369_1881)"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132; 2" fill-rule="evenodd" clip-rule="evenodd" d="M42.4015 59.0942L38.7988 77.5039L85.0602 93.325V71.8816L42.4015 59.0942Z" fill="#FD8767"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132; 2_2" fill-rule="evenodd" clip-rule="evenodd" d="M42.4015 59.0942L38.7988 77.5039L81.4265 87.8878L85.0602 71.8816L42.4015 59.0942Z" fill="url(#paint4_linear_4369_1881)"/>
+</g>
+<g id="&#230;&#164;&#173;&#229;&#156;&#134;&#229;&#189;&#162;" filter="url(#filter0_i_4369_1881)">
+<circle cx="79.5" cy="43" r="10" fill="url(#paint5_radial_4369_1881)"/>
+</g>
+<g id="&#230;&#164;&#173;&#229;&#156;&#134;&#229;&#189;&#162;&#229;&#164;&#135;&#228;&#187;&#189;" filter="url(#filter1_i_4369_1881)">
+<circle cx="109.5" cy="26" r="6" fill="url(#paint6_linear_4369_1881)"/>
+</g>
+<g id="&#230;&#164;&#173;&#229;&#156;&#134;&#229;&#189;&#162;&#229;&#164;&#135;&#228;&#187;&#189; 2" opacity="0.5" filter="url(#filter2_i_4369_1881)">
+<circle cx="30" cy="54.5" r="4.5" fill="url(#paint7_linear_4369_1881)"/>
+</g>
+<g id="&#230;&#164;&#173;&#229;&#156;&#134;&#229;&#189;&#162;&#229;&#164;&#135;&#228;&#187;&#189; 3" opacity="0.3" filter="url(#filter3_i_4369_1881)">
+<circle cx="81" cy="15.5" r="3.5" fill="url(#paint8_linear_4369_1881)"/>
+</g>
+<g id="&#232;&#141;&#137;">
+<path id="&#232;&#183;&#175;&#229;&#190;&#132; 100" fill-rule="evenodd" clip-rule="evenodd" d="M33.6215 65.0853C33.6215 65.0853 17.6059 92.8799 16.6145 100.663H15.25C15.25 100.663 12.7614 83.6069 17.3469 76.9397C25.5 65.0853 33.6215 65.0853 33.6215 65.0853Z" fill="#FC3701"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132; 100_2" opacity="0.539039" fill-rule="evenodd" clip-rule="evenodd" d="M33.6215 65.0853C33.6215 65.0853 24.0139 75.8072 22.7137 79.3558C22.0385 81.1984 14.3774 90.4918 14.3774 90.4918C14.314 84.4276 15.3038 79.9103 17.3469 76.9397C25.5 65.0853 33.6215 65.0853 33.6215 65.0853Z" fill="#FEB8A5"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132; 99" fill-rule="evenodd" clip-rule="evenodd" d="M19.2286 114.434C19.2286 114.434 20.8705 98.1597 35.5763 88.5496C35.5763 88.5496 23.4398 83.2857 15.3804 95.3464C8.40071 105.791 18.0535 108.78 18.0535 108.78V113.734L19.2286 114.53V114.434Z" fill="#FC3701"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132; 99_2" fill-rule="evenodd" clip-rule="evenodd" d="M18.0535 101.565C21.3857 98.7725 21.3857 92.2515 25.696 92.2515C26.8253 92.2515 29.0789 91.5418 30.565 90.4917C31.6537 89.7224 34.3413 89.3567 35.5763 88.5496C35.5763 88.5496 23.4398 83.2857 15.3804 95.3464C8.40071 105.791 18.0535 108.78 18.0535 108.78C18.0535 108.78 16.0836 103.216 18.0535 101.565Z" fill="#FEB8A5"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132; 101" fill-rule="evenodd" clip-rule="evenodd" d="M12.4514 109.141V93.7094C12.4514 93.7094 16.9096 70.8834 10.0418 66.9417C10.0418 66.9417 2.45015 79.9963 10.0418 95.6272C10.0418 95.6272 10.1437 99.3927 10.3476 106.924L12.4514 109.141Z" fill="#FC3701"/>
+<path id="&#232;&#183;&#175;&#229;&#190;&#132; 101_2" opacity="0.536923" fill-rule="evenodd" clip-rule="evenodd" d="M11.882 91.7717C12.7399 89.1433 16.9096 70.8834 10.0418 66.9417C10.0418 66.9417 2.45015 79.9963 10.0418 95.6272C10.0418 95.6272 11.0242 94.4001 11.882 91.7717Z" fill="#FEB8A5"/>
+</g>
+</g>
+<defs>
+<filter id="filter0_i_4369_1881" x="69.5" y="33" width="20" height="20" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="3"/>
+<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
+<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.2 0"/>
+<feBlend mode="normal" in2="shape" result="effect1_innerShadow_4369_1881"/>
+</filter>
+<filter id="filter1_i_4369_1881" x="103.5" y="20" width="12" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="3"/>
+<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
+<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.2 0"/>
+<feBlend mode="normal" in2="shape" result="effect1_innerShadow_4369_1881"/>
+</filter>
+<filter id="filter2_i_4369_1881" x="25.5" y="50" width="9" height="9" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="3"/>
+<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
+<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.2 0"/>
+<feBlend mode="normal" in2="shape" result="effect1_innerShadow_4369_1881"/>
+</filter>
+<filter id="filter3_i_4369_1881" x="77.5" y="12" width="7" height="7" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="3"/>
+<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
+<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.2 0"/>
+<feBlend mode="normal" in2="shape" result="effect1_innerShadow_4369_1881"/>
+</filter>
+<linearGradient id="paint0_linear_4369_1881" x1="127.589" y1="62.8812" x2="0.527588" y2="62.8812" gradientUnits="userSpaceOnUse">
+<stop stop-color="#FF4C1B"/>
+<stop offset="1" stop-color="#FC7B1C"/>
+</linearGradient>
+<linearGradient id="paint1_linear_4369_1881" x1="69.2509" y1="46.7015" x2="57.9003" y2="53.8864" gradientUnits="userSpaceOnUse">
+<stop stop-color="#FD3600"/>
+<stop offset="1" stop-color="#FD3600"/>
+</linearGradient>
+<linearGradient id="paint2_linear_4369_1881" x1="26.5717" y1="88.7495" x2="75.4419" y2="122.816" gradientUnits="userSpaceOnUse">
+<stop stop-color="#F7BDAE"/>
+<stop offset="1" stop-color="#FFA78F"/>
+</linearGradient>
+<linearGradient id="paint3_linear_4369_1881" x1="132.842" y1="60.9422" x2="91.9383" y2="53.5078" gradientUnits="userSpaceOnUse">
+<stop stop-color="#FE9477"/>
+<stop offset="1" stop-color="#FE9578"/>
+</linearGradient>
+<linearGradient id="paint4_linear_4369_1881" x1="30.0773" y1="73.7097" x2="48.7738" y2="102.574" gradientUnits="userSpaceOnUse">
+<stop stop-color="#FDB19C"/>
+<stop offset="1" stop-color="#FEDFD7"/>
+</linearGradient>
+<radialGradient id="paint5_radial_4369_1881" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(74.3085 43) rotate(-90) scale(13.2339)">
+<stop stop-color="white"/>
+<stop offset="1" stop-color="#FE9275"/>
+</radialGradient>
+<linearGradient id="paint6_linear_4369_1881" x1="115.5" y1="20" x2="103.5" y2="20" gradientUnits="userSpaceOnUse">
+<stop stop-color="#FD8665"/>
+<stop stop-color="#FF7D59"/>
+<stop offset="1" stop-color="#FFC5B5"/>
+</linearGradient>
+<linearGradient id="paint7_linear_4369_1881" x1="34.5" y1="50" x2="25.5" y2="50" gradientUnits="userSpaceOnUse">
+<stop stop-color="#FD8665"/>
+<stop stop-color="#FF7D59"/>
+<stop offset="1" stop-color="#FFC5B5"/>
+</linearGradient>
+<linearGradient id="paint8_linear_4369_1881" x1="84.5" y1="12" x2="77.5" y2="12" gradientUnits="userSpaceOnUse">
+<stop stop-color="#FD8665"/>
+<stop stop-color="#FF7D59"/>
+<stop offset="1" stop-color="#FFC5B5"/>
+</linearGradient>
+</defs>
+</svg>

BIN
src/static/images/cart/sku-img1.png


+ 7 - 0
src/static/images/home/home-search.svg

@@ -0,0 +1,7 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Frame">
+<path id="Vector" d="M8.75 15.8333C12.662 15.8333 15.8333 12.662 15.8333 8.75C15.8333 4.838 12.662 1.66666 8.75 1.66666C4.838 1.66666 1.66667 4.838 1.66667 8.75C1.66667 12.662 4.838 15.8333 8.75 15.8333Z" stroke="#FF4C1B" stroke-width="1.25" stroke-linejoin="round"/>
+<path id="Vector_2" d="M11.1071 5.97629C10.5039 5.37308 9.67053 5 8.75003 5C7.82957 5 6.99623 5.37308 6.39303 5.97629" stroke="#FF4C1B" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
+<path id="Vector_3" d="M13.8424 13.8424L17.3779 17.3779" stroke="#FF4C1B" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+</svg>

+ 5 - 0
src/static/images/home/msg.svg

@@ -0,0 +1,5 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="&#230;&#182;&#136;&#230;&#129;&#175;">
+<path id="Subtract" fill-rule="evenodd" clip-rule="evenodd" d="M10.2713 19.1521C11.7103 19.1521 12.8592 18.1081 12.8617 16.8193H7.68084C7.68346 18.1124 8.84227 19.1521 10.2713 19.1521ZM10.2713 1.66667C11.225 1.66667 11.9982 2.36415 11.9982 3.22083C11.9982 3.30629 11.9905 3.39158 11.9751 3.47563C14.4864 4.22157 16.3156 6.51975 16.3156 9.24126V11.4023C16.3156 11.4023 16.3156 14.4893 17.1792 14.4893C17.6561 14.4893 18.0426 14.8342 18.0426 15.2663C18.0426 15.6956 17.6583 16.0435 17.1792 16.0435H3.3634C2.88643 16.0435 2.5 15.6986 2.5 15.2663C2.5 14.8372 2.8842 14.4893 3.38564 14.4893C4.22689 14.4624 4.22689 11.3808 4.22689 11.3808V9.24087C4.22689 6.52306 6.18312 4.22575 8.56785 3.47766C8.5524 3.39403 8.54434 3.30825 8.54434 3.22083C8.54434 2.36259 9.31941 1.66667 10.2713 1.66667ZM11.0417 5.83333C11.0417 5.48816 10.7618 5.20833 10.4167 5.20833C10.0715 5.20833 9.79167 5.48816 9.79167 5.83333V9.58333C9.79167 9.92851 10.0715 10.2083 10.4167 10.2083C10.7618 10.2083 11.0417 9.92851 11.0417 9.58333V5.83333Z" fill="#333333"/>
+</g>
+</svg>

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 9 - 0
src/static/images/login/login-bg.svg


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 3 - 0
src/static/images/login/login-logo.svg


+ 5 - 0
src/static/images/login/login-right.svg

@@ -0,0 +1,5 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Left (&#229;&#183;&#166;)">
+<path id="Vector" d="M8.5 18L14.5 12L8.5 6" stroke="#999999" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+</svg>

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 2 - 0
src/static/images/login/login-title.svg


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 5 - 0
src/static/images/login/man.svg


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 6 - 0
src/static/images/login/test-code.svg


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 15 - 0
src/static/images/login/woman.svg


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно