Kaynağa Gözat

统一结算详情弹窗与相关页面的 Fresh Mint 样式细节。

本次调整聚焦结算单详情/编辑弹窗与多层列表展示一致性,同时合并当前工作区内同轮 UI 规范改动并提交。

Made-with: Cursor
zlwu 6 ay önce
ebeveyn
işleme
2c7335fe9c

+ 2 - 0
docs/web-admin-ui-spec-fresh-mint.md

@@ -649,6 +649,8 @@
 37. **商品单位按同构流程收口(2026-04-17)**:`#/base/unit` 参照 `#/system/region` 与 `#/base/category` 的改造路径统一壳层与交互细节:工具栏增加 **`list-toolbar-selected + sel-num`**,并保持“首按钮主色、其余中性描边”(含批量导入);状态标签语义统一为 **启用=success、禁用=info**;操作列固定右侧并预留宽度(≥260);新增/编辑弹窗改为 **`label-position="top"`** 两列布局,footer 统一 **`btn-ghost/btn-main + Close/Check`** 图标规范。
 38. **商品管理同构改造(2026-04-17)**:`#/base/product` 对齐 `fresh-mint-green` 与区域/分类/单位页面规范:页头改为 **`page-head + h1 + page-desc`**;筛选区改为 **`filter-form + filter-grid + filter-actions-row`**(非 inline);列表工具栏合并为单行 **`list-toolbar-row`**(含 **`list-toolbar-selected + sel-num`** 与主次按钮);表格列头/列值统一居中,状态标签语义为 **启用=success、禁用=info**;操作列改为 **`btn-op-item`**(去 link 形态)并固定右侧预留宽度;新增/编辑与选择参数弹窗 footer 统一 **`btn-ghost/btn-main + Close/Check`**,参数选择表格去 `border` 并改为 Mint 容器边框与透明检索栏。
 39. **树形字段统一路径展示(全局,2026-04-17)**:凡树形字段(商品分类等)统一执行“**下拉展示本级、选中回显全路径、列表展示全路径**”规则:`el-tree-select` 展开态节点文案保留本级名称;选中后输入框通过 `#label` 插槽显示 `根 / 子 / 孙` 全路径;表格/卡片中的分类字段同样优先展示全路径(缺路径时回退接口字段)。实现统一复用 `web-admin/src/utils/tree-path.ts`(`buildTreePathLabelFromFlat`、`findTreeSelectShortLabel`),避免各页面重复拼接逻辑。
+40. **询价报价管理二轮收口(2026-04-17)**:`#/business/inquiry-quotation` 按 Fresh Mint 模板完成结构升级:补齐 `page-head + panel + panel-top`,区域切换统一为 `region-tab-strip/region-tab-btn`;询价与历史报价列表改为 `list-body/list-wrap + list-table` 组合;历史报价筛选改为 `filter-block + history-filter-grid + filter-actions-row`(重置/搜索按钮采用 `btn-ghost/btn-main` + 图标);操作列从 link 统一为 `btn-op-item`(首动作主色),并将历史列表分页条对齐为 `list-pager-bar`;关键弹窗 footer 同步 `btn-ghost/btn-main + Close/Check`,保持与已改造基础数据页同一视觉语言。
+41. **询价报价管理二轮精修(2026-04-17)**:继续对 `#/business/inquiry-quotation` 弹窗与内嵌选品区细节收口:`新增/编辑询价`、`询价详情`、`报价明细`、`调价确认` 统一接入 `inquiry-quotation-dialog` + `inquiry-quotation-dialog-mask`(header 薄荷渐变、footer 浅底、无遮罩模糊);弹窗 footer 按全站规范使用 `btn-ghost/btn-main` 与 `Close/Check` 图标;选品区(左树/右列表/已选区/卡片选中态)统一替换旧灰蓝边框为 Mint 语义边框与主色阴影;历史报价操作列保持固定右侧并使用 `btn-op-item primary`,补齐“弹窗内按钮层级、间距密度、操作列语义”与基础数据页一致性。
 
 ---
 

+ 253 - 61
web-admin/src/components/settlement/SettlementSheetDetailPanel.vue

@@ -1,22 +1,46 @@
 <!-- 结算单详情:凭证缩略图/视频可播放 + 订单树形表(含商品行) -->
 <template>
   <div class="sheet-detail-panel">
-    <el-descriptions :column="2" border>
-      <el-descriptions-item label="结算单号" :span="2">{{ detail.settlementNo }}</el-descriptions-item>
-      <el-descriptions-item :label="counterpartyLabel">{{
-        counterpartyName || '-'
-      }}</el-descriptions-item>
-      <el-descriptions-item label="订单数">{{ detail.orderCount }}</el-descriptions-item>
-      <el-descriptions-item label="结算金额(元)">
-        <span class="amount-text">¥{{ formatAmount(detail.totalAmount) }}</span>
-      </el-descriptions-item>
-      <el-descriptions-item label="结算时间">{{ detail.settlementAt || '-' }}</el-descriptions-item>
-      <el-descriptions-item label="操作人">{{ detail.createName || '-' }}</el-descriptions-item>
-      <el-descriptions-item label="备注" :span="2">{{ detail.remark || '-' }}</el-descriptions-item>
-    </el-descriptions>
-
-    <div class="detail-block-title">结算凭证</div>
-    <div v-if="voucherUrls.length" class="voucher-preview-grid">
+    <div class="view-hero">
+      <div class="view-hero-icon">
+        <el-icon><Document /></el-icon>
+      </div>
+      <div class="view-hero-main">
+        <h2>{{ detail.settlementNo || '-' }}</h2>
+        <div class="view-hero-tags">
+          <el-tag type="primary" size="small">{{ counterpartyLabel }}:{{ counterpartyName || '-' }}</el-tag>
+          <el-tag type="success" size="small">订单数 {{ detail.orderCount || 0 }}</el-tag>
+        </div>
+        <p class="view-hero-meta">
+          结算金额:<span class="amount-text">¥{{ formatAmount(detail.totalAmount) }}</span>
+          <span class="dot">·</span>
+          结算时间:{{ detail.settlementAt || '-' }}
+          <span class="dot">·</span>
+          操作人:{{ detail.createName || '-' }}
+        </p>
+      </div>
+    </div>
+
+    <div class="view-section">
+      <h3>基础信息</h3>
+      <el-descriptions :column="2" border>
+        <el-descriptions-item label="结算单号" :span="2">{{ detail.settlementNo }}</el-descriptions-item>
+        <el-descriptions-item :label="counterpartyLabel">{{
+          counterpartyName || '-'
+        }}</el-descriptions-item>
+        <el-descriptions-item label="订单数">{{ detail.orderCount }}</el-descriptions-item>
+        <el-descriptions-item label="结算金额(元)">
+          <span class="amount-text">¥{{ formatAmount(detail.totalAmount) }}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="结算时间">{{ detail.settlementAt || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="操作人">{{ detail.createName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="备注" :span="2">{{ detail.remark || '-' }}</el-descriptions-item>
+      </el-descriptions>
+    </div>
+
+    <div class="view-section">
+      <div class="detail-block-title">结算凭证</div>
+      <div v-if="voucherUrls.length" class="voucher-preview-grid">
       <div
         v-for="(url, i) in voucherUrls"
         :key="i"
@@ -31,19 +55,22 @@
         </template>
         <el-image v-else :src="url" fit="cover" class="voucher-thumb" />
       </div>
+      </div>
+      <el-empty v-else description="无凭证" :image-size="56" />
     </div>
-    <el-empty v-else description="无凭证" :image-size="56" />
-
-    <div class="detail-block-title">包含订单</div>
-    <el-table
-      ref="ordersTableRef"
-      :data="orders"
-      row-key="id"
-      size="small"
-      stripe
-      max-height="480"
-      class="orders-table"
-    >
+
+    <div class="view-section">
+      <div class="detail-block-title">包含订单</div>
+      <div class="detail-orders-list list-body list-wrap">
+        <el-table
+          ref="ordersTableRef"
+          :data="orders"
+          row-key="id"
+          size="small"
+          stripe
+          max-height="480"
+          class="orders-table"
+        >
       <el-table-column type="expand" width="48">
         <template #default="{ row: ord }">
           <div class="order-row-expand-wrap">
@@ -52,7 +79,7 @@
               class="settlement-row-expand-table-wrap sqxp-subtable-wrap"
             >
               <el-table :data="ord.children" stripe size="small" class="order-row-expand-table sqxp-subtable">
-              <el-table-column label="商品" min-width="240">
+                <el-table-column label="商品" min-width="180">
                 <template #default="{ row: line }">
                   <div class="expand-product-cell">
                     <div class="expand-product-thumb-wrap">
@@ -133,16 +160,6 @@
           <span class="amount-text">¥{{ formatAmount(orderSupplierTotal(ord)) }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="售出总金额(元)" min-width="140" align="right">
-        <template #default="{ row: ord }">
-          <span class="amount-text">¥{{ formatAmount(orderSellingTotal(ord)) }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="应收金额(元)" min-width="130" align="right">
-        <template #default="{ row: ord }">
-          <span class="amount-text">¥{{ formatAmount(orderReceivableAmount(ord)) }}</span>
-        </template>
-      </el-table-column>
       <el-table-column label="订单状态" min-width="120">
         <template #default="{ row: ord }">
           <el-tag :type="orderStatusTagType(resolveOrderStatus(ord))" size="small">
@@ -174,7 +191,9 @@
       <el-table-column label="下单时间" min-width="170">
         <template #default="{ row: ord }">{{ ord.orderCreateAt || '—' }}</template>
       </el-table-column>
-    </el-table>
+        </el-table>
+      </div>
+    </div>
 
     <el-dialog v-model="imagePreviewVisible" title="图片预览" width="820px" destroy-on-close>
       <el-image
@@ -198,7 +217,7 @@
 
 <script setup lang="ts">
   import { computed, ref } from 'vue'
-  import { VideoPlay } from '@element-plus/icons-vue'
+  import { Document, VideoPlay } from '@element-plus/icons-vue'
   const ordersTableRef = ref()
 
   const props = withDefaults(
@@ -355,16 +374,104 @@
 
 <style scoped lang="scss">
   .sheet-detail-panel {
+    .view-hero {
+      display: flex;
+      gap: 16px;
+      padding: 20px 22px;
+      background: linear-gradient(135deg, #ecfdf5 0%, #ffffff 55%);
+      border-bottom: 1px solid var(--mint-border, #d9e8e2);
+      align-items: center;
+    }
+
+    .view-hero-icon {
+      width: 72px;
+      height: 72px;
+      border-radius: 12px;
+      border: 1px solid rgba(16, 185, 129, 0.2);
+      background: rgba(16, 185, 129, 0.08);
+      color: #059669;
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+      font-size: 28px;
+      flex: 0 0 auto;
+    }
+
+    .view-hero-main h2 {
+      margin: 0 0 8px;
+      font-size: 18px;
+      font-weight: 700;
+      color: var(--mint-text, #1e293b);
+    }
+
+    .view-hero-tags {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+      margin-bottom: 8px;
+      flex-wrap: wrap;
+    }
+
+    .view-hero-meta {
+      margin: 0;
+      font-size: 13px;
+      color: var(--mint-text-muted, #64748b);
+      line-height: 1.5;
+    }
+
+    .dot {
+      margin: 0 6px;
+      opacity: 0.7;
+    }
+
+    .view-section {
+      padding: 18px 22px;
+      border-bottom: 1px solid var(--mint-border, #d9e8e2);
+    }
+
+    .view-section:last-child {
+      border-bottom: none;
+    }
+
+    .view-section h3 {
+      margin: 0 0 14px;
+      font-size: 13px;
+      font-weight: 700;
+      letter-spacing: 0.06em;
+      color: var(--mint-text-muted, #64748b);
+    }
+
     .amount-text {
-      color: #ee0a24;
-      font-weight: 600;
+      color: #dc2626;
+      font-weight: 700;
     }
 
     .detail-block-title {
-      margin: 20px 0 12px;
+      margin: 18px 0 12px;
       font-weight: 600;
       font-size: 15px;
-      color: #303133;
+      color: var(--mint-text, #1e293b);
+      padding-left: 12px;
+      border-left: 4px solid #10b981;
+    }
+
+    :deep(.el-descriptions) {
+      --el-descriptions-border-color: var(--mint-border, #d9e8e2);
+      border-radius: 10px;
+      overflow: hidden;
+      background: #fff;
+    }
+
+    :deep(.el-descriptions__label) {
+      color: var(--mint-text-muted, #64748b);
+      font-size: 12px;
+      font-weight: 600;
+      background: #f8fafc;
+    }
+
+    :deep(.el-descriptions__content) {
+      color: var(--mint-text, #1e293b);
+      font-weight: 500;
     }
 
     .voucher-preview-grid {
@@ -377,12 +484,12 @@
       position: relative;
       width: 120px;
       height: 100px;
-      border-radius: 12px;
+      border-radius: 10px;
       overflow: hidden;
       cursor: pointer;
-      border: 1px solid #ebeef5;
-      box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
-      background: #f5f7fa;
+      border: 1px solid var(--mint-border, #d9e8e2);
+      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+      background: #fff;
 
       &:hover .voucher-tile-play {
         opacity: 1;
@@ -404,18 +511,76 @@
       justify-content: center;
       background: rgba(0, 0, 0, 0.35);
       color: #fff;
-      opacity: 0.85;
+      opacity: 0.8;
       transition: opacity 0.2s;
     }
 
-    .orders-table {
+    .detail-orders-list {
       margin-top: 4px;
+      border: 1px solid var(--mint-border, #d9e8e2);
+      border-radius: 10px;
+      overflow: hidden;
+      background: #fff;
+    }
+
+    .orders-table {
+      margin-top: 0;
+    }
+
+    .orders-table :deep(th.el-table__cell > .cell),
+    .orders-table :deep(td.el-table__cell > .cell) {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+
+    .orders-table :deep(td.el-table__cell.el-table__expand-column > .cell) {
+      font-size: 0;
+      line-height: 0;
+      overflow: hidden;
+    }
+
+    .orders-table :deep(.el-table__expand-icon) {
+      width: 28px;
+      height: 28px;
+      border-radius: 6px;
+      border: 1px solid var(--mint-border, #d9e8e2);
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+      transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
+      color: var(--mint-text-muted, #64748b);
+      background: #fff;
+      font-size: 0;
+      line-height: 0;
+    }
+
+    .orders-table :deep(.el-table__expand-icon:hover) {
+      background: var(--mint-primary-bg, #ecfdf5);
+      border-color: #10b981;
+      color: var(--mint-primary-dark, #059669);
+    }
+
+    .orders-table :deep(.el-table__expand-icon--expanded) {
+      background: var(--mint-primary-bg, #ecfdf5);
+      border-color: #10b981;
+      color: var(--mint-primary-dark, #059669);
+    }
+
+    .orders-table :deep(.el-table__expand-icon svg) {
+      width: 14px;
+      height: 14px;
+      transition: transform 0.15s ease;
+    }
+
+    .orders-table :deep(.el-table__expand-icon--expanded svg) {
+      transform: rotate(180deg);
     }
 
     .order-row-expand-wrap {
-      padding: 8px 12px;
-      background: #fafbfc;
-      border-radius: 8px;
+      padding: 0;
+      background: #f8fafc;
+      border-radius: 0;
     }
 
     :deep(td.el-table__expanded-cell) {
@@ -430,30 +595,57 @@
     }
 
     .order-row-expand-table {
-      width: max-content;
+      width: 100%;
       max-width: 100%;
+      border: 1px solid var(--mint-border, #d9e8e2);
+      border-radius: 8px;
+      overflow: hidden;
+      background: #fff;
 
       :deep(.el-table__body-wrapper) {
         overflow-x: auto;
       }
+
+      :deep(th.el-table__cell) {
+        background: #e2e8f0 !important;
+        color: var(--mint-text-muted, #64748b);
+        font-size: 11px;
+        font-weight: 600;
+      }
+
+      :deep(.el-table__cell) {
+        padding: 8px 10px;
+      }
     }
 
     .settlement-row-expand-table-wrap {
-      display: inline-block;
-      width: fit-content;
+      display: block;
+      width: 100%;
       max-width: 100%;
-      overflow-x: auto;
-      overflow-y: hidden;
-      border-radius: 8px;
+      overflow: hidden;
+      border-radius: 0;
       text-align: left;
+      position: relative;
+      background: #f8fafc;
+      padding: 12px 384px 14px 16px;
 
       :deep(.el-table__inner-wrapper),
       :deep(.el-table__header-wrapper),
       :deep(.el-table__body-wrapper) {
-        width: max-content;
+        width: 100%;
       }
     }
 
+    .settlement-row-expand-table-wrap::before {
+      content: '';
+      position: absolute;
+      left: 0;
+      top: 0;
+      bottom: 0;
+      width: 3px;
+      background: #10b981;
+    }
+
     .order-row-expand-summary {
       margin-top: 10px;
       display: flex;

+ 10 - 10
web-admin/src/components/settlement/VoucherMediaUploader.vue

@@ -344,7 +344,7 @@
     margin-bottom: 8px;
     font-size: 15px;
     font-weight: 600;
-    color: #303133;
+    color: var(--mint-text, #1e293b);
   }
 
   .section-tip {
@@ -358,7 +358,7 @@
     margin: 0 0 12px;
     font-size: 12px;
     line-height: 1.5;
-    color: #909399;
+    color: var(--mint-text-muted, #64748b);
   }
 
   .banner-upload-section {
@@ -382,9 +382,9 @@
     width: 120px;
     padding: 10px;
     background: #fff;
-    border: 1px solid #ebeef5;
-    border-radius: 12px;
-    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
+    border: 1px solid var(--mint-border, #d9e8e2);
+    border-radius: 10px;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
   }
 
   .banner-card--uploading .banner-card-drag {
@@ -409,9 +409,9 @@
     gap: 6px;
     margin-bottom: 8px;
     padding-bottom: 8px;
-    border-bottom: 1px solid #f0f2f5;
+    border-bottom: 1px solid #eef4f0;
     font-size: 12px;
-    color: #606266;
+    color: var(--mint-text-muted, #64748b);
     user-select: none;
   }
 
@@ -424,7 +424,7 @@
     min-width: 18px;
     text-align: center;
     font-weight: 500;
-    color: #303133;
+    color: var(--mint-text, #1e293b);
   }
 
   .banner-image {
@@ -439,7 +439,7 @@
     border-radius: 8px;
     overflow: hidden;
     cursor: pointer;
-    background: #f5f7fa;
+    background: #f8fafc;
 
     &.is-uploading {
       cursor: default;
@@ -501,7 +501,7 @@
     display: flex;
     justify-content: center;
     padding-top: 12px;
-    border-top: 1px dashed #ebeef5;
+    border-top: 1px dashed var(--mint-border, #d9e8e2);
     margin-top: 12px;
   }
 

+ 5 - 2
web-admin/src/views/system/category/index.vue

@@ -9,7 +9,10 @@
 
     <section class="panel">
       <div class="panel-top">
-        <h2>分类列表</h2>
+        <h2>
+          <el-icon class="title-ic"><Setting /></el-icon>
+          <span>分类列表</span>
+        </h2>
       </div>
 
     <div class="filter-block">
@@ -417,7 +420,7 @@
   import { ref, reactive, onMounted, nextTick, watch, computed } from 'vue'
   import { ElMessage, ElMessageBox } from 'element-plus'
   import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
-  import { Plus, Search, Rank, Close, Check } from '@element-plus/icons-vue'
+  import { Plus, Search, Rank, Close, Check, Setting } from '@element-plus/icons-vue'
   import { VueDraggable as draggable } from 'vue-draggable-plus'
   import type { UploadFile } from 'element-plus'
   import {

Dosya farkı çok büyük olduğundan ihmal edildi
+ 513 - 157
web-admin/src/views/system/inquiry-quotation/index.vue


+ 3 - 3
web-admin/src/views/system/inquiry/index.vue

@@ -10,7 +10,7 @@
     <div class="panel">
       <div class="panel-top">
         <div class="panel-title">
-          <el-icon class="title-ic"><Operation /></el-icon>
+          <el-icon class="title-ic"><Setting /></el-icon>
           <span>筛选条件</span>
         </div>
       </div>
@@ -38,7 +38,7 @@
     <div class="panel">
       <div class="panel-top">
         <div class="panel-title">
-          <el-icon class="title-ic"><Operation /></el-icon>
+          <el-icon class="title-ic"><Setting /></el-icon>
           <span>询价列表</span>
         </div>
       </div>
@@ -234,7 +234,7 @@
   import { ref, reactive, onMounted, watch } from 'vue'
   import { ElMessage } from 'element-plus'
   import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
-  import { Plus, Operation } from '@element-plus/icons-vue'
+  import { Plus, Setting } from '@element-plus/icons-vue'
   import { fetchInquiryList, fetchInquiryDetail, fetchCreateInquiry, fetchPublishInquiry, fetchCloseInquiry } from '@/api/business'
   import { fetchActiveRegions, fetchProductList } from '@/api/admin'
   import { fetchAllPagedRecords } from '@/utils/fetch-paged-list'

Dosya farkı çok büyük olduğundan ihmal edildi
+ 546 - 252
web-admin/src/views/system/order/index.vue


Dosya farkı çok büyük olduğundan ihmal edildi
+ 620 - 205
web-admin/src/views/system/supplier-settlement/index.vue


+ 2 - 2
web-admin/src/views/system/user/index.vue

@@ -10,7 +10,7 @@
     <section class="panel">
       <div class="panel-top">
         <div class="panel-title">
-          <el-icon class="title-ic"><UserFilled /></el-icon>
+          <el-icon class="title-ic"><Setting /></el-icon>
           <span>用户列表</span>
         </div>
       </div>
@@ -297,7 +297,7 @@
   import { ref, reactive, onMounted, computed, watch } from 'vue'
   import { ElMessage, ElMessageBox } from 'element-plus'
   import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
-  import { Check, Close, Plus, UserFilled } from '@element-plus/icons-vue'
+  import { Check, Close, Plus, Setting } from '@element-plus/icons-vue'
   import {
     fetchUserList,
     fetchCreateUser,

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor