Prechádzať zdrojové kódy

订单页面UI优化

skyline 3 mesiacov pred
rodič
commit
e43fbab022
2 zmenil súbory, kde vykonal 884 pridanie a 113 odobranie
  1. 438 12
      src/pages-charge/order/order.vue
  2. 446 101
      src/pages-charge/orders/orders.vue

+ 438 - 12
src/pages-charge/order/order.vue

@@ -1,19 +1,105 @@
 <template>
-  <view class="body" v-if="list">
-    <shadow-card :list="list" @on-discount="handleClickDesc">
-      <view class="flex-column flex-center pb-48">
-        <view class="fw-500 color-000">
-          <text class="fs-40 mr-8">¥</text>
-          <text class="fs-60 lh-60">{{ price }}</text>
+  <view class="page-container bg-f5f7fa" v-if="list">
+    <!-- 金额卡片 -->
+    <view class="amount-card bg-theme br-16 mx-32 mt-32 p-36 shadow-md">
+      <view class="flex-column items-center">
+        <view class="fs-26 color-fff mb-12">实付金额</view>
+        <view class="flex-align-center">
+          <text class="fs-44 color-fff mr-10">¥</text>
+          <text class="fs-64 fw-600 color-fff lh-64">{{ price }}</text>
         </view>
-        <view class="fs-26 mt-8" style="color: rgba(0, 0, 0, 0.4)"
-          >实付金额</view
+        <view class="fs-22 color-fff opacity-80 mt-12" v-if="orderDetail">
+          {{ orderDetail.chargeStatus === 2 ? '充电中' : '已完成' }}
+        </view>
+      </view>
+    </view>
+    
+    <!-- 订单信息卡片 -->
+    <view class="info-card bg-fff br-20 mx-32 mt-28 p-36 shadow-sm">
+      <!-- 充电站点 -->
+      <view class="info-item flex-align-center pb-32 border-b border-eee">
+        <view class="info-icon flex-center w-56 h-56 bg-theme-light br-10 mr-20">
+          <uni-icons type="location" size="26" color="#419D95"></uni-icons>
+        </view>
+        <view class="flex-grow">
+          <view class="fs-24 color-999 mb-8">充电站点</view>
+          <view class="fs-28 fw-500 color-333">{{ list[0]?.value }}</view>
+        </view>
+      </view>
+      
+      <!-- 充电详情 -->
+      <view class="charge-detail mt-32">
+        <view class="fs-28 fw-500 color-333 mb-24">充电详情</view>
+        
+        <view class="detail-grid">
+          <!-- 累计充电量 -->
+          <view class="detail-item flex-column items-center p-24 bg-f5f7fa br-12">
+            <view class="fs-24 color-999 mb-8">累计充电量</view>
+            <view class="fs-32 fw-600 color-333">{{ list[1]?.value }}</view>
+          </view>
+          
+          <!-- 充电用时 -->
+          <view class="detail-item flex-column items-center p-24 bg-f5f7fa br-12 ml-16">
+            <view class="fs-24 color-999 mb-8">充电用时</view>
+            <view class="fs-32 fw-600 color-333">{{ list[list.length - 2]?.value }}</view>
+          </view>
+        </view>
+      </view>
+      
+      <!-- 订单信息列表 -->
+      <view class="info-list mt-36">
+        <block v-for="(item, index) in list" :key="index">
+          <view 
+            class="info-row flex-align-center justify-between py-20" 
+            v-if="index > 1 && index < list.length - 2"
+          >
+            <view class="fs-26 color-666">
+              {{ item.label }}
+            </view>
+            <view 
+              class="fs-26" 
+              :class="{ 'color-theme': item.color === '#F43636', 'color-333': !item.color }"
+              :style="{ color: item.color ? '#419D95' : '' }"
+            >
+              {{ item.value }}
+              <uni-icons 
+                v-if="item.copy" 
+                type="copy" 
+                size="24" 
+                color="#999" 
+                class="ml-12"
+                @click="copyText(item.value)"
+              ></uni-icons>
+            </view>
+          </view>
+        </block>
+      </view>
+      
+      <!-- 优惠信息 -->
+      <view class="discount-info mt-32 pt-32 border-t border-eee">
+        <view 
+          class="flex-align-center justify-between cursor-pointer" 
+          @click="handleClickDesc"
+          v-if="orderDetail && orderDetail.discountAmount > 0"
         >
+          <view class="fs-26 color-666">优惠信息</view>
+          <view class="flex-align-center">
+            <view class="fs-26 color-theme mr-12">查看详情</view>
+            <uni-icons type="right" size="24" color="#999"></uni-icons>
+          </view>
+        </view>
       </view>
-    </shadow-card>
-
+    </view>
+    
+    <!-- 优惠券详情组件 -->
     <order-coupon-detail ref="order_coupon_detail_ref"></order-coupon-detail>
   </view>
+  
+  <!-- 加载状态 -->
+  <view class="loading-state flex-center" v-else>
+    <uni-icons type="spinner" size="40" color="#419D95" class="loading-icon"></uni-icons>
+    <view class="fs-28 color-999 ml-16">加载中...</view>
+  </view>
 </template>
 
 <script setup lang="ts">
@@ -21,6 +107,7 @@ import { fetchOrder} from "../../api/user";
 import { onLoad } from "@dcloudio/uni-app";
 import { ref } from "vue";
 import OrderCouponDetail from "@/components/order-coupon-detail/order-coupon-detail.vue";
+import { to } from "@/utils/navigate";
 
 const list = ref<any[]>();
 const price = ref();
@@ -33,6 +120,22 @@ const handleClickDesc = () => {
   order_coupon_detail_ref.value?.open(startChargeSeq,discountType);
 }
 
+// 复制文本
+const copyText = (text: string) => {
+  uni.setClipboardData({
+    data: text,
+    success() {
+      uni.showToast({
+        title: "复制成功",
+        icon: "success",
+        duration: 1500
+      });
+    }
+  });
+};
+
+
+
 onLoad((options: any) => {
   fetchOrder(options.id)
     .then((res) => {
@@ -118,7 +221,330 @@ onLoad((options: any) => {
 </script>
 
 <style lang="scss">
-.body {
-  padding: 30rpx 60rpx;
+.page-container {
+  min-height: 100vh;
+  padding-bottom: 40rpx;
+}
+
+/* 金额卡片 */
+.amount-card {
+  background: linear-gradient(135deg, #419D95 0%, #368a84 100%);
+  transition: all 0.3s ease;
+  
+  &:hover {
+    transform: translateY(-4rpx);
+    box-shadow: 0 8rpx 24rpx rgba(65, 157, 149, 0.3);
+  }
+}
+
+/* 信息卡片 */
+.info-card {
+  transition: all 0.3s ease;
+  
+  &:hover {
+    transform: translateY(-2rpx);
+    box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
+  }
+}
+
+/* 充电详情网格 */
+.detail-grid {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 16rpx;
+}
+
+.detail-item {
+  transition: all 0.3s ease;
+  
+  &:hover {
+    transform: translateY(-2rpx);
+    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+  }
+}
+
+/* 操作按钮 */
+.btn-theme {
+  background: linear-gradient(135deg, #419D95 0%, #368a84 100%);
+  transition: all 0.3s ease;
+  box-shadow: 0 4rpx 12rpx rgba(65, 157, 149, 0.3);
+  
+  &:hover {
+    transform: translateY(-2rpx);
+    box-shadow: 0 6rpx 16rpx rgba(65, 157, 149, 0.4);
+  }
+  
+  &:active {
+    transform: translateY(0);
+  }
+}
+
+/* 加载状态 */
+.loading-state {
+  min-height: 60vh;
+}
+
+.loading-icon {
+  animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+/* 工具类 */
+.bg-f5f7fa {
+  background-color: #f5f7fa;
+}
+
+.bg-fff {
+  background-color: #ffffff;
+}
+
+.bg-primary {
+  background-color: #ff6b35;
+}
+
+.bg-primary-light {
+  background-color: rgba(255, 107, 53, 0.1);
+}
+
+.bg-success {
+  background-color: #4cd964;
+}
+
+.bg-success-light {
+  background-color: rgba(76, 217, 100, 0.1);
+}
+
+.bg-theme {
+  background-color: #419D95;
+}
+
+.bg-theme-light {
+  background-color: rgba(65, 157, 149, 0.1);
+}
+
+.br-12 {
+  border-radius: 12rpx;
+}
+
+.br-16 {
+  border-radius: 16rpx;
+}
+
+.br-20 {
+  border-radius: 20rpx;
+}
+
+.mx-32 {
+  margin-left: 32rpx;
+  margin-right: 32rpx;
+}
+
+.mt-28 {
+  margin-top: 28rpx;
+}
+
+.mt-32 {
+  margin-top: 32rpx;
+}
+
+.mb-16 {
+  margin-bottom: 16rpx;
+}
+
+.mb-24 {
+  margin-bottom: 24rpx;
+}
+
+.mb-40 {
+  margin-bottom: 40rpx;
+}
+
+.ml-12 {
+  margin-left: 12rpx;
+}
+
+.ml-16 {
+  margin-left: 16rpx;
+}
+
+.ml-24 {
+  margin-left: 24rpx;
+}
+
+.mt-8 {
+  margin-top: 8rpx;
+}
+
+.mt-16 {
+  margin-top: 16rpx;
+}
+
+.p-24 {
+  padding: 24rpx;
+}
+
+.p-36 {
+  padding: 36rpx;
+}
+
+.p-40 {
+  padding: 40rpx;
+}
+
+.pb-32 {
+  padding-bottom: 32rpx;
+}
+
+.pt-32 {
+  padding-top: 32rpx;
+}
+
+.py-20 {
+  padding-top: 20rpx;
+  padding-bottom: 20rpx;
+}
+
+.w-60 {
+  width: 60rpx;
+}
+
+.h-60 {
+  height: 60rpx;
+}
+
+.h-92 {
+  height: 92rpx;
+}
+
+.flex-center {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.flex-align-center {
+  display: flex;
+  align-items: center;
+}
+
+.flex-column {
+  display: flex;
+  flex-direction: column;
+}
+
+.flex-grow {
+  flex-grow: 1;
+}
+
+.justify-between {
+  justify-content: space-between;
+}
+
+.items-center {
+  align-items: center;
+}
+
+.border-b {
+  border-bottom: 1rpx solid #f0f0f0;
+}
+
+.border-t {
+  border-top: 1rpx solid #f0f0f0;
+}
+
+.border-eee {
+  border-color: #eeeeee;
+}
+
+.shadow-sm {
+  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+}
+
+.shadow-md {
+  box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
+}
+
+.color-333 {
+  color: #333333;
+}
+
+.color-666 {
+  color: #666666;
+}
+
+.color-999 {
+  color: #999999;
+}
+
+.color-fff {
+  color: #ffffff;
+}
+
+.color-primary {
+  color: #ff6b35;
+}
+
+.color-success {
+  color: #4cd964;
+}
+
+.color-theme {
+  color: #419D95;
+}
+
+.fs-24 {
+  font-size: 24rpx;
+}
+
+.fs-26 {
+  font-size: 26rpx;
+}
+
+.fs-28 {
+  font-size: 28rpx;
+}
+
+.fs-32 {
+  font-size: 32rpx;
+}
+
+.fs-48 {
+  font-size: 48rpx;
+}
+
+.fs-72 {
+  font-size: 72rpx;
+}
+
+.lh-72 {
+  line-height: 72rpx;
+}
+
+.fw-500 {
+  font-weight: 500;
+}
+
+.fw-600 {
+  font-weight: 600;
+}
+
+.opacity-80 {
+  opacity: 0.8;
+}
+
+.cursor-pointer {
+  cursor: pointer;
+}
+
+.w-full {
+  width: 100%;
 }
 </style>

+ 446 - 101
src/pages-charge/orders/orders.vue

@@ -1,108 +1,122 @@
 <template>
-  <view
-    class="pl-30 pr-30"
-    v-if="infiniteScroller.list && infiniteScroller.list.length"
-  >
-    <block v-for="(item, index) in infiniteScroller.list" :key="index">
-      <view
-        class="item flex-align-center"
-        @click="detail(index)"
-        v-if="!item.hidden"
-      >
-        <view class="mr-28" v-if="isInvoice">
-          <style-checkbox :checked="item.checked"></style-checkbox>
-        </view>
-
-
-        <view class="flex-column flex-grow">
-          <view class="flex-align-center ">
-            <view
-              class="fs-24 fw-500"
-              :style="{ opacity: item.invoiceStatus === 1 ? 0.5 : 1 }"
-              >订单号:{{item.startChargeSeq}}</view>
-
-            <view
-              v-if="
-                isInvoice &&
-                (item.invoiceStatus === 1 ||
-                  item.invoiceStatus === 2 ||
-                  item.invoiceStatus === 3)
-              "
-              class="flex-center ml-24 fs-24 color-000-6 width-96 height-46 br-8"
-              style="background: #e5e5e5"
-              >{{ isInvoiceTitles[item.invoiceStatus] }}</view
-            >
-          </view>
-
-          <view class="flex-inline">
-            <view class="fs-24 mt-10" style="color: rgba(0, 0, 0, 0.4)">
-              {{item.endTime}}
+  <view class="page-container">
+    <!-- 订单列表 -->
+    <view class="order-list pl-30 pr-30 pt-20" v-if="infiniteScroller.list && infiniteScroller.list.length">
+      <block v-for="(item, index) in infiniteScroller.list" :key="index">
+        <view 
+          class="order-card bg-fff br-16 mb-24 shadow-sm" 
+          @click="detail(index)"
+          v-if="!item.hidden"
+        >
+          <!-- 订单头部 -->
+          <view class="order-header pl-30 pr-30 pt-28" :class="{ 'invoice-mode': isInvoice }">
+            <!-- 复选框 -->
+            <view class="checkbox-container" v-if="isInvoice">
+              <style-checkbox :checked="item.checked" @click.stop></style-checkbox>
+            </view>
+            <view class="flex-align-center justify-between">
+              <view 
+                class="order-id fs-24 fw-500" 
+                :style="{ opacity: item.invoiceStatus === 1 ? 0.5 : 1 }"
+              >
+                <uni-icons type="document" size="20" color="#666" class="mr-12"></uni-icons>
+                订单号:{{item.startChargeSeq}}
+              </view>
+              
+              <!-- 发票状态标签 -->
+              <view 
+                v-if="
+                  isInvoice &&
+                  (item.invoiceStatus === 1 ||
+                    item.invoiceStatus === 2 ||
+                    item.invoiceStatus === 3)
+                "
+                class="invoice-status-tag flex-center fs-20 br-6 px-16 py-8"
+                :class="{
+                  'status-invoiced': item.invoiceStatus === 1,
+                  'status-cancelled': item.invoiceStatus === 2,
+                  'status-processing': item.invoiceStatus === 3
+                }"
+              >
+                {{ isInvoiceTitles[item.invoiceStatus] }}
+              </view>
+            </view>
           </view>
-            <view class="ml-auto" style="text-align: right">
-              <view class="fs-36 fw-500 ">
-                <text>{{ item.payAmount }}</text>
-                <text class="fs-24 ml-6">元</text>
-                  <uni-icons  class="ml-20" type="right" size="12" color="rgba(0,0,0,0.4)"></uni-icons>
+          
+          <!-- 订单信息 -->
+          <view class="order-info pl-30 pr-30 pb-28" :class="{ 'invoice-mode': isInvoice }">
+            <view class="flex-align-center justify-between">
+              <view class="order-time fs-24 color-999">
+                <uni-icons type="time" size="18" color="#999" class="mr-12"></uni-icons>
+                {{item.endTime || '未结束'}}
+              </view>
+              <view class="order-amount flex-align-center">
+                <view class="fs-32 fw-600 color-theme mr-8">¥{{ item.payAmount }}</view>
+                <uni-icons type="right" size="16" color="#ccc" class="ml-8"></uni-icons>
               </view>
             </view>
-
-
           </view>
-
         </view>
-
-
-      </view>
-
-    </block>
-  </view>
-
-  <view
-    class="pt-40 flex-center fs-30"
-    style="color: rgba(0, 0, 0, 0.6)"
-    v-if="infiniteScroller.list && infiniteScroller.list.length <= 0"
-    >暂无数据</view
-  >
-  <view class="invoice-placeholder" v-if="isInvoice"></view>
-  <view class="foot-placeholder"></view>
-
-  <style-bottom-view>
-    <view class="foot flex-align-center" v-if="!isInvoice">
-      <view
-        class="flex-center ml-auto mr-40 height-72 br-72"
-        style="width: 176rpx; border: 1rpx solid rgba(0, 0, 0, 0.2)"
-        @click="openInvoice"
-      >
-        <view class="fs-28 color-333 mr-12">开发票</view>
-        <uni-icons type="right" size="14" color="#333"></uni-icons>
-      </view>
+      </block>
     </view>
-    <view class="foot flex-align-center relative" v-else>
-      <view
-        class="height-56 flex-align-center absolute-top-left pl-30"
-        style="
-          background-color: #f6f7fa;
-          width: 100%;
-          transform: translateY(-100%);
-        "
-      >
-        <view class="fs-28 color-primary mr-8">{{ checkedCount }}</view>
-        <view class="fs-28 color-000">个订单,共</view>
-        <view class="fs-28 color-primary mr-8 ml-8">{{ checkedPrice }}</view>
-        <view class="fs-28 color-000">元</view>
+    
+    <!-- 空状态 -->
+    <view class="empty-state flex-center flex-column" v-if="infiniteScroller.list && infiniteScroller.list.length <= 0">
+      <uni-icons type="empty" size="80" color="#ccc"></uni-icons>
+      <view class="fs-30 color-999 mt-24">暂无订单数据</view>
+    </view>
+    
+    <!-- 占位符 -->
+    <view class="invoice-placeholder" v-if="isInvoice"></view>
+    <view class="foot-placeholder"></view>
+    
+    <!-- 底部操作栏 -->
+    <style-bottom-view>
+      <view class="foot flex-align-center" v-if="!isInvoice">
+        <view
+          class="flex-center ml-auto mr-40 height-72 br-72"
+          style="width: 176rpx; border: 1rpx solid rgba(0, 0, 0, 0.2)"
+          @click="openInvoice"
+        >
+          <view class="fs-28 color-333 mr-12">开发票</view>
+          <uni-icons type="right" size="14" color="#333"></uni-icons>
+        </view>
       </view>
-      <view class="flex-align-center ml-30" @click="checkPage">
-        <style-checkbox :checked="checkAll"></style-checkbox>
-        <view class="ml-16 fs-28 color666">本页全选</view>
+      <view class="foot flex-align-center relative" v-else>
+        <view
+          class="height-56 flex-align-center absolute-top-left pl-30"
+          style="
+            background-color: #f6f7fa;
+            width: 100%;
+            transform: translateY(-100%);
+          "
+        >
+          <view class="fs-28 color-theme mr-8">{{ checkedCount }}</view>
+          <view class="fs-28 color-000">个订单,共</view>
+          <view class="fs-28 color-theme mr-8 ml-8">{{ checkedPrice }}</view>
+          <view class="fs-28 color-000">元</view>
+        </view>
+        <view class="flex-align-center ml-30" @click="checkPage">
+          <style-checkbox :checked="checkAll"></style-checkbox>
+          <view class="ml-16 fs-28 color666">本页全选</view>
+        </view>
+        <view class="flex-align-center ml-auto mr-20">
+          <view
+            class="cancel-btn mr-20 border border-theme height-80 br-80 fs-32 fw-500 color-theme flex-center"
+            style="width: 180rpx"
+            @click="cancelInvoice"
+            >取消</view
+          >
+          <view
+            class="mr-40 bg-theme height-80 br-80 fs-32 fw-500 color-fff flex-center"
+            style="width: 216rpx"
+            @click="nextInvoice"
+            >下一步</view
+          >
+        </view>
       </view>
-      <view
-        class="ml-auto mr-40 bg-primary height-80 br-80 fs-32 fw-500 color-fff flex-center"
-        style="width: 216rpx"
-        @click="nextInvoice"
-        >下一步</view
-      >
-    </view>
-  </style-bottom-view>
+    </style-bottom-view>
+  </view>
 </template>
 
 <script setup lang="ts">
@@ -184,6 +198,15 @@ const detail = (index: number) => {
 const openInvoice = () => {
   isInvoice.value = true;
 };
+
+const cancelInvoice = () => {
+  isInvoice.value = false;
+  // 重置所有选中状态
+  infiniteScroller.list.forEach((item: any) => {
+    item.checked = false;
+  });
+  setCheckData();
+};
 // 获取发票历史
 const openInvoiceHistory = () => {
   uni.showLoading({
@@ -271,21 +294,343 @@ onReachBottom(() => {
 </script>
 
 <style lang="scss">
-.item {
-  height: 150rpx;
-  border-bottom: 1rpx solid rgba(0, 0, 0, 0.1);
-  &:last-child {
-    border-bottom: none;
+.page-container {
+  background-color: #f5f7fa;
+  min-height: 100vh;
+}
+
+.order-list {
+  padding-bottom: 20rpx;
+}
+
+.order-card {
+  background-color: #ffffff;
+  border-radius: 16rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+  transition: all 0.3s ease;
+  position: relative;
+  overflow: hidden;
+  
+  &:hover {
+    transform: translateY(-2rpx);
+    box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
   }
 }
+
+.checkbox-container {
+  position: absolute;
+  left: 24rpx;
+  top: 28rpx;
+  z-index: 2;
+}
+
+.order-header {
+  border-bottom: 1rpx solid #f0f0f0;
+  padding-bottom: 20rpx;
+}
+
+.order-header.invoice-mode {
+  padding-left: 80rpx !important;
+}
+
+.order-info {
+  padding-top: 20rpx;
+}
+
+.order-info.invoice-mode {
+  padding-left: 80rpx !important;
+}
+
+.order-id {
+  color: #333;
+  display: flex;
+  align-items: center;
+  white-space: nowrap;
+  flex: 1;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.invoice-status-tag {
+  font-size: 20rpx;
+  font-weight: 500;
+  white-space: nowrap;
+  min-width: 80rpx;
+  text-align: center;
+  
+  &.status-invoiced {
+    background-color: rgba(65, 157, 149, 0.1);
+    color: #419D95;
+  }
+  
+  &.status-cancelled {
+    background-color: #ffebee;
+    color: #f44336;
+  }
+  
+  &.status-processing {
+    background-color: rgba(65, 157, 149, 0.1);
+    color: #419D95;
+  }
+}
+
+.order-info {
+  padding-top: 20rpx;
+}
+
+.order-time {
+  display: flex;
+  align-items: center;
+}
+
+.order-amount {
+  color: #419D95;
+  font-weight: 600;
+  display: flex;
+  align-items: center;
+}
+
+.empty-state {
+  min-height: 60vh;
+  padding-top: 100rpx;
+}
+
 .invoice-placeholder {
   height: 60rpx;
 }
+
 .foot-placeholder {
   height: 112rpx;
 }
+
 .foot {
   height: 112rpx;
   border-top: 1px solid rgba(0, 0, 0, 0.1);
+  background-color: #ffffff;
 }
+
+/* 工具类 */
+.shadow-sm {
+  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+}
+
+.bg-fff {
+  background-color: #ffffff;
+}
+
+.br-16 {
+  border-radius: 16rpx;
+}
+
+.br-12 {
+  border-radius: 12rpx;
+}
+
+.br-8 {
+  border-radius: 8rpx;
+}
+
+.br-6 {
+  border-radius: 6rpx;
+}
+
+.p-20 {
+  padding: 20rpx;
+}
+
+.px-16 {
+  padding-left: 16rpx;
+  padding-right: 16rpx;
+}
+
+.py-8 {
+  padding-top: 8rpx;
+  padding-bottom: 8rpx;
+}
+
+.mb-24 {
+  margin-bottom: 24rpx;
+}
+
+.mr-28 {
+  margin-right: 28rpx;
+}
+
+.mr-12 {
+  margin-right: 12rpx;
+}
+
+.mr-8 {
+  margin-right: 8rpx;
+}
+
+.ml-8 {
+  margin-left: 8rpx;
+}
+
+.ml-16 {
+  margin-left: 16rpx;
+}
+
+.ml-24 {
+  margin-left: 24rpx;
+}
+
+.mt-24 {
+  margin-top: 24rpx;
+}
+
+.mt-20 {
+  margin-top: 20rpx;
+}
+
+.flex-center {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.flex-align-center {
+  display: flex;
+  align-items: center;
+}
+
+.flex-column {
+  display: flex;
+  flex-direction: column;
+}
+
+.flex-grow {
+  flex-grow: 1;
+}
+
+.flex-inline {
+  display: inline-flex;
+}
+
+.justify-between {
+  justify-content: space-between;
+}
+
+.bg-fff {
+  background-color: #ffffff;
+}
+
+.color-333 {
+  color: #333333;
+}
+
+.color-666 {
+  color: #666666;
+}
+
+.color-999 {
+  color: #999999;
+}
+
+.color-primary {
+  color: #ff6b35;
+}
+
+.color-theme {
+  color: #419D95;
+}
+
+.color-fff {
+  color: #ffffff;
+}
+
+.bg-primary {
+  background-color: #ff6b35;
+}
+
+.bg-theme {
+  background-color: #419D95;
+}
+
+.cancel-btn {
+  transition: all 0.3s ease;
+  border: 2rpx solid #419D95;
+  background-color: transparent;
+  
+  &:hover {
+    background-color: rgba(65, 157, 149, 0.05);
+    transform: translateY(-2rpx);
+    box-shadow: 0 2rpx 8rpx rgba(65, 157, 149, 0.2);
+  }
+  
+  &:active {
+    background-color: rgba(65, 157, 149, 0.1);
+    transform: translateY(0);
+  }
+}
+
+.fs-20 {
+  font-size: 20rpx;
+}
+
+.fs-24 {
+  font-size: 24rpx;
+}
+
+.fs-28 {
+  font-size: 28rpx;
+}
+
+.fs-30 {
+  font-size: 30rpx;
+}
+
+.fs-32 {
+  font-size: 32rpx;
+}
+
+.fs-36 {
+  font-size: 36rpx;
+}
+
+.fw-500 {
+  font-weight: 500;
+}
+
+.fw-600 {
+  font-weight: 600;
+}
+
+.height-46 {
+  height: 46rpx;
+}
+
+.height-72 {
+  height: 72rpx;
+}
+
+.height-80 {
+  height: 80rpx;
+}
+
+.width-96 {
+  width: 96rpx;
+}
+
+.pl-30 {
+  padding-left: 30rpx;
+}
+
+.pr-30 {
+  padding-right: 30rpx;
+}
+
+.pt-20 {
+  padding-top: 20rpx;
+}
+
+.pt-28 {
+  padding-top: 28rpx;
+}
+
+.pb-28 {
+  padding-bottom: 28rpx;
+}
+
 </style>