Browse Source

修复若干显示问题

Pchen. 3 weeks ago
parent
commit
270cbcf87b

+ 168 - 0
src/components/store/OrderProgressSteps.vue

@@ -0,0 +1,168 @@
+<template>
+  <div v-if="mobile" class="mobile-order-steps">
+    <div
+      v-for="(label, index) in labels"
+      :key="index"
+      class="mobile-order-steps__col"
+      :class="{ 'mobile-order-steps__col--connector-active': isConnectorActive(index) }"
+    >
+      <div
+        class="mobile-order-steps__node"
+        :class="`mobile-order-steps__node--${stepState(index)}`"
+      >
+        <icon-close v-if="stepState(index) === 'error'" />
+        <icon-check v-else-if="stepState(index) === 'finish'" />
+        <span v-else class="mobile-order-steps__num">{{ index + 1 }}</span>
+      </div>
+      <div class="mobile-order-steps__label">{{ label }}</div>
+    </div>
+  </div>
+  <a-steps
+    v-else
+    :current="current"
+    :status="status"
+    direction="horizontal"
+    label-placement="vertical"
+    class="app-order-steps"
+  >
+    <a-step
+      v-for="(label, index) in labels"
+      :key="index"
+      :description="descriptions[index] || ''"
+    >
+      {{ label }}
+    </a-step>
+  </a-steps>
+</template>
+
+<script setup>
+const props = defineProps({
+  mobile: { type: Boolean, default: false },
+  current: { type: Number, default: 1 },
+  status: { type: String, default: 'process' },
+  labels: { type: Array, default: () => ['', '', ''] },
+  descriptions: { type: Array, default: () => ['', '', ''] }
+})
+
+const stepState = (index) => {
+  const num = index + 1
+  if (num < props.current) return 'finish'
+  if (num > props.current) return 'wait'
+  return props.status
+}
+
+const isConnectorActive = (index) => {
+  if (props.status === 'error' && props.current === 1) return false
+  return props.current > index + 1
+}
+</script>
+
+<style scoped lang="less">
+@import '@/styles/store-theme.less';
+
+.mobile-order-steps {
+  display: flex;
+  align-items: flex-start;
+  width: 100%;
+  padding: 8px 0 4px;
+  box-sizing: border-box;
+}
+
+.mobile-order-steps__col {
+  position: relative;
+  flex: 1 1 0;
+  min-width: 0;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.mobile-order-steps__col:not(:last-child)::after {
+  content: '';
+  position: absolute;
+  top: 13px;
+  left: calc(50% + 13px);
+  right: 0;
+  height: 1px;
+  background: var(--color-neutral-3);
+  pointer-events: none;
+}
+
+.mobile-order-steps__col--connector-active:not(:last-child)::after {
+  background: rgb(var(--primary-6));
+}
+
+.mobile-order-steps__node {
+  position: relative;
+  z-index: 1;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 26px;
+  height: 26px;
+  border-radius: 50%;
+  font-size: 13px;
+  font-weight: 500;
+  box-sizing: border-box;
+  border: 1px solid transparent;
+}
+
+.mobile-order-steps__node--wait {
+  color: var(--color-text-3);
+  background: var(--color-fill-2);
+  border-color: var(--color-fill-2);
+}
+
+.mobile-order-steps__node--process {
+  color: #fff;
+  background: rgb(var(--primary-6));
+  border-color: rgb(var(--primary-6));
+}
+
+.mobile-order-steps__node--finish {
+  color: rgb(var(--primary-6));
+  background: var(--color-primary-light-1);
+  border-color: rgb(var(--primary-6));
+}
+
+.mobile-order-steps__node--error {
+  color: #fff;
+  background: rgb(var(--danger-6));
+  border-color: rgb(var(--danger-6));
+}
+
+.mobile-order-steps__num {
+  line-height: 1;
+}
+
+.mobile-order-steps__label {
+  margin-top: 6px;
+  max-width: 100%;
+  padding: 0 2px;
+  font-size: 11px;
+  line-height: 1.3;
+  text-align: center;
+  color: var(--color-text-2);
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.app-order-steps {
+  max-width: 100%;
+  margin: 8px 0 16px;
+
+  :deep(.arco-steps-item-node) {
+    line-height: 1;
+    vertical-align: top;
+  }
+
+  :deep(.arco-steps-icon) {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    box-sizing: border-box;
+    line-height: 1;
+  }
+}
+</style>

+ 16 - 32
src/pages/admin/goods/orderDetail.vue

@@ -5,11 +5,14 @@
       <div v-if="!loading && data.orderId" class="detail-grid">
       <div v-if="!loading && data.orderId" class="detail-grid">
         <a-card :bordered="false" class="detail-card detail-card--status">
         <a-card :bordered="false" class="detail-card detail-card--status">
           <template #title>订单进度</template>
           <template #title>订单进度</template>
-          <a-steps :current="stepCurrent" :status="stepStatus" label-placement="vertical" class="order-steps">
-            <a-step :description="isMobile ? '' : '用户提交订单'">待支付</a-step>
-            <a-step :description="isMobile ? '' : '支付成功,系统处理'">待处理</a-step>
-            <a-step :description="isMobile ? '' : '权益已发放'">已完成</a-step>
-          </a-steps>
+          <OrderProgressSteps
+            class="order-steps"
+            :mobile="isMobile"
+            :current="stepCurrent"
+            :status="stepStatus"
+            :labels="adminStepLabels"
+            :descriptions="adminStepDescriptions"
+          />
           <div class="state-center">
           <div class="state-center">
             <a-tag :color="stateMeta.color" size="large">{{ stateMeta.label }}</a-tag>
             <a-tag :color="stateMeta.color" size="large">{{ stateMeta.label }}</a-tag>
           </div>
           </div>
@@ -70,6 +73,7 @@ import { ref, computed, onMounted, onUnmounted } from 'vue'
 import { useRoute } from 'vue-router'
 import { useRoute } from 'vue-router'
 import { adminOrderDetail } from '@/api/order'
 import { adminOrderDetail } from '@/api/order'
 import { Notification } from '@arco-design/web-vue'
 import { Notification } from '@arco-design/web-vue'
+import OrderProgressSteps from '@/components/store/OrderProgressSteps.vue'
 import {
 import {
   formatStoreTimeFull,
   formatStoreTimeFull,
   getPayTypeLabel,
   getPayTypeLabel,
@@ -84,6 +88,13 @@ const goodsContent = ref('')
 const isMobile = ref(false)
 const isMobile = ref(false)
 let resizeHandler = null
 let resizeHandler = null
 
 
+const adminStepLabels = ['待支付', '待处理', '已完成']
+const adminStepDescriptions = computed(() =>
+  isMobile.value
+    ? ['', '', '']
+    : ['用户提交订单', '支付成功,系统处理', '权益已发放']
+)
+
 const stateMeta = computed(() => getOrderStateMeta(data.value?.state ?? -1))
 const stateMeta = computed(() => getOrderStateMeta(data.value?.state ?? -1))
 
 
 const stepCurrent = computed(() => {
 const stepCurrent = computed(() => {
@@ -173,10 +184,6 @@ onUnmounted(() => {
   color: #e85d04;
   color: #e85d04;
 }
 }
 
 
-.order-steps {
-  margin-top: 4px;
-}
-
 .goods-content {
 .goods-content {
   line-height: 1.75;
   line-height: 1.75;
 
 
@@ -190,29 +197,6 @@ onUnmounted(() => {
     padding: 0 12px 16px;
     padding: 0 12px 16px;
   }
   }
 
 
-  .order-steps {
-    overflow: visible;
-  }
-
-  .order-steps :deep(.arco-steps-item) {
-    flex: 1 1 0;
-    min-width: 0;
-  }
-
-  .order-steps :deep(.arco-steps-item-title) {
-    font-size: 11px;
-    white-space: nowrap;
-  }
-
-  .order-steps :deep(.arco-steps-item-description) {
-    display: none;
-  }
-
-  .order-steps :deep(.arco-steps-item-content) {
-    width: 100%;
-    min-width: 0;
-  }
-
   :deep(.arco-descriptions-item-value) {
   :deep(.arco-descriptions-item-value) {
     white-space: normal;
     white-space: normal;
     word-break: break-all;
     word-break: break-all;

+ 42 - 57
src/pages/store/orders/orderDetail/index.vue

@@ -20,17 +20,14 @@
         </div>
         </div>
 
 
         <a-card :bordered="false" class="status-card">
         <a-card :bordered="false" class="status-card">
-          <a-steps
+          <OrderProgressSteps
+            class="steps"
+            :mobile="isMobile"
             :current="stepCurrent"
             :current="stepCurrent"
             :status="stepStatus"
             :status="stepStatus"
-            direction="horizontal"
-            label-placement="vertical"
-            class="steps"
-          >
-            <a-step :description="displayStepDescriptions[0]">{{ stepLabels[0] }}</a-step>
-            <a-step :description="displayStepDescriptions[1]">{{ stepLabels[1] }}</a-step>
-            <a-step :description="displayStepDescriptions[2]">{{ stepLabels[2] }}</a-step>
-          </a-steps>
+            :labels="stepLabels"
+            :descriptions="stepDescriptions"
+          />
           <div class="status-badge-wrap">
           <div class="status-badge-wrap">
             <OrderStateTag :state="data?.state ?? 0" />
             <OrderStateTag :state="data?.state ?? 0" />
           </div>
           </div>
@@ -85,6 +82,7 @@ import { orderDeatil } from '@/api/order'
 import { useRoute } from 'vue-router'
 import { useRoute } from 'vue-router'
 import { Notification, Message } from '@arco-design/web-vue'
 import { Notification, Message } from '@arco-design/web-vue'
 import OrderStateTag from '@/components/store/OrderStateTag.vue'
 import OrderStateTag from '@/components/store/OrderStateTag.vue'
+import OrderProgressSteps from '@/components/store/OrderProgressSteps.vue'
 import {
 import {
   formatStoreTimeFull,
   formatStoreTimeFull,
   getPayTypeLabel,
   getPayTypeLabel,
@@ -108,12 +106,9 @@ const stepCurrent = ref(1)
 const stepStatus = ref('process')
 const stepStatus = ref('process')
 const stepLabels = ref(['待支付', '待处理', '已完成'])
 const stepLabels = ref(['待支付', '待处理', '已完成'])
 const stepDescriptions = ref(['', '', ''])
 const stepDescriptions = ref(['', '', ''])
-const displayStepDescriptions = computed(() =>
-  isMobile.value ? ['', '', ''] : stepDescriptions.value
-)
-
 const PAY_TIMEOUT_SEC = 300
 const PAY_TIMEOUT_SEC = 300
-let timer = null
+let pollTimer = null
+let countdownTimer = null
 let resizeHandler = null
 let resizeHandler = null
 
 
 const updateStepState = () => {
 const updateStepState = () => {
@@ -126,25 +121,26 @@ const updateStepState = () => {
       stepStatus.value = 'process'
       stepStatus.value = 'process'
       stepLabels.value = ['待支付', '待处理', '已完成']
       stepLabels.value = ['待支付', '待处理', '已完成']
       updatePaymentCountdown()
       updatePaymentCountdown()
+      startCountdownTicker()
       break
       break
     case 1:
     case 1:
       stepCurrent.value = 2
       stepCurrent.value = 2
       stepStatus.value = 'process'
       stepStatus.value = 'process'
       stepDescriptions.value[1] = '支付成功,等待系统处理'
       stepDescriptions.value[1] = '支付成功,等待系统处理'
-      stopPolling()
+      stopPendingOrderTimers()
       break
       break
     case 2:
     case 2:
       stepCurrent.value = 3
       stepCurrent.value = 3
       stepStatus.value = 'finish'
       stepStatus.value = 'finish'
       stepDescriptions.value[2] = '权益已发放至账户'
       stepDescriptions.value[2] = '权益已发放至账户'
-      stopPolling()
+      stopPendingOrderTimers()
       break
       break
     case 3:
     case 3:
       stepCurrent.value = 1
       stepCurrent.value = 1
       stepStatus.value = 'error'
       stepStatus.value = 'error'
       stepLabels.value = ['已关闭', '待处理', '已完成']
       stepLabels.value = ['已关闭', '待处理', '已完成']
       stepDescriptions.value[0] = '订单超时或已取消'
       stepDescriptions.value[0] = '订单超时或已取消'
-      stopPolling()
+      stopPendingOrderTimers()
       break
       break
     default:
     default:
       break
       break
@@ -191,9 +187,27 @@ const getOrderDetail = async () => {
   }
   }
 }
 }
 
 
+const startCountdownTicker = () => {
+  if (countdownTimer) return
+  countdownTimer = setInterval(() => {
+    if (data.value?.state !== 0) {
+      stopCountdownTicker()
+      return
+    }
+    updatePaymentCountdown()
+  }, 1000)
+}
+
+const stopCountdownTicker = () => {
+  if (countdownTimer) {
+    clearInterval(countdownTimer)
+    countdownTimer = null
+  }
+}
+
 const startPolling = () => {
 const startPolling = () => {
-  if (timer) return
-  timer = setInterval(async () => {
+  if (pollTimer) return
+  pollTimer = setInterval(async () => {
     if (data.value?.state !== 0) {
     if (data.value?.state !== 0) {
       stopPolling()
       stopPolling()
       return
       return
@@ -203,12 +217,17 @@ const startPolling = () => {
 }
 }
 
 
 const stopPolling = () => {
 const stopPolling = () => {
-  if (timer) {
-    clearInterval(timer)
-    timer = null
+  if (pollTimer) {
+    clearInterval(pollTimer)
+    pollTimer = null
   }
   }
 }
 }
 
 
+const stopPendingOrderTimers = () => {
+  stopCountdownTicker()
+  stopPolling()
+}
+
 onMounted(async () => {
 onMounted(async () => {
   const syncMobile = () => {
   const syncMobile = () => {
     isMobile.value = window.innerWidth <= 768
     isMobile.value = window.innerWidth <= 768
@@ -223,7 +242,7 @@ onMounted(async () => {
 })
 })
 
 
 onUnmounted(() => {
 onUnmounted(() => {
-  stopPolling()
+  stopPendingOrderTimers()
   if (resizeHandler) window.removeEventListener('resize', resizeHandler)
   if (resizeHandler) window.removeEventListener('resize', resizeHandler)
 })
 })
 
 
@@ -313,11 +332,6 @@ function openPaymentWindow(payUrl, formData) {
   box-shadow: @store-shadow;
   box-shadow: @store-shadow;
 }
 }
 
 
-.steps {
-  max-width: 100%;
-  margin: 8px 0 16px;
-}
-
 .status-badge-wrap {
 .status-badge-wrap {
   text-align: center;
   text-align: center;
 }
 }
@@ -361,35 +375,6 @@ function openPaymentWindow(payUrl, formData) {
     padding: 14px 12px;
     padding: 14px 12px;
   }
   }
 
 
-  .steps {
-    margin: 4px 0 10px;
-    overflow: visible;
-  }
-
-  .steps :deep(.arco-steps-item) {
-    flex: 1 1 0;
-    min-width: 0;
-  }
-
-  .steps :deep(.arco-steps-item-content) {
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    text-align: center;
-    width: 100%;
-    min-width: 0;
-  }
-
-  .steps :deep(.arco-steps-item-title) {
-    font-size: 11px;
-    white-space: nowrap;
-    text-align: center;
-  }
-
-  .steps :deep(.arco-steps-item-description) {
-    display: none;
-  }
-
   .detail-actions {
   .detail-actions {
     display: grid;
     display: grid;
     grid-template-columns: 1fr;
     grid-template-columns: 1fr;

+ 42 - 10
src/pages/store/sendCountRecords/index.vue

@@ -3,17 +3,17 @@
     <Breadcrumb />
     <Breadcrumb />
 
 
     <a-card title="赠送记录">
     <a-card title="赠送记录">
-      <a-row>
-        <a-col :flex="1">
-          <a-form :model="queryData" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }"
-            label-align="left">
+      <a-row class="query-row">
+        <a-col :flex="'1000px'" class="query-main">
+          <a-form class="queryForm app-query-form" :model="queryData" :label-col-props="{ span: 6 }"
+            :wrapper-col-props="{ span: 18 }" label-align="left">
             <a-row :gutter="16">
             <a-row :gutter="16">
-              <a-col :span="8">
+              <a-col :span="12">
                 <a-form-item field="direction" label="记录类型">
                 <a-form-item field="direction" label="记录类型">
                   <a-select v-model="queryData.direction" :options="directionOptions" placeholder="请选择记录类型" />
                   <a-select v-model="queryData.direction" :options="directionOptions" placeholder="请选择记录类型" />
                 </a-form-item>
                 </a-form-item>
               </a-col>
               </a-col>
-              <a-col :span="8">
+              <a-col :span="12">
                 <a-form-item field="status" label="审核状态">
                 <a-form-item field="status" label="审核状态">
                   <a-select v-model="queryData.status" :options="statusOptions" placeholder="请选择审核状态" />
                   <a-select v-model="queryData.status" :options="statusOptions" placeholder="请选择审核状态" />
                 </a-form-item>
                 </a-form-item>
@@ -21,9 +21,9 @@
             </a-row>
             </a-row>
           </a-form>
           </a-form>
         </a-col>
         </a-col>
-        <a-divider style="height: 84px" direction="vertical" />
-        <a-col :flex="'86px'" style="text-align: right">
-          <a-space direction="vertical" :size="18">
+        <a-divider class="query-divider" style="height: 84px" direction="vertical" />
+        <a-col :flex="1" class="app-query-actions">
+          <a-space class="query-actions-space" direction="vertical" :size="18">
             <a-button type="primary" @click="search">
             <a-button type="primary" @click="search">
               <template #icon>
               <template #icon>
                 <icon-search />
                 <icon-search />
@@ -191,8 +191,40 @@ onMounted(() => {
 })
 })
 </script>
 </script>
 
 
-<style scoped>
+<style scoped lang="less">
+.query-row {
+  margin-bottom: 10px;
+}
+
+.query-main {
+  min-width: 0;
+}
+
+.query-actions-space {
+  width: 100%;
+}
+
 .table {
 .table {
   margin-top: 15px;
   margin-top: 15px;
 }
 }
+
+@media (max-width: 768px) {
+  .query-row {
+    display: block;
+  }
+
+  .query-divider {
+    display: none;
+  }
+
+  .query-actions-space {
+    margin-top: 4px;
+    flex-direction: row !important;
+    gap: 10px !important;
+  }
+
+  .query-actions-space :deep(.arco-btn) {
+    flex: 1;
+  }
+}
 </style>
 </style>

+ 1 - 0
src/styles/store-theme.less

@@ -339,3 +339,4 @@
     width: auto !important;
     width: auto !important;
   }
   }
 }
 }
+