Bladeren bron

feat:更新

钱新宇 12 uur geleden
bovenliggende
commit
afcfe42c9e

+ 12 - 0
pages.json

@@ -83,12 +83,24 @@
             "navigationStyle": "custom"
           }
         },
+        {
+          "path": "traceabilityReport/pages/blacklist/history/index",
+          "style": {
+            "navigationBarTitleText": "历史记录"
+          }
+        },
         {
           "path": "traceabilityReport/pages/ganmaoling/detail/index",
           "style": {
             "navigationBarTitleText": "",
             "navigationStyle": "custom"
           }
+        },
+        {
+          "path": "traceabilityReport/pages/ganmaoling/history/index",
+          "style": {
+            "navigationBarTitleText": "历史记录"
+          }
         }
       ]
     }

+ 368 - 0
traceCodePackages/traceabilityReport/pages/blacklist/history/index.vue

@@ -0,0 +1,368 @@
+<template>
+  <view class="history-page">
+    <view class="header-section">
+      <view class="header-title">历史记录</view>
+      <view class="filter-tabs">
+        <view
+          class="tab-item"
+          v-for="(tab, index) in timeFilters"
+          :key="index"
+          :class="{ active: currentFilter === tab.value }"
+          @click="switchFilter(tab.value)"
+        >
+          {{ tab.label }}
+        </view>
+      </view>
+    </view>
+
+    <scroll-view
+      class="list-scroll"
+      scroll-y="true"
+      refresher-enabled
+      :refresher-triggered="isRefreshing"
+      @refresherrefresh="onRefresh"
+      @scrolltolower="onLoadMore"
+    >
+      <view class="list-container">
+        <view
+          class="card-item"
+          v-for="(item, index) in displayRows"
+          :key="index"
+        >
+          <view class="card-header">
+            <view class="header-left">
+              <text class="date-text">{{ item.date }}</text>
+            </view>
+            <view class="header-right">
+              <text class="status-tag">黑名单</text>
+            </view>
+          </view>
+
+          <view class="card-body">
+            <view class="row">
+              <text class="label">公司名称:</text>
+              <text class="value">{{ item.receiverName }}</text>
+            </view>
+            <view class="code-row">
+              <text class="label">信用代码:</text>
+              <text class="value code-text">{{ item.companyCode }}</text>
+            </view>
+            <view class="info-grid">
+              <view class="info-item">
+                <text class="label">省份</text>
+                <text class="value">{{ item.receiverProvince }}</text>
+              </view>
+              <view class="info-item">
+                <text class="label">性质</text>
+                <text class="value">{{ item.customerNature }}</text>
+              </view>
+            </view>
+          </view>
+        </view>
+
+        <view class="loading-more" v-if="loading">
+          <image
+            class="loading-icon"
+            src="../../../../static/images/loading.png"
+          />
+          <text class="loading-text">加载中...</text>
+        </view>
+
+        <view v-if="!loading && displayRows.length === 0" class="empty-data">
+          <EmptyView text="暂无历史记录" />
+        </view>
+
+        <view v-if="!hasMore && displayRows.length > 0" class="no-more">
+          <text>没有更多数据了</text>
+        </view>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+import EmptyView from "../../../../wigets/empty.vue";
+import { formatDate } from "../../../../utils/utils.js";
+
+const PROVINCES = ["北京市", "上海市", "广东省", "浙江省", "江苏省"];
+const NATURES = ["协议客户", "非协议客户", "商业客户", "终端客户"];
+
+export default {
+  components: {
+    EmptyView,
+  },
+  data() {
+    return {
+      isRefreshing: false,
+      loading: false,
+      rows: [],
+      hasMore: true,
+      pageNum: 1,
+      pageSize: 20,
+      currentFilter: "all",
+      timeFilters: [
+        { label: "全部", value: "all" },
+        { label: "近7天", value: "7" },
+        { label: "近15天", value: "15" },
+        { label: "近30天", value: "30" },
+      ],
+    };
+  },
+  computed: {
+    displayRows() {
+      return this.rows;
+    },
+  },
+  created() {
+    this.resetFetch();
+  },
+  methods: {
+    formatDate,
+
+    switchFilter(value) {
+      if (this.currentFilter === value) return;
+      this.currentFilter = value;
+      this.resetFetch();
+    },
+
+    generateFakeData() {
+      const newRows = [];
+      const count = 15;
+      const today = new Date();
+      const codes = [
+        "91370104771001730R",
+        "91420105783155681H",
+        "91330110MA2CCJE32Y",
+        "91441581761581268X",
+        "91510106768621824L",
+      ];
+
+      for (let i = 0; i < count; i++) {
+        const date = new Date(today);
+        const daysBack = Math.floor(Math.random() * 30);
+
+        if (this.currentFilter !== "all") {
+          const limit = parseInt(this.currentFilter);
+          if (daysBack > limit) continue;
+        }
+
+        date.setDate(date.getDate() - daysBack);
+
+        newRows.push({
+          id: Math.random().toString(36).substr(2, 9),
+          date: formatDate(date, "YYYY-MM-DD"),
+          receiverName: `黑名单企业${Math.floor(Math.random() * 100)}有限公司`,
+          companyCode: codes[Math.floor(Math.random() * codes.length)],
+          receiverProvince:
+            PROVINCES[Math.floor(Math.random() * PROVINCES.length)],
+          customerNature: NATURES[Math.floor(Math.random() * NATURES.length)],
+        });
+      }
+
+      newRows.sort((a, b) => new Date(b.date) - new Date(a.date));
+      return newRows;
+    },
+
+    async onRefresh() {
+      this.isRefreshing = true;
+      this.pageNum = 1;
+      this.hasMore = true;
+      setTimeout(() => {
+        this.rows = this.generateFakeData();
+        this.isRefreshing = false;
+      }, 1000);
+    },
+
+    onLoadMore() {
+      if (this.loading || !this.hasMore) return;
+      this.loading = true;
+      this.pageNum++;
+      setTimeout(() => {
+        const more = this.generateFakeData();
+        if (more.length > 0) {
+          this.rows = [...this.rows, ...more];
+        } else {
+          this.hasMore = false;
+        }
+        this.loading = false;
+      }, 800);
+    },
+
+    resetFetch() {
+      this.loading = true;
+      this.pageNum = 1;
+      this.hasMore = true;
+      this.rows = [];
+      setTimeout(() => {
+        this.rows = this.generateFakeData();
+        this.loading = false;
+      }, 500);
+    },
+  },
+};
+</script>
+
+<style scoped>
+.history-page {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background: #f5f7fa;
+}
+
+.header-section {
+  background: linear-gradient(135deg, #2b32b2 0%, #1488cc 100%);
+  padding: 30rpx 30rpx 60rpx;
+  color: #fff;
+  border-bottom-left-radius: 40rpx;
+  border-bottom-right-radius: 40rpx;
+  box-shadow: 0 10rpx 30rpx rgba(20, 136, 204, 0.2);
+  z-index: 10;
+  position: relative;
+}
+
+.header-title {
+  font-size: 36rpx;
+  font-weight: bold;
+  text-align: center;
+  margin-bottom: 30rpx;
+}
+
+.filter-tabs {
+  display: flex;
+  background: rgba(255, 255, 255, 0.2);
+  border-radius: 16rpx;
+  padding: 8rpx;
+}
+
+.tab-item {
+  flex: 1;
+  text-align: center;
+  font-size: 26rpx;
+  padding: 12rpx 0;
+  border-radius: 12rpx;
+  color: rgba(255, 255, 255, 0.8);
+  transition: all 0.3s;
+}
+
+.tab-item.active {
+  background: #fff;
+  color: #2b32b2;
+  font-weight: 600;
+  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
+}
+
+.list-scroll {
+  flex: 1;
+  height: 0;
+  margin-top: -40rpx;
+  z-index: 11;
+  padding: 0 24rpx;
+  box-sizing: border-box;
+}
+
+.list-container {
+  padding-bottom: 40rpx;
+}
+
+.card-item {
+  background: #fff;
+  border-radius: 20rpx;
+  padding: 30rpx;
+  margin-bottom: 24rpx;
+  box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.05);
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20rpx;
+  padding-bottom: 20rpx;
+  border-bottom: 1rpx solid #f5f5f5;
+}
+
+.date-text {
+  font-size: 28rpx;
+  font-weight: 600;
+  color: #333;
+}
+
+.status-tag {
+  font-size: 20rpx;
+  padding: 4rpx 12rpx;
+  border-radius: 8rpx;
+  background: #fff1f0;
+  color: #f5222d;
+  font-weight: 500;
+}
+
+.card-body {
+  font-size: 26rpx;
+}
+
+.row {
+  display: flex;
+  margin-bottom: 16rpx;
+}
+
+.code-row {
+  display: flex;
+  align-items: center;
+  margin-bottom: 16rpx;
+}
+
+.code-text {
+  font-family: monospace;
+  color: #333 !important;
+  font-weight: 600 !important;
+}
+
+.info-grid {
+  display: flex;
+  justify-content: space-between;
+}
+
+.info-item {
+  display: flex;
+  align-items: center;
+  width: 48%;
+}
+
+.label {
+  color: #999;
+  margin-right: 12rpx;
+}
+
+.value {
+  color: #666;
+  font-weight: 500;
+}
+
+.loading-more,
+.no-more,
+.empty-data {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 30rpx 0;
+  color: #999;
+  font-size: 24rpx;
+}
+
+.loading-icon {
+  width: 32rpx;
+  height: 32rpx;
+  margin-right: 12rpx;
+  animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
+</style>

+ 233 - 15
traceCodePackages/traceabilityReport/pages/blacklist/index.vue

@@ -12,16 +12,30 @@
           <text class="stat-unit">家</text>
         </view>
       </view>
-      <view class="update-tip">
-        <text class="tip-icon">🕒</text>
-        <text
-          >数据更新时间:{{
-            formatDate(
-              new Date().setDate(new Date().getDate() - 1),
-              "YYYY-MM-DD",
-            ) || "--"
-          }}</text
-        >
+      <view class="header-toolbar">
+        <view class="update-tip">
+          <text class="tip-icon">🕒</text>
+          <text
+            >更新:{{
+              formatDate(
+                new Date().setDate(new Date().getDate() - 1),
+                "YYYY-MM-DD",
+              ) || "--"
+            }}</text
+          >
+        </view>
+
+        <view class="filter-wrap">
+          <view class="selector" @click.stop="toggleProvinceDropdown">
+            <text class="selector-text">{{
+              selectedProvince || "全部省份"
+            }}</text>
+            <text
+              class="selector-arrow"
+              :class="{ open: dropdownProvinceOpen }"
+            ></text>
+          </view>
+        </view>
       </view>
       <!-- 背景装饰 -->
       <view class="header-circle circle-1"></view>
@@ -90,10 +104,49 @@
     </scroll-view>
 
     <view class="footer-btn-area">
+      <button class="action-btn history-btn" @click="handleHistory">
+        历史记录
+      </button>
       <button class="action-btn export-btn" @click="handleExport">
         导出至邮箱
       </button>
     </view>
+
+    <!-- 独立的下拉菜单层 -->
+    <view class="dropdown-layer" v-if="dropdownProvinceOpen" @click.stop>
+      <view class="dropdown">
+        <view class="dropdown-search-bar">
+          <input
+            class="dropdown-search-input"
+            v-model="provinceSearchText"
+            @input="onProvinceSearch"
+            placeholder="搜索省份..."
+            placeholder-style="color: #999"
+          />
+        </view>
+        <scroll-view scroll-y="true" class="dropdown-scroll-view">
+          <view
+            class="dropdown-item"
+            v-for="(p, i) in filteredProvinceList"
+            :key="p || i"
+            :class="{
+              active: p === selectedProvince || (!selectedProvince && i === 0),
+            }"
+            @click.stop="selectProvince(p)"
+          >
+            {{ p || "全部省份" }}
+            <text
+              v-if="p === selectedProvince || (!selectedProvince && i === 0)"
+              class="check-mark"
+              >✓</text
+            >
+          </view>
+          <view v-if="filteredProvinceList.length === 0" class="dropdown-empty"
+            >暂无数据</view
+          >
+        </scroll-view>
+      </view>
+    </view>
   </view>
 </template>
 
@@ -102,7 +155,6 @@ import EmptyView from "../../../wigets/empty.vue";
 import request from "../../../request/index.js";
 import { formatDate } from "../../../utils/utils.js";
 
-const PROVINCES = ["北京市", "上海市", "广东省", "浙江省", "江苏省"];
 const NATURES = ["协议客户", "非协议客户", "商业客户", "终端客户"];
 
 export default {
@@ -118,14 +170,30 @@ export default {
       hasMore: true,
       pageNum: 1,
       pageSize: 20,
+      dropdownProvinceOpen: false,
+      provinceSearchText: "",
+      provinceList: [""],
+      selectedProvince: "",
     };
   },
   created() {
     this.resetFetch();
+    this.getProviceList();
   },
   methods: {
     formatDate,
 
+    getProviceList() {
+      request("/common/getProviceList", {
+        path: "traceabilityReport/pages/blacklist/index.vue",
+      }).then((res) => {
+        if (res.code == 200) {
+          const _data = res.data || [];
+          this.provinceList = ["", ..._data.map((item) => item.regionName)];
+        }
+      });
+    },
+
     generateFakeData() {
       const newRows = [];
       const codes = [
@@ -135,6 +203,10 @@ export default {
         "91441581761581268X",
         "91510106768621824L",
       ];
+      const provinces =
+        this.provinceList.length > 1
+          ? this.provinceList.slice(1)
+          : ["北京市", "上海市", "广东省", "浙江省", "江苏省"];
 
       const startIdx = (this.pageNum - 1) * this.pageSize;
       const endIdx = Math.min(startIdx + this.pageSize, this.totalCount);
@@ -149,7 +221,7 @@ export default {
           id: i,
           receiverName: `测试收货企业${i + 1}有限公司`,
           companyCode: codes[i % codes.length],
-          receiverProvince: PROVINCES[i % PROVINCES.length],
+          receiverProvince: provinces[i % provinces.length],
           customerNature: NATURES[i % NATURES.length],
         });
       }
@@ -201,6 +273,12 @@ export default {
       });
     },
 
+    handleHistory() {
+      uni.navigateTo({
+        url: "/traceCodePackages/traceabilityReport/pages/blacklist/history/index",
+      });
+    },
+
     handleExport() {
       uni.showModal({
         title: "导出至邮箱",
@@ -223,6 +301,29 @@ export default {
         },
       });
     },
+
+    toggleProvinceDropdown() {
+      this.dropdownProvinceOpen = !this.dropdownProvinceOpen;
+    },
+
+    closeProvinceDropdown() {
+      this.dropdownProvinceOpen = false;
+    },
+
+    selectProvince(province) {
+      this.selectedProvince = province || "";
+      this.dropdownProvinceOpen = false;
+    },
+  },
+  computed: {
+    filteredProvinceList() {
+      if (!this.provinceSearchText) {
+        return this.provinceList;
+      }
+      return this.provinceList.filter((p) =>
+        p?.toLowerCase()?.includes(this.provinceSearchText.toLowerCase()),
+      );
+    },
   },
 };
 </script>
@@ -292,6 +393,14 @@ export default {
   opacity: 0.8;
 }
 
+.header-toolbar {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  position: relative;
+  z-index: 3;
+}
+
 .update-tip {
   display: inline-flex;
   align-items: center;
@@ -300,8 +409,6 @@ export default {
   border-radius: 30rpx;
   font-size: 22rpx;
   backdrop-filter: blur(10px);
-  position: relative;
-  z-index: 2;
 }
 
 .tip-icon {
@@ -309,6 +416,108 @@ export default {
   font-size: 20rpx;
 }
 
+/* Filter Styles */
+.filter-wrap {
+  position: relative;
+}
+
+.selector {
+  display: flex;
+  align-items: center;
+  color: #fff;
+  font-size: 26rpx;
+  font-weight: 500;
+  padding: 8rpx 20rpx;
+  background: rgba(255, 255, 255, 0.2);
+  border-radius: 30rpx;
+  border: 1rpx solid rgba(255, 255, 255, 0.3);
+}
+
+.selector-text {
+  max-width: 200rpx;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+}
+
+.selector-arrow {
+  margin-left: 10rpx;
+  width: 0;
+  height: 0;
+  border-left: 8rpx solid transparent;
+  border-right: 8rpx solid transparent;
+  border-top: 10rpx solid #fff;
+  transition: transform 0.3s;
+}
+
+.selector-arrow.open {
+  transform: rotate(180deg);
+}
+
+/* Dropdown Layer (Absolute on top of everything) */
+.dropdown-layer {
+  position: absolute;
+  top: 300rpx; /* Adjust based on header layout */
+  right: 40rpx;
+  z-index: 999;
+}
+
+.dropdown {
+  width: 360rpx;
+  background: #fff;
+  border-radius: 12rpx;
+  box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.15);
+  overflow: hidden;
+}
+
+.dropdown-search-bar {
+  padding: 16rpx;
+  border-bottom: 1rpx solid #f0f0f0;
+}
+
+.dropdown-search-input {
+  background: #f5f7fa;
+  height: 64rpx;
+  border-radius: 32rpx;
+  padding: 0 24rpx;
+  font-size: 26rpx;
+}
+
+.dropdown-scroll-view {
+  max-height: 400rpx;
+}
+
+.dropdown-item {
+  padding: 20rpx 30rpx;
+  font-size: 28rpx;
+  color: #333;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  transition: background 0.2s;
+}
+
+.dropdown-item:active {
+  background: #f5f7fa;
+}
+
+.dropdown-item.active {
+  color: #1890ff;
+  font-weight: 500;
+  background: #e6f7ff;
+}
+
+.check-mark {
+  font-size: 24rpx;
+}
+
+.dropdown-empty {
+  padding: 40rpx;
+  text-align: center;
+  color: #999;
+  font-size: 26rpx;
+}
+
 /* Header Decor Circles */
 .header-circle {
   position: absolute;
@@ -503,10 +712,12 @@ export default {
   bottom: 0;
   width: 100%;
   box-sizing: border-box;
+  display: flex;
+  gap: 24rpx;
 }
 
 .action-btn {
-  width: 100%;
+  flex: 1;
   height: 88rpx;
   line-height: 88rpx;
   border-radius: 44rpx;
@@ -525,6 +736,13 @@ export default {
   transform: scale(0.96);
 }
 
+.history-btn {
+  background: #fff;
+  color: #1890ff;
+  border: 2rpx solid #1890ff;
+  box-shadow: 0 4rpx 12rpx rgba(24, 144, 255, 0.1);
+}
+
 .export-btn {
   background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
   color: #fff;

+ 348 - 0
traceCodePackages/traceabilityReport/pages/ganmaoling/history/index.vue

@@ -0,0 +1,348 @@
+<template>
+  <view class="history-page">
+    <view class="header-section">
+      <view class="filter-tabs">
+        <view
+          class="tab-item"
+          v-for="(tab, index) in timeFilters"
+          :key="index"
+          :class="{ active: currentFilter === tab.value }"
+          @click="switchFilter(tab.value)"
+        >
+          {{ tab.label }}
+        </view>
+      </view>
+    </view>
+
+    <scroll-view
+      class="list-scroll"
+      scroll-y="true"
+      refresher-enabled
+      :refresher-triggered="isRefreshing"
+      @refresherrefresh="onRefresh"
+      @scrolltolower="onLoadMore"
+    >
+      <view class="list-container">
+        <view
+          class="card-item"
+          v-for="(item, index) in displayRows"
+          :key="index"
+        >
+          <view class="card-header">
+            <view class="header-left">
+              <text class="date-text">{{ item.date }}</text>
+            </view>
+            <view class="header-right">
+              <text class="alert-count" v-if="item.alertCount > 0"
+                >{{ item.alertCount }}次预警</text
+              >
+            </view>
+          </view>
+
+          <view class="card-body">
+            <view class="row">
+              <text class="label">公司名称:</text>
+              <text class="value">{{ item.receiverName }}</text>
+            </view>
+            <view class="info-grid">
+              <view class="info-item">
+                <text class="label">省份</text>
+                <text class="value">{{ item.receiverProvince }}</text>
+              </view>
+              <view class="info-item">
+                <text class="label">性质</text>
+                <text class="value">{{ item.customerNature }}</text>
+              </view>
+            </view>
+          </view>
+        </view>
+
+        <view class="loading-more" v-if="loading">
+          <image
+            class="loading-icon"
+            src="../../../../static/images/loading.png"
+          />
+          <text class="loading-text">加载中...</text>
+        </view>
+
+        <view v-if="!loading && displayRows.length === 0" class="empty-data">
+          <EmptyView text="暂无历史记录" />
+        </view>
+
+        <view v-if="!hasMore && displayRows.length > 0" class="no-more">
+          <text>没有更多数据了</text>
+        </view>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+import EmptyView from "../../../../wigets/empty.vue";
+import { formatDate } from "../../../../utils/utils.js";
+
+const PROVINCES = ["北京市", "上海市", "广东省", "浙江省", "江苏省"];
+
+export default {
+  components: {
+    EmptyView,
+  },
+  data() {
+    return {
+      isRefreshing: false,
+      loading: false,
+      rows: [],
+      hasMore: true,
+      pageNum: 1,
+      pageSize: 20,
+      currentFilter: "all",
+      timeFilters: [
+        { label: "全部", value: "all" },
+        { label: "近7天", value: "7" },
+        { label: "近15天", value: "15" },
+        { label: "近30天", value: "30" },
+      ],
+    };
+  },
+  computed: {
+    displayRows() {
+      return this.rows;
+    },
+  },
+  created() {
+    this.resetFetch();
+  },
+  methods: {
+    formatDate,
+
+    switchFilter(value) {
+      if (this.currentFilter === value) return;
+      this.currentFilter = value;
+      this.resetFetch();
+    },
+
+    generateFakeData() {
+      const newRows = [];
+      const count = 15;
+      const today = new Date();
+
+      for (let i = 0; i < count; i++) {
+        const date = new Date(today);
+        // Random date within last 30 days
+        const daysBack = Math.floor(Math.random() * 30);
+
+        // Filter logic simulation
+        if (this.currentFilter !== "all") {
+          const limit = parseInt(this.currentFilter);
+          if (daysBack > limit) continue;
+        }
+
+        date.setDate(date.getDate() - daysBack);
+
+        newRows.push({
+          id: Math.random().toString(36).substr(2, 9),
+          date: formatDate(date, "YYYY-MM-DD"),
+          receiverName: `历史收货企业${Math.floor(Math.random() * 100)}有限公司`,
+          receiverProvince:
+            PROVINCES[Math.floor(Math.random() * PROVINCES.length)],
+          customerNature: Math.random() > 0.5 ? "协议客户" : "非协议客户",
+          alertCount: Math.floor(Math.random() * 5),
+        });
+      }
+
+      // Sort by date descending
+      newRows.sort((a, b) => new Date(b.date) - new Date(a.date));
+
+      return newRows;
+    },
+
+    async onRefresh() {
+      this.isRefreshing = true;
+      this.pageNum = 1;
+      this.hasMore = true;
+      setTimeout(() => {
+        this.rows = this.generateFakeData();
+        this.isRefreshing = false;
+      }, 1000);
+    },
+
+    onLoadMore() {
+      if (this.loading || !this.hasMore) return;
+      this.loading = true;
+      this.pageNum++;
+      setTimeout(() => {
+        const more = this.generateFakeData();
+        if (more.length > 0) {
+          this.rows = [...this.rows, ...more];
+        } else {
+          this.hasMore = false;
+        }
+        this.loading = false;
+      }, 800);
+    },
+
+    resetFetch() {
+      this.loading = true;
+      this.pageNum = 1;
+      this.hasMore = true;
+      this.rows = [];
+      setTimeout(() => {
+        this.rows = this.generateFakeData();
+        this.loading = false;
+      }, 500);
+    },
+  },
+};
+</script>
+
+<style scoped>
+.history-page {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background: #f5f7fa;
+}
+
+.header-section {
+  background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
+  padding: 30rpx 30rpx 60rpx;
+  color: #fff;
+  border-bottom-left-radius: 40rpx;
+  border-bottom-right-radius: 40rpx;
+  box-shadow: 0 10rpx 30rpx rgba(24, 144, 255, 0.2);
+  z-index: 10;
+  position: relative;
+}
+
+.header-title {
+  font-size: 36rpx;
+  font-weight: bold;
+  text-align: center;
+  margin-bottom: 30rpx;
+}
+
+.filter-tabs {
+  display: flex;
+  background: rgba(255, 255, 255, 0.2);
+  border-radius: 16rpx;
+  padding: 8rpx;
+}
+
+.tab-item {
+  flex: 1;
+  text-align: center;
+  font-size: 26rpx;
+  padding: 12rpx 0;
+  border-radius: 12rpx;
+  color: rgba(255, 255, 255, 0.8);
+  transition: all 0.3s;
+}
+
+.tab-item.active {
+  background: #fff;
+  color: #1890ff;
+  font-weight: 600;
+  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
+}
+
+.list-scroll {
+  flex: 1;
+  height: 0;
+  margin-top: -40rpx;
+  z-index: 11;
+  padding: 0 24rpx;
+  box-sizing: border-box;
+}
+
+.list-container {
+  padding-bottom: 40rpx;
+}
+
+.card-item {
+  background: #fff;
+  border-radius: 20rpx;
+  padding: 30rpx;
+  margin-bottom: 24rpx;
+  box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.05);
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20rpx;
+  padding-bottom: 20rpx;
+  border-bottom: 1rpx solid #f5f5f5;
+}
+
+.date-text {
+  font-size: 28rpx;
+  font-weight: 600;
+  color: #333;
+}
+
+.alert-count {
+  font-size: 22rpx;
+  color: #ff4d4f;
+  background: rgba(255, 77, 79, 0.1);
+  padding: 4rpx 12rpx;
+  border-radius: 8rpx;
+}
+
+.card-body {
+  font-size: 26rpx;
+}
+
+.row {
+  display: flex;
+  margin-bottom: 16rpx;
+}
+
+.info-grid {
+  display: flex;
+  justify-content: space-between;
+}
+
+.info-item {
+  display: flex;
+  align-items: center;
+  width: 48%;
+}
+
+.label {
+  color: #999;
+  margin-right: 12rpx;
+}
+
+.value {
+  color: #666;
+  font-weight: 500;
+}
+
+.loading-more,
+.no-more,
+.empty-data {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 30rpx 0;
+  color: #999;
+  font-size: 24rpx;
+}
+
+.loading-icon {
+  width: 32rpx;
+  height: 32rpx;
+  margin-right: 12rpx;
+  animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
+</style>

+ 38 - 15
traceCodePackages/traceabilityReport/pages/ganmaoling/index.vue

@@ -7,8 +7,13 @@
     <view class="header-section">
       <view class="header-content">
         <view class="title-group">
-          <text class="main-title">感冒灵颗粒</text>
-          <text class="sub-title">药品流向实时追踪</text>
+          <text class="main-title">感冒灵大批量跨区域客户</text>
+          <text class="sub-title"
+            >订单维度:单笔订单实际盒数 {{ ">" }} 20000盒</text
+          >
+          <text class="sub-title"
+            >地域维度:出库企业所在省份 ≠ 入库企业所在省份</text
+          >
         </view>
         <view class="stat-box">
           <text class="stat-num">{{ totalCount }}</text>
@@ -164,8 +169,6 @@ import EmptyView from "../../../wigets/empty.vue";
 import request from "../../../request/index.js";
 import { formatDate } from "../../../utils/utils.js";
 
-const PROVINCES = ["北京市", "上海市", "广东省", "浙江省", "江苏省"];
-
 export default {
   components: {
     EmptyView,
@@ -181,7 +184,7 @@ export default {
       pageSize: 20,
       dropdownProvinceOpen: false,
       provinceSearchText: "",
-      provinceList: ["", ...PROVINCES],
+      provinceList: [""],
       selectedProvince: "",
     };
   },
@@ -202,10 +205,22 @@ export default {
   },
   created() {
     this.resetFetch();
+    this.getProviceList();
   },
   methods: {
     formatDate,
 
+    getProviceList() {
+      request("/common/getProviceList", {
+        path: "traceabilityReport/pages/ganmaoling/index.vue",
+      }).then((res) => {
+        if (res.code == 200) {
+          const _data = res.data || [];
+          this.provinceList = ["", ..._data.map((item) => item.regionName)];
+        }
+      });
+    },
+
     getLevelClass(level) {
       if (level === "VIP") return "tag-vip";
       if (level === "二级") return "tag-l2";
@@ -218,6 +233,10 @@ export default {
       const levels = ["VIP", "二级", "三级"];
       const natures = ["协议客户", "非协议客户"];
       const managers = ["张明华", "李建华", "王丽萍", "陈大文"];
+      const provinces =
+        this.provinceList.length > 1
+          ? this.provinceList.slice(1)
+          : ["北京市", "上海市", "广东省", "浙江省", "江苏省"];
 
       const startIdx = (this.pageNum - 1) * this.pageSize;
       const endIdx = Math.min(startIdx + this.pageSize, this.totalCount);
@@ -231,7 +250,7 @@ export default {
         newRows.push({
           id: i,
           receiverName: `测试收货企业${i + 1}有限公司`,
-          receiverProvince: PROVINCES[i % PROVINCES.length],
+          receiverProvince: provinces[i % provinces.length],
           customerLevel: levels[i % levels.length],
           customerNature: natures[i % natures.length],
           manager: managers[i % managers.length],
@@ -325,9 +344,8 @@ export default {
     },
 
     handleHistory() {
-      uni.showToast({
-        title: "历史记录功能开发中",
-        icon: "none",
+      uni.navigateTo({
+        url: "/traceCodePackages/traceabilityReport/pages/ganmaoling/history/index",
       });
     },
   },
@@ -370,27 +388,32 @@ export default {
 .header-content {
   display: flex;
   justify-content: space-between;
-  align-items: center;
+  align-items: flex-start;
   position: relative;
   z-index: 2;
-  margin-bottom: 24rpx;
+  margin-bottom: 30rpx;
 }
 
 .title-group {
   display: flex;
   flex-direction: column;
+  flex: 1;
+  margin-right: 20rpx;
 }
 
 .main-title {
-  font-size: 40rpx;
+  font-size: 44rpx;
   font-weight: bold;
-  margin-bottom: 8rpx;
+  margin-bottom: 16rpx;
   letter-spacing: 2rpx;
+  line-height: 1.2;
 }
 
 .sub-title {
   font-size: 24rpx;
-  opacity: 0.8;
+  opacity: 0.9;
+  margin-bottom: 8rpx;
+  line-height: 1.4;
 }
 
 .stat-box {
@@ -474,7 +497,7 @@ export default {
 /* Dropdown Layer (Absolute on top of everything) */
 .dropdown-layer {
   position: absolute;
-  top: 220rpx; /* Adjust based on header layout */
+  top: 270rpx; /* Adjust based on header layout */
   right: 40rpx;
   z-index: 999;
 }

+ 2 - 2
traceCodePackages/traceabilityReport/pages/reportExport/index.vue

@@ -398,7 +398,7 @@
               <input
                 class="report-export-dropdown-search-input"
                 v-model="form.otherCustomerCode"
-                placeholder="请填写其他客户信用代码(请依照其他客户填写顺序多个客户用半角逗号隔开)"
+                placeholder="请填写信用代码(请依照其他客户填写顺序用半角逗号隔开)"
                 placeholder-class="report-export-dropdown-other-customer-input"
                 :disabled="modalType === 'read'"
                 :style="{ color: '#666', padding: 0, border: 'none' }"
@@ -1617,7 +1617,7 @@ export default {
       const url = "/report/sendEmail";
       request(url, {
         taskId: this.currentItem?.id,
-        emailAddress: email + "@999.com.cn",
+        emailAddress: uni.getStorageSync("accessToken") || "", //email + "@999.com.cn",
         path: "reportExport/index.vue",
       }).then((res) => {
         uni.hideLoading();