Răsfoiți Sursa

PureAdmin优化

skyline 2 săptămâni în urmă
părinte
comite
467666e604

+ 1 - 1
admin-web-new/src/components/ExtForm/ExtDSelect.vue

@@ -5,7 +5,7 @@
     :multiple="multiple"
     filterable
     clearable
-    style="width: 100%"
+    style="width: 100%; min-width: 200px"
     :placeholder="placeholder"
     v-model="state.dataVal"
     @change="handleChange"

+ 9 - 0
admin-web-new/src/style/element-plus.scss

@@ -2,6 +2,15 @@
   font-weight: 700;
 }
 
+/*
+  inline 表单中的 el-select 默认无宽度,其父容器 el-form-item 为 display: inline-flex,
+  会导致 width: 100% 循环依赖塌陷到 min-content。此处提供合理默认宽度,
+  内联 style 属性可覆盖此默认值(如 style="width: 130px")。
+*/
+.el-form--inline .el-form-item .el-select {
+  width: 200px;
+}
+
 .el-breadcrumb__inner,
 .el-breadcrumb__inner a {
   font-weight: 400 !important;

+ 16 - 6
admin-web-new/src/views/admin/finance/recharge.vue

@@ -2,6 +2,7 @@
 import { reactive, onMounted, ref, nextTick } from "vue";
 import { getRechargeList } from "@/api/finance";
 import { useRenderIcon } from "@/components/ReIcon/src/hooks";
+import { ElMessage } from "element-plus";
 
 defineOptions({
   name: "AdminFinanceRecharge"
@@ -60,6 +61,7 @@ const loadData = (refresh: boolean = false) => {
     })
     .catch(() => {
       state.tableData.data = [];
+      ElMessage.error("加载充值记录失败");
     })
     .finally(() => {
       state.tableData.loading = false;
@@ -90,14 +92,17 @@ const handleReset = () => {
 };
 
 const formatMoney = (value: number) => {
-  if (value === null || value === undefined) return "0.00";
-  return (value / 100).toFixed(2);
+  if (value === null || value === undefined) return "¥0.00";
+  return `¥${(value / 100).toFixed(2)}`;
 };
 </script>
 
 <template>
   <div class="page-container">
     <el-card shadow="hover">
+      <template #header>
+        <span class="card-header">充值记录</span>
+      </template>
       <el-form
         ref="queryRef"
         :model="state.formQuery"
@@ -154,7 +159,7 @@ const formatMoney = (value: number) => {
         stripe
       >
         <template #empty>
-          <el-empty description="暂无数据" />
+          <el-empty description="暂无充值记录" />
         </template>
         <el-table-column
           v-for="col in state.tableData.columns"
@@ -192,16 +197,21 @@ const formatMoney = (value: number) => {
 
 <style scoped lang="scss">
 .page-container {
-  padding: 15px;
+  padding: 20px;
+}
+
+.card-header {
+  font-size: 16px;
+  font-weight: 600;
 }
 
 .search-form {
-  margin-bottom: 15px;
+  margin-bottom: 16px;
 }
 
 .pagination-container {
   display: flex;
   justify-content: flex-end;
-  margin-top: 15px;
+  margin-top: 20px;
 }
 </style>

+ 31 - 19
admin-web-new/src/views/admin/finance/refund.vue

@@ -65,33 +65,37 @@ const loadData = (refresh: boolean = false) => {
     })
     .catch(() => {
       state.tableData.data = [];
+      ElMessage.error("加载退款记录失败");
     })
     .finally(() => {
       state.tableData.loading = false;
     });
 };
 
+const refundLoading = ref(false);
+
 const handleRefundDeal = (row: any) => {
-  ElMessageBox.confirm("确认执行本操作吗?", "退款处理", {
-    confirmButtonText: "确定",
-    cancelButtonText: "取消",
-    type: "warning"
-  }).then(() => {
-    const loading = ElMessage({
-      message: "退款处理中...",
-      type: "info",
-      duration: 0
-    });
+  ElMessageBox.confirm(
+    `确认对退款单「${row.refundLogId}」执行退款处理吗?`,
+    "退款处理",
+    {
+      confirmButtonText: "确定",
+      cancelButtonText: "取消",
+      type: "warning"
+    }
+  ).then(() => {
+    refundLoading.value = true;
     http
       .request("get", `finance/customWxRefund/${row.refundLogId}`)
       .then(() => {
-        loading.close();
         ElMessage.success("退款系统处理中,请稍后查看结果");
         loadData(true);
       })
       .catch(() => {
-        loading.close();
-        ElMessage.error("退款处理错误,请联系管理员");
+        ElMessage.error("退款处理失败,请联系管理员");
+      })
+      .finally(() => {
+        refundLoading.value = false;
       });
   });
 };
@@ -119,14 +123,17 @@ const handleReset = () => {
 };
 
 const formatMoney = (value: number) => {
-  if (value === null || value === undefined) return "0.00";
-  return (value / 100).toFixed(2);
+  if (value === null || value === undefined) return "¥0.00";
+  return `¥${(value / 100).toFixed(2)}`;
 };
 </script>
 
 <template>
   <div class="page-container">
     <el-card shadow="hover">
+      <template #header>
+        <span class="card-header">退款清单</span>
+      </template>
       <el-form
         ref="queryRef"
         :model="state.formQuery"
@@ -175,7 +182,7 @@ const formatMoney = (value: number) => {
         stripe
       >
         <template #empty>
-          <el-empty description="暂无数据" />
+          <el-empty description="暂无退款记录" />
         </template>
         <el-table-column
           v-for="col in state.tableData.columns"
@@ -229,16 +236,21 @@ const formatMoney = (value: number) => {
 
 <style scoped lang="scss">
 .page-container {
-  padding: 15px;
+  padding: 20px;
+}
+
+.card-header {
+  font-size: 16px;
+  font-weight: 600;
 }
 
 .search-form {
-  margin-bottom: 15px;
+  margin-bottom: 16px;
 }
 
 .pagination-container {
   display: flex;
   justify-content: flex-end;
-  margin-top: 15px;
+  margin-top: 20px;
 }
 </style>

+ 27 - 12
admin-web-new/src/views/admin/finance/settlement.vue

@@ -83,22 +83,27 @@ const loadData = (refresh: boolean = false) => {
     })
     .catch(() => {
       state.tableData.data = [];
+      ElMessage.error("加载结算记录失败");
     })
     .finally(() => {
       state.tableData.loading = false;
     });
 };
 
+const settlementLoading = ref(false);
+
 const handleTriggerSettlement = () => {
-  const loading = ElMessage({ message: "结算执行中...", type: "info", duration: 0 });
+  settlementLoading.value = true;
   triggerSettlement()
     .then(() => {
-      loading.close();
       ElMessage.success("结算已完成");
       loadData(true);
     })
     .catch(() => {
-      loading.close();
+      ElMessage.error("结算执行失败,请稍后重试");
+    })
+    .finally(() => {
+      settlementLoading.value = false;
     });
 };
 
@@ -122,8 +127,8 @@ const handleReset = () => {
 };
 
 const formatMoney = (value: number) => {
-  if (value === null || value === undefined) return "0.00";
-  return (value / 100).toFixed(2);
+  if (value === null || value === undefined) return "¥0.00";
+  return `¥${(value / 100).toFixed(2)}`;
 };
 
 const getStatusLabel = (status: number) => {
@@ -135,8 +140,8 @@ const getStatusLabel = (status: number) => {
   return map[status] || String(status);
 };
 
-const getStatusType = (status: number): any => {
-  const map: Record<number, string> = {
+const getStatusType = (status: number): "info" | "success" | "danger" | "warning" => {
+  const map: Record<number, "info" | "success" | "danger"> = {
     0: "info",
     1: "success",
     2: "danger"
@@ -148,6 +153,9 @@ const getStatusType = (status: number): any => {
 <template>
   <div class="page-container">
     <el-card shadow="hover">
+      <template #header>
+        <span class="card-header">结算记录</span>
+      </template>
       <el-form ref="queryRef" :model="state.formQuery" inline class="search-form">
         <el-form-item label="站点">
           <el-select v-model="state.formQuery.stationId" placeholder="请选择站点" clearable filterable @change="handleSearch">
@@ -165,13 +173,13 @@ const getStatusType = (status: number): any => {
         <el-form-item>
           <el-button type="primary" :icon="useRenderIcon('ri/search-line')" @click="handleSearch">查询</el-button>
           <el-button :icon="useRenderIcon('ri/refresh-line')" @click="handleReset">重置</el-button>
-          <el-button type="warning" :icon="useRenderIcon('ri/money-cny-circle-line')" @click="handleTriggerSettlement">手动触发结算</el-button>
+          <el-button type="warning" :loading="settlementLoading" :icon="useRenderIcon('ri/money-cny-circle-line')" @click="handleTriggerSettlement">手动触发结算</el-button>
         </el-form-item>
       </el-form>
 
       <el-table ref="tableRef" v-loading="state.tableData.loading" :data="state.tableData.data" :height="state.tableData.height" border stripe>
         <template #empty>
-          <el-empty description="暂无数据" />
+          <el-empty description="暂无结算记录" />
         </template>
         <el-table-column v-for="col in state.tableData.columns" :key="col.prop" :prop="col.prop" :label="col.label" :width="col.width" show-overflow-tooltip>
           <template #default="{ row }">
@@ -207,14 +215,21 @@ const getStatusType = (status: number): any => {
 
 <style scoped lang="scss">
 .page-container {
-  padding: 15px;
+  padding: 20px;
 }
+
+.card-header {
+  font-size: 16px;
+  font-weight: 600;
+}
+
 .search-form {
-  margin-bottom: 15px;
+  margin-bottom: 16px;
 }
+
 .pagination-container {
   display: flex;
   justify-content: flex-end;
-  margin-top: 15px;
+  margin-top: 20px;
 }
 </style>

+ 35 - 19
admin-web-new/src/views/admin/finance/split-record.vue

@@ -3,6 +3,7 @@ import { reactive, onMounted, ref, nextTick } from "vue";
 import { getSplitRecordList } from "@/api/finance";
 import { getStationList } from "@/api/station";
 import { useRenderIcon } from "@/components/ReIcon/src/hooks";
+import { ElMessage } from "element-plus";
 import { ExtDLabel, ExtDSelect } from "@/components/ExtForm";
 
 defineOptions({
@@ -70,6 +71,7 @@ const loadData = (refresh: boolean = false) => {
     })
     .catch(() => {
       state.tableData.data = [];
+      ElMessage.error("加载分账记录失败");
     })
     .finally(() => {
       state.tableData.loading = false;
@@ -100,14 +102,17 @@ const handleReset = () => {
 };
 
 const formatMoney = (value: number) => {
-  if (value === null || value === undefined) return "0.00";
-  return (value / 100).toFixed(2);
+  if (value === null || value === undefined) return "¥0.00";
+  return `¥${(value / 100).toFixed(2)}`;
 };
 </script>
 
 <template>
   <div class="page-container">
     <el-card shadow="hover">
+      <template #header>
+        <span class="card-header">分账记录</span>
+      </template>
       <el-form
         ref="queryRef"
         :model="state.formQuery"
@@ -172,7 +177,7 @@ const formatMoney = (value: number) => {
         stripe
       >
         <template #empty>
-          <el-empty description="暂无数据" />
+          <el-empty description="暂无分账记录" />
         </template>
         <el-table-column
           v-for="col in state.tableData.columns"
@@ -185,16 +190,16 @@ const formatMoney = (value: number) => {
           <template #default="{ row }">
             <template v-if="col.prop === 'toStationId'">
               <div class="station-name-cell">
-                <div class="station-id">{{ row.toStationId }}</div>
-                <el-divider direction="horizontal" />
-                <div class="station-name">{{ row.toStationName }}</div>
+                <span class="station-id">{{ row.toStationId }}</span>
+                <span class="station-divider"></span>
+                <span class="station-name">{{ row.toStationName }}</span>
               </div>
             </template>
             <template v-else-if="col.prop === 'fromStationId'">
               <div class="station-name-cell">
-                <div class="station-id">{{ row.fromStationId }}</div>
-                <el-divider direction="horizontal" />
-                <div class="station-name">{{ row.fromStationName }}</div>
+                <span class="station-id">{{ row.fromStationId }}</span>
+                <span class="station-divider"></span>
+                <span class="station-name">{{ row.fromStationName }}</span>
               </div>
             </template>
             <template v-else-if="col.prop === 'amount'">
@@ -227,34 +232,45 @@ const formatMoney = (value: number) => {
 
 <style scoped lang="scss">
 .page-container {
-  padding: 15px;
+  padding: 20px;
+}
+
+.card-header {
+  font-size: 16px;
+  font-weight: 600;
 }
 
 .search-form {
-  margin-bottom: 15px;
+  margin-bottom: 16px;
 }
 
 .pagination-container {
   display: flex;
   justify-content: flex-end;
-  margin-top: 15px;
+  margin-top: 20px;
 }
 
 .station-name-cell {
-  text-align: center;
-  
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 4px;
+
   .station-id {
     color: var(--el-text-color-secondary);
     font-size: 12px;
   }
-  
+
+  .station-divider {
+    display: block;
+    width: 40px;
+    height: 1px;
+    background: var(--el-border-color-light);
+  }
+
   .station-name {
     font-weight: 500;
   }
-  
-  .el-divider {
-    margin: 4px 0;
-  }
 }
 
 .money {

+ 50 - 34
admin-web-new/src/views/admin/finance/withdraw.vue

@@ -75,56 +75,64 @@ const loadData = (refresh: boolean = false) => {
     })
     .catch(() => {
       state.tableData.data = [];
+      ElMessage.error("加载提现记录失败");
     })
     .finally(() => {
       state.tableData.loading = false;
     });
 };
 
+const auditLoading = ref(false);
+const confirmLoading = ref(false);
+
 const handleWithdrawAudit = (row: any) => {
-  ElMessageBox.confirm("确认执行本操作吗?", "提现审核", {
-    confirmButtonText: "确定",
-    cancelButtonText: "取消",
-    type: "warning"
-  }).then(() => {
-    const loading = ElMessage({
-      message: "操作中...",
-      type: "info",
-      duration: 0
-    });
+  ElMessageBox.confirm(
+    `确认对站点「${row.stationName}」的提现申请执行审核通过吗?`,
+    "提现审核",
+    {
+      confirmButtonText: "确定",
+      cancelButtonText: "取消",
+      type: "warning"
+    }
+  ).then(() => {
+    auditLoading.value = true;
     http
       .request("post", "finance/reviewWithdrawn", { data: { id: row.id, status: 1 } })
       .then(() => {
-        loading.close();
         ElMessage.success("提现审核通过");
         loadData(true);
       })
       .catch(() => {
-        loading.close();
+        ElMessage.error("审核操作失败,请稍后重试");
+      })
+      .finally(() => {
+        auditLoading.value = false;
       });
   });
 };
 
 const handleWithdrawConfirm = (row: any) => {
-  ElMessageBox.confirm("确认执行本操作吗?", "打款确认", {
-    confirmButtonText: "确定",
-    cancelButtonText: "取消",
-    type: "warning"
-  }).then(() => {
-    const loading = ElMessage({
-      message: "操作中...",
-      type: "info",
-      duration: 0
-    });
+  ElMessageBox.confirm(
+    `确认对站点「${row.stationName}」的提现执行打款确认吗?`,
+    "打款确认",
+    {
+      confirmButtonText: "确定",
+      cancelButtonText: "取消",
+      type: "warning"
+    }
+  ).then(() => {
+    confirmLoading.value = true;
     http
       .request("post", "finance/confirmWithdrawnPayment", { data: { id: row.id, paymentStatus: 1 } })
       .then(() => {
-        loading.close();
         ElMessage.success("提现打款已确认");
         loadData(true);
       })
       .catch(() => {
-        loading.close();
+        ElMessage.error("打款确认失败,请稍后重试");
+      })
+      .finally(() => {
+        confirmLoading.value = false;
       });
   });
 };
@@ -153,8 +161,8 @@ const handleReset = () => {
 };
 
 const formatMoney = (value: number) => {
-  if (value === null || value === undefined) return "0.00";
-  return (value / 100).toFixed(2);
+  if (value === null || value === undefined) return "¥0.00";
+  return `¥${(value / 100).toFixed(2)}`;
 };
 
 const getStatusLabel = (status: number) => {
@@ -165,8 +173,8 @@ const getStatusLabel = (status: number) => {
   return map[status] || String(status);
 };
 
-const getStatusType = (status: number) => {
-  const map: Record<number, string> = {
+const getStatusType = (status: number): "info" | "success" | "danger" | "warning" => {
+  const map: Record<number, "warning" | "success"> = {
     0: "warning",
     1: "success"
   };
@@ -181,8 +189,8 @@ const getPaymentStatusLabel = (status: number) => {
   return map[status] || String(status);
 };
 
-const getPaymentStatusType = (status: number) => {
-  const map: Record<number, string> = {
+const getPaymentStatusType = (status: number): "info" | "success" | "danger" | "warning" => {
+  const map: Record<number, "warning" | "success"> = {
     0: "warning",
     1: "success"
   };
@@ -193,6 +201,9 @@ const getPaymentStatusType = (status: number) => {
 <template>
   <div class="page-container">
     <el-card shadow="hover">
+      <template #header>
+        <span class="card-header">提现记录</span>
+      </template>
       <el-form
         ref="queryRef"
         :model="state.formQuery"
@@ -255,7 +266,7 @@ const getPaymentStatusType = (status: number) => {
         stripe
       >
         <template #empty>
-          <el-empty description="暂无数据" />
+          <el-empty description="暂无提现记录" />
         </template>
         <el-table-column
           v-for="col in state.tableData.columns"
@@ -325,16 +336,21 @@ const getPaymentStatusType = (status: number) => {
 
 <style scoped lang="scss">
 .page-container {
-  padding: 15px;
+  padding: 20px;
+}
+
+.card-header {
+  font-size: 16px;
+  font-weight: 600;
 }
 
 .search-form {
-  margin-bottom: 15px;
+  margin-bottom: 16px;
 }
 
 .pagination-container {
   display: flex;
   justify-content: flex-end;
-  margin-top: 15px;
+  margin-top: 20px;
 }
 </style>

+ 8 - 37
admin-web-new/src/views/admin/ordering/dialog.vue

@@ -1,5 +1,6 @@
 <script setup lang="ts">
 import { ref, computed } from "vue";
+import { formatDict } from "@/utils/dict";
 
 const dialogVisible = ref(false);
 const orderData = ref<any>(null);
@@ -45,43 +46,13 @@ const detailList = computed(() => {
     }));
 });
 
-const getFeeTypeName = (name: string) => {
-  const map: Record<string, string> = {
-    "water": "水费",
-    "foam": "泡沫费",
-    "electricity": "电费",
-    "service": "服务费",
-    "parking": "停车费"
-  };
-  return map[name] || name;
-};
+const getFeeTypeName = (name: string) => formatDict("Order.feeType", name) || name;
 
-const getOrderStatusText = (status: string) => {
-  const map: Record<string, string> = {
-    "0": "进行中",
-    "1": "已完成",
-    "2": "已取消"
-  };
-  return map[status] || status;
-};
+const getOrderStatusText = (status: string) => formatDict("Order.status", status);
 
-const getPayStatusText = (status: string) => {
-  const map: Record<string, string> = {
-    "0": "待支付",
-    "1": "已支付",
-    "2": "已退款"
-  };
-  return map[status] || status;
-};
+const getPayStatusText = (status: string) => formatDict("Order.pay", status);
 
-const getCloseTypeText = (type: string) => {
-  const map: Record<string, string> = {
-    "0": "手动关机",
-    "1": "自动关机",
-    "2": "远程关机"
-  };
-  return map[type] || type;
-};
+const getCloseTypeText = (type: string) => formatDict("Order.closeType", type);
 
 defineExpose({ open });
 </script>
@@ -165,7 +136,7 @@ defineExpose({ open });
       </el-table>
     </template>
     <template #footer>
-      <el-button @click="handleClose">关 闭</el-button>
+      <el-button @click="handleClose">关闭</el-button>
     </template>
   </el-dialog>
 </template>
@@ -177,7 +148,7 @@ defineExpose({ open });
 
 .detail-title {
   font-size: 16px;
-  font-weight: 500;
+  font-weight: 600;
   margin-bottom: 10px;
   padding-left: 10px;
   border-bottom: 2px solid var(--el-color-primary);
@@ -187,7 +158,7 @@ defineExpose({ open });
   font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
   
   &.highlight {
-    color: var(--el-color-danger);
+    color: #C83A35;
     font-weight: 600;
   }
   

+ 17 - 22
admin-web-new/src/views/admin/ordering/index.vue

@@ -1,6 +1,7 @@
 <script setup lang="ts">
 import { reactive, onMounted, ref, nextTick } from "vue";
 import { http } from "@/utils/http";
+import { message } from "@/utils/message";
 import { useRenderIcon } from "@/components/ReIcon/src/hooks";
 import { ExtDLabel, ExtDSelect } from "@/components/ExtForm";
 import { formatDict } from "@/utils/dict";
@@ -18,7 +19,6 @@ const state = reactive({
   formQuery: {
     orderId: "",
     mobilePhone: "",
-    stationId: "",
     orderStatus: "",
     payStatus: ""
   },
@@ -73,6 +73,7 @@ const loadData = (refresh: boolean = false) => {
     })
     .catch(() => {
       state.tableData.data = [];
+      message("加载订单数据失败", { type: "error" });
     })
     .finally(() => {
       state.tableData.loading = false;
@@ -97,7 +98,6 @@ const handleReset = () => {
   state.formQuery = {
     orderId: "",
     mobilePhone: "",
-    stationId: "",
     orderStatus: "",
     payStatus: ""
   };
@@ -113,21 +113,16 @@ const formatMoney = (value: number) => {
   return (value / 100).toFixed(2);
 };
 
-const formatDuration = (ms: number) => {
-  if (!ms) return "0秒";
-  const seconds = Math.floor(ms / 1000);
-  const minutes = Math.floor(seconds / 60);
-  const hours = Math.floor(minutes / 60);
-  
-  const h = hours % 24;
-  const m = minutes % 60;
-  const s = seconds % 60;
-  
-  let str = "";
-  if (h > 0) str += `${h}时`;
-  if (m > 0) str += `${m}分`;
-  if (s > 0 || str === "") str += `${s}秒`;
-  return str;
+const formatDuration = (seconds: number) => {
+  if (!seconds) return "-";
+  const hours = Math.floor(seconds / 3600);
+  const minutes = Math.floor((seconds % 3600) / 60);
+  const secs = seconds % 60;
+  const parts = [];
+  if (hours > 0) parts.push(`${hours}时`);
+  if (minutes > 0) parts.push(`${minutes}分`);
+  if (secs > 0 || parts.length === 0) parts.push(`${secs}秒`);
+  return parts.join("");
 };
 
 const isMoneyField = (prop: string) => {
@@ -140,7 +135,7 @@ const processDetailData = (detail: any[]) => {
     .filter((s: any) => s.amount > 0 || s.seconds > 0)
     .map((s: any) => ({
       name: formatDict("Order.feeType", s.name),
-      seconds: formatDuration(s.seconds * 1000),
+      seconds: formatDuration(s.seconds),
       price: formatMoney(s.price),
       amount: formatMoney(s.amount)
     }));
@@ -244,7 +239,7 @@ const processDetailData = (detail: any[]) => {
         >
           <template #default="{ row }">
             <template v-if="isMoneyField(col.prop)">
-              {{ formatMoney(row[col.prop]) }}
+              ¥{{ formatMoney(row[col.prop]) }}
             </template>
             <template v-else-if="col.prop === 'orderStatus'">
               <ExtDLabel type="Order.status" :model-value="row.orderStatus" />
@@ -291,17 +286,17 @@ const processDetailData = (detail: any[]) => {
 
 <style scoped lang="scss">
 .page-container {
-  padding: 15px;
+  padding: 16px;
 }
 
 .search-form {
-  margin-bottom: 15px;
+  margin-bottom: 16px;
 }
 
 .pagination-container {
   display: flex;
   justify-content: flex-end;
-  margin-top: 15px;
+  margin-top: 16px;
 }
 
 .order-detail {

+ 38 - 30
admin-web-new/src/views/admin/station/account.vue

@@ -66,40 +66,40 @@ const loadData = (refresh: boolean = false) => {
     })
     .catch(() => {
       state.tableData.data = [];
+      ElMessage.error("加载账户列表失败");
     })
     .finally(() => {
       state.tableData.loading = false;
     });
 };
 
+const withdrawLoading = ref(false);
+
 const handleWithdrawApply = (row: any) => {
-  ElMessageBox.confirm("确认执行本操作吗?", "提现申请", {
-    confirmButtonText: "确定",
-    cancelButtonText: "取消",
-    type: "warning"
-  }).then(() => {
-    ElMessageBox.prompt("请输入提现金额(单位:元)", "提现申请", {
-      confirmButtonText: "确定",
+  ElMessageBox.prompt(
+    `站点「${row.stationName}」可提现 ${formatMoney(row.availableBalance)} 元`,
+    "提现申请",
+    {
+      confirmButtonText: "确认提现",
       cancelButtonText: "取消",
       inputPattern: /^[0-9]+(\.[0-9]{1,2})?$/,
-      inputErrorMessage: "请输入有效的金额"
-    }).then(({ value }) => {
-      const amount = Number(value) * 100;
-      const loading = ElMessage({
-        message: "请等待...",
-        type: "info",
-        duration: 0
+      inputErrorMessage: "请输入有效的金额",
+      inputPlaceholder: "请输入提现金额(元)"
+    }
+  ).then(({ value }) => {
+    const amount = Number(value) * 100;
+    withdrawLoading.value = true;
+    applyWithdrawn({ stationId: row.stationId, amount })
+      .then(() => {
+        ElMessage.success("提现已发起");
+        loadData(true);
+      })
+      .catch(() => {
+        ElMessage.error("提现申请失败,请稍后重试");
+      })
+      .finally(() => {
+        withdrawLoading.value = false;
       });
-      applyWithdrawn({ stationId: row.stationId, amount })
-        .then(() => {
-          loading.close();
-          ElMessage.success("提现已发起");
-          loadData(true);
-        })
-        .catch(() => {
-          loading.close();
-        });
-    });
   });
 };
 
@@ -125,14 +125,17 @@ const handleReset = () => {
 };
 
 const formatMoney = (value: number) => {
-  if (value === null || value === undefined) return "0.00";
-  return (value / 100).toFixed(2);
+  if (value === null || value === undefined) return "¥0.00";
+  return `¥${(value / 100).toFixed(2)}`;
 };
 </script>
 
 <template>
   <div class="page-container">
     <el-card shadow="hover">
+      <template #header>
+        <span class="card-header">站点账户</span>
+      </template>
       <el-form
         ref="queryRef"
         :model="state.formQuery"
@@ -181,7 +184,7 @@ const formatMoney = (value: number) => {
         stripe
       >
         <template #empty>
-          <el-empty description="暂无数据" />
+          <el-empty description="暂无账户数据,请选择站点后查询" />
         </template>
         <el-table-column
           v-for="col in state.tableData.columns"
@@ -228,16 +231,21 @@ const formatMoney = (value: number) => {
 
 <style scoped lang="scss">
 .page-container {
-  padding: 15px;
+  padding: 20px;
 }
 
 .search-form {
-  margin-bottom: 15px;
+  margin-bottom: 16px;
+}
+
+.card-header {
+  font-size: 16px;
+  font-weight: 600;
 }
 
 .pagination-container {
   display: flex;
   justify-content: flex-end;
-  margin-top: 15px;
+  margin-top: 20px;
 }
 </style>

+ 9 - 6
admin-web-new/src/views/admin/station/device-dialog.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import { reactive, ref } from "vue";
 import { getDeviceDetail } from "@/api/station";
-import { http } from "@/utils/http";
+import { addDevice, modifyDevice } from "@/api/device";
 import { ElMessage } from "element-plus";
 
 const emit = defineEmits(["refresh"]);
@@ -78,8 +78,11 @@ const handleSubmit = async () => {
   
   loading.value = true;
   try {
-    const url = state.ruleForm.id ? "washDevice/modify" : "washDevice/add";
-    await http.request("post", url, { data: state.ruleForm });
+    if (state.ruleForm.id) {
+      await modifyDevice(state.ruleForm);
+    } else {
+      await addDevice(state.ruleForm);
+    }
     ElMessage.success("操作成功");
     handleClose();
     emit("refresh");
@@ -113,12 +116,12 @@ defineExpose({ open });
       ref="formRef"
       :model="state.ruleForm"
       :rules="state.rules"
-      label-width="100px"
+      label-width="120px"
     >
       <el-form-item label="设备名称" prop="deviceName">
         <el-input v-model="state.ruleForm.deviceName" placeholder="请输入设备名称" />
       </el-form-item>
-      <el-form-item label="产品key" prop="productKey">
+      <el-form-item label="产品 Key" prop="productKey">
         <el-input v-model="state.ruleForm.productKey" placeholder="请输入产品key" />
       </el-form-item>
       <el-form-item label="状态" prop="status">
@@ -156,7 +159,7 @@ defineExpose({ open });
     </el-form>
     <template #footer>
       <span class="dialog-footer">
-        <el-button @click="handleClose">取 消</el-button>
+        <el-button @click="handleClose">取消</el-button>
         <el-button :loading="loading" type="primary" @click="handleSubmit">
           {{ getSubmitText() }}
         </el-button>

+ 24 - 18
admin-web-new/src/views/admin/station/device.vue

@@ -103,6 +103,7 @@ const loadData = (refresh: boolean = false) => {
     })
     .catch(() => {
       state.tableData.data = [];
+      ElMessage.error("加载设备列表失败");
     })
     .finally(() => {
       state.tableData.loading = false;
@@ -161,7 +162,7 @@ const handleDelete = (row: any) => {
         loadData(true);
       })
       .catch(() => {
-        ElMessage.error("删除失败");
+        ElMessage.error("删除失败,请稍后重试");
       });
   });
 };
@@ -291,7 +292,7 @@ const formatDuration = (ms: number) => {
         stripe
       >
         <template #empty>
-          <el-empty description="暂无数据" />
+          <el-empty description="暂无设备数据,点击「新增」按钮添加设备" />
         </template>
         <el-table-column
           v-for="col in state.tableData.columns"
@@ -304,9 +305,9 @@ const formatDuration = (ms: number) => {
           <template #default="{ row }">
             <template v-if="col.prop === 'stationName'">
               <div class="station-name-cell">
-                <div class="station-id">{{ row.stationId }}</div>
-                <el-divider direction="horizontal" />
-                <div class="station-name">{{ row.stationName }}</div>
+                <span class="station-id">{{ row.stationId }}</span>
+                <span class="station-divider"></span>
+                <span class="station-name">{{ row.stationName }}</span>
               </div>
             </template>
             <template v-else-if="col.prop === 'state'">
@@ -328,9 +329,6 @@ const formatDuration = (ms: number) => {
             <el-button type="warning" link size="small" @click="handleEdit(row)">
               编辑
             </el-button>
-            <el-button type="primary" link size="small">
-              结算
-            </el-button>
             <el-button type="danger" link size="small" @click="handleDelete(row)">
               删除
             </el-button>
@@ -357,39 +355,47 @@ const formatDuration = (ms: number) => {
 
 <style scoped lang="scss">
 .page-container {
-  padding: 15px;
+  padding: 20px;
 }
 
 .card-header {
   display: flex;
   justify-content: space-between;
   align-items: center;
+  font-size: 16px;
+  font-weight: 600;
 }
 
 .search-form {
-  margin-bottom: 15px;
+  margin-bottom: 16px;
 }
 
 .pagination-container {
   display: flex;
   justify-content: flex-end;
-  margin-top: 15px;
+  margin-top: 20px;
 }
 
 .station-name-cell {
-  text-align: center;
-  
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 4px;
+
   .station-id {
     color: var(--el-text-color-secondary);
     font-size: 12px;
   }
-  
+
+  .station-divider {
+    display: block;
+    width: 40px;
+    height: 1px;
+    background: var(--el-border-color-light);
+  }
+
   .station-name {
     font-weight: 500;
   }
-  
-  .el-divider {
-    margin: 4px 0;
-  }
 }
 </style>

+ 3 - 3
admin-web-new/src/views/admin/station/dialog.vue

@@ -141,7 +141,7 @@ defineExpose({ open });
           <el-option label="私人" value="2" />
         </el-select>
       </el-form-item>
-      <el-form-item label="免停服务" prop="parkingFee">
+      <el-form-item label="停车减免规则" prop="parkingFee">
         <el-input
           v-model="state.ruleForm.parkingFee"
           type="textarea"
@@ -168,9 +168,9 @@ defineExpose({ open });
     </el-form>
     <template #footer>
       <span class="dialog-footer">
-        <el-button @click="handleClose">取 消</el-button>
+        <el-button @click="handleClose">取消</el-button>
         <el-button v-if="!isView()" :loading="loading" type="primary" @click="handleSubmit">
-          确 
+          确定
         </el-button>
       </span>
     </template>

+ 33 - 16
admin-web-new/src/views/admin/station/list.vue

@@ -1,5 +1,6 @@
 <script setup lang="ts">
 import { reactive, onMounted, ref, nextTick } from "vue";
+import { useRouter } from "vue-router";
 import { getStationList, removeStation } from "@/api/station";
 import { useRenderIcon } from "@/components/ReIcon/src/hooks";
 import { ElMessage, ElMessageBox } from "element-plus";
@@ -10,6 +11,7 @@ defineOptions({
   name: "AdminStationList"
 });
 
+const router = useRouter();
 const queryRef = ref();
 const tableRef = ref();
 const dialogRef = ref();
@@ -71,6 +73,7 @@ const loadData = (refresh: boolean = false) => {
     })
     .catch(() => {
       state.tableData.data = [];
+      ElMessage.error("加载站点列表失败");
     })
     .finally(() => {
       state.tableData.loading = false;
@@ -125,7 +128,7 @@ const handleDelete = (row: any) => {
         loadData(true);
       })
       .catch(() => {
-        ElMessage.error("删除失败");
+        ElMessage.error("删除失败,请稍后重试");
       });
   });
 };
@@ -137,13 +140,16 @@ const handleShowQrCode = (row: any) => {
 };
 
 const handleGotoDevice = (row: any) => {
-  window.location.href = `/#/admin/station/device?stationId=${row.stationId}`;
+  router.push(`/admin/station/device?stationId=${row.stationId}`);
 };
 </script>
 
 <template>
   <div class="page-container">
     <el-card shadow="hover">
+      <template #header>
+        <span class="card-header">站点清单</span>
+      </template>
       <el-form
         ref="queryRef"
         :model="state.formQuery"
@@ -215,7 +221,7 @@ const handleGotoDevice = (row: any) => {
         stripe
       >
         <template #empty>
-          <el-empty description="暂无数据" />
+          <el-empty description="暂无站点数据,点击「新增」按钮添加站点" />
         </template>
         <el-table-column
           v-for="col in state.tableData.columns"
@@ -228,9 +234,9 @@ const handleGotoDevice = (row: any) => {
           <template #default="{ row }">
             <template v-if="col.prop === 'stationName'">
               <div class="station-name-cell">
-                <div class="station-id">{{ row.stationId }}</div>
-                <el-divider direction="horizontal" />
-                <div class="station-name">{{ row.stationName }}</div>
+                <span class="station-id">{{ row.stationId }}</span>
+                <span class="station-divider"></span>
+                <span class="station-name">{{ row.stationName }}</span>
               </div>
             </template>
             <template v-else-if="col.prop === 'pictures'">
@@ -313,34 +319,45 @@ const handleGotoDevice = (row: any) => {
 
 <style scoped lang="scss">
 .page-container {
-  padding: 15px;
+  padding: 20px;
+}
+
+.card-header {
+  font-size: 16px;
+  font-weight: 600;
 }
 
 .search-form {
-  margin-bottom: 15px;
+  margin-bottom: 16px;
 }
 
 .pagination-container {
   display: flex;
   justify-content: flex-end;
-  margin-top: 15px;
+  margin-top: 20px;
 }
 
 .station-name-cell {
-  text-align: center;
-  
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 4px;
+
   .station-id {
     color: var(--el-text-color-secondary);
     font-size: 12px;
   }
-  
+
+  .station-divider {
+    display: block;
+    width: 40px;
+    height: 1px;
+    background: var(--el-border-color-light);
+  }
+
   .station-name {
     font-weight: 500;
   }
-  
-  .el-divider {
-    margin: 4px 0;
-  }
 }
 
 .qr-code-container {