|
|
@@ -18,21 +18,18 @@
|
|
|
</view>
|
|
|
|
|
|
<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 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 class="filter-wrap">
|
|
|
<view class="selector" @click.stop="toggleProvinceDropdown">
|
|
|
<text class="selector-text">{{
|
|
|
selectedProvince || "全部省份"
|
|
|
- }}</text>
|
|
|
+ }}</text>
|
|
|
<text class="selector-arrow" :class="{ open: dropdownProvinceOpen }"></text>
|
|
|
</view>
|
|
|
</view>
|
|
|
@@ -43,33 +40,45 @@
|
|
|
<scroll-view class="list-scroll" scroll-y="true" refresher-enabled :refresher-triggered="isRefreshing"
|
|
|
@refresherrefresh="onRefresh">
|
|
|
<view class="list-container">
|
|
|
- <view class="card-item" v-for="(item, index) in rows" :key="index" @click="toDetail(item)">
|
|
|
- <view class="card-header">
|
|
|
- <view class="header-left">
|
|
|
- <text class="index-num">{{ index + 1 }}</text>
|
|
|
- <text class="company-name">{{ item.customerName }}</text>
|
|
|
- <text class="level-tag" :class="getLevelClass(item.customerLevel)">
|
|
|
- {{ item.customerLevel }}
|
|
|
- </text>
|
|
|
- </view>
|
|
|
- <view class="header-right">
|
|
|
- <text class="alert-count">{{ item.totalWarningAmount }}次预警</text>
|
|
|
- </view>
|
|
|
+ <view class="date-group" v-for="group in groupedRows" :key="group.date">
|
|
|
+ <view class="group-header" @click="toggleGroup(group)">
|
|
|
+ <view class="checkbox group-checkbox" :class="{ checked: isGroupChecked(group) }"></view>
|
|
|
+ <text class="group-date">{{ group.date }}</text>
|
|
|
</view>
|
|
|
|
|
|
- <view class="card-body">
|
|
|
- <view class="info-grid">
|
|
|
- <view class="info-item">
|
|
|
- <text class="label">省份</text>
|
|
|
- <text class="value">{{ item.customerProvinceName ?? '--' }}</text>
|
|
|
- </view>
|
|
|
- <view class="info-item">
|
|
|
- <text class="label">责任人</text>
|
|
|
- <text class="value">{{ item.responsibleManager }}</text>
|
|
|
+ <view class="card-item" v-for="(item, index) in group.list" :key="index" @click="toDetail(item)">
|
|
|
+ <view class="checkbox item-checkbox" :class="{ checked: item.checked }" @click.stop="toggleItem(item)">
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="card-content">
|
|
|
+ <view class="card-header">
|
|
|
+ <view class="header-left">
|
|
|
+ <text class="index-num">{{ index + 1 }}</text>
|
|
|
+ <text class="company-name">{{ item.customerName }}</text>
|
|
|
+ <text class="level-tag" :class="getLevelClass(item.customerLevel)">
|
|
|
+ {{ item.customerLevel }}
|
|
|
+ </text>
|
|
|
+ </view>
|
|
|
+ <view class="header-right">
|
|
|
+ <text class="alert-count">{{ item.totalWarningAmount }}次预警</text>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
- <view class="info-item">
|
|
|
- <text class="label">性质</text>
|
|
|
- <text class="value">{{ item.customerCategory }}</text>
|
|
|
+
|
|
|
+ <view class="card-body">
|
|
|
+ <view class="info-grid">
|
|
|
+ <view class="info-item">
|
|
|
+ <text class="label">省份</text>
|
|
|
+ <text class="value">{{ item.customerProvinceName ?? '--' }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-item">
|
|
|
+ <text class="label">责任人</text>
|
|
|
+ <text class="value">{{ item.responsibleManager }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-item">
|
|
|
+ <text class="label">性质</text>
|
|
|
+ <text class="value">{{ item.customerCategory }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
@@ -83,9 +92,9 @@
|
|
|
|
|
|
<!-- 底部按钮区 -->
|
|
|
<view class="footer-btn-area">
|
|
|
- <button class="action-btn history-btn" @click="handleHistory">
|
|
|
+ <!-- <button class="action-btn history-btn" @click="handleHistory">
|
|
|
历史记录
|
|
|
- </button>
|
|
|
+ </button> -->
|
|
|
<button class="action-btn export-btn" @click="handleExport">
|
|
|
导出至邮箱
|
|
|
</button>
|
|
|
@@ -166,6 +175,13 @@ export default {
|
|
|
email: "",
|
|
|
},
|
|
|
emailError: false,
|
|
|
+ currentFilter: "1",
|
|
|
+ timeFilters: [
|
|
|
+ { label: "昨日", value: "1" },
|
|
|
+ { label: "近7天", value: "7" },
|
|
|
+ { label: "近15天", value: "15" },
|
|
|
+ { label: "近30天", value: "30" },
|
|
|
+ ],
|
|
|
};
|
|
|
},
|
|
|
created() {
|
|
|
@@ -195,19 +211,25 @@ export default {
|
|
|
return "tag-default";
|
|
|
},
|
|
|
|
|
|
+ switchFilter(value) {
|
|
|
+ if (this.currentFilter === value) return;
|
|
|
+ this.currentFilter = value;
|
|
|
+ this.resetFetch();
|
|
|
+ },
|
|
|
+
|
|
|
fetchList() {
|
|
|
if (this.loading) return;
|
|
|
this.loading = true;
|
|
|
|
|
|
request(
|
|
|
- `/report/ganmaoling/list?days=1`,
|
|
|
+ `/report/ganmaoling/list?days=${this.currentFilter}`,
|
|
|
{
|
|
|
path: "traceabilityReport/pages/ganmaoling/index.vue",
|
|
|
},
|
|
|
"get",
|
|
|
).then((res) => {
|
|
|
if (res.code == 200) {
|
|
|
- this.allRows = res.data || [];
|
|
|
+ this.allRows = (res.data || []).map(item => ({ ...item, checked: false }));
|
|
|
this.applyFilter();
|
|
|
}
|
|
|
this.loading = false;
|
|
|
@@ -307,12 +329,46 @@ export default {
|
|
|
},
|
|
|
|
|
|
handleHistory() {
|
|
|
- uni.navigateTo({
|
|
|
- url: "/traceCodePackages/traceabilityReport/pages/ganmaoling/history/index",
|
|
|
+ // uni.navigateTo({
|
|
|
+ // url: "/traceCodePackages/traceabilityReport/pages/ganmaoling/history/index",
|
|
|
+ // });
|
|
|
+ },
|
|
|
+
|
|
|
+ toggleItem(item) {
|
|
|
+ item.checked = !item.checked;
|
|
|
+ },
|
|
|
+
|
|
|
+ toggleGroup(group) {
|
|
|
+ const allChecked = this.isGroupChecked(group);
|
|
|
+ group.list.forEach(item => {
|
|
|
+ item.checked = !allChecked;
|
|
|
});
|
|
|
},
|
|
|
+
|
|
|
+ isGroupChecked(group) {
|
|
|
+ if (!group || !group.list || group.list.length === 0) return false;
|
|
|
+ return group.list.every(item => item.checked);
|
|
|
+ },
|
|
|
},
|
|
|
computed: {
|
|
|
+ groupedRows() {
|
|
|
+ const groups = {};
|
|
|
+ this.rows.forEach((item) => {
|
|
|
+ const fullTime = item.updatedTime || "";
|
|
|
+ const date = fullTime.split(" ")[0] || fullTime.split("T")[0] || "未知日期";
|
|
|
+ if (!groups[date]) {
|
|
|
+ groups[date] = [];
|
|
|
+ }
|
|
|
+ groups[date].push(item);
|
|
|
+ });
|
|
|
+ // Sort by date descending
|
|
|
+ return Object.keys(groups)
|
|
|
+ .sort((a, b) => new Date(b.replace(/-/g, "/")) - new Date(a.replace(/-/g, "/")))
|
|
|
+ .map((date) => ({
|
|
|
+ date,
|
|
|
+ list: groups[date],
|
|
|
+ }));
|
|
|
+ },
|
|
|
filteredProvinceList() {
|
|
|
if (!this.provinceSearchText) {
|
|
|
return this.provinceList;
|
|
|
@@ -432,6 +488,30 @@ export default {
|
|
|
font-size: 20rpx;
|
|
|
}
|
|
|
|
|
|
+/* Filter Tabs */
|
|
|
+.filter-tabs {
|
|
|
+ display: flex;
|
|
|
+ background: rgba(255, 255, 255, 0.2);
|
|
|
+ border-radius: 30rpx;
|
|
|
+ padding: 4rpx;
|
|
|
+ margin-right: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.tab-item {
|
|
|
+ padding: 8rpx 24rpx;
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: rgba(255, 255, 255, 0.8);
|
|
|
+ border-radius: 26rpx;
|
|
|
+ transition: all 0.3s;
|
|
|
+}
|
|
|
+
|
|
|
+.tab-item.active {
|
|
|
+ background: #fff;
|
|
|
+ color: #1890ff;
|
|
|
+ font-weight: 600;
|
|
|
+ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
/* Filter Styles */
|
|
|
.filter-wrap {
|
|
|
position: relative;
|
|
|
@@ -541,7 +621,7 @@ export default {
|
|
|
height: 0;
|
|
|
padding: 0 24rpx;
|
|
|
box-sizing: border-box;
|
|
|
- margin-top: -50rpx;
|
|
|
+ margin-top: -20rpx;
|
|
|
/* Overlap effect */
|
|
|
position: relative;
|
|
|
z-index: 2;
|
|
|
@@ -553,13 +633,85 @@ export default {
|
|
|
padding-bottom: calc(200rpx + env(safe-area-inset-bottom));
|
|
|
}
|
|
|
|
|
|
+.date-group {
|
|
|
+ margin-bottom: 24rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.group-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ padding: 24rpx 30rpx;
|
|
|
+ background: #e6f7ff;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ box-shadow: 0 4rpx 12rpx rgba(24, 144, 255, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.group-date {
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #1890ff;
|
|
|
+ margin-left: 20rpx;
|
|
|
+ line-height: 44rpx;
|
|
|
+ /* Match checkbox height for alignment */
|
|
|
+}
|
|
|
+
|
|
|
+.checkbox {
|
|
|
+ width: 44rpx;
|
|
|
+ height: 44rpx;
|
|
|
+ border: 3rpx solid #d9d9d9;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ flex-shrink: 0;
|
|
|
+ position: relative;
|
|
|
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
+ box-sizing: border-box;
|
|
|
+ margin-top: 0;
|
|
|
+ /* Reset margin */
|
|
|
+ background: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.checkbox.checked {
|
|
|
+ background: #1890ff;
|
|
|
+ border-color: #1890ff;
|
|
|
+ box-shadow: 0 4rpx 10rpx rgba(24, 144, 255, 0.3);
|
|
|
+}
|
|
|
+
|
|
|
+.checkbox.checked::after {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ left: 14rpx;
|
|
|
+ top: 8rpx;
|
|
|
+ width: 10rpx;
|
|
|
+ height: 20rpx;
|
|
|
+ border: 4rpx solid #fff;
|
|
|
+ border-top: 0;
|
|
|
+ border-left: 0;
|
|
|
+ transform: rotate(45deg);
|
|
|
+}
|
|
|
+
|
|
|
.card-item {
|
|
|
background: #fff;
|
|
|
border-radius: 20rpx;
|
|
|
padding: 30rpx;
|
|
|
margin-bottom: 24rpx;
|
|
|
- box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.05);
|
|
|
- transition: transform 0.2s;
|
|
|
+ box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
|
|
|
+ transition: all 0.2s;
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ /* Align to top */
|
|
|
+}
|
|
|
+
|
|
|
+.item-checkbox {
|
|
|
+ margin-right: 24rpx;
|
|
|
+ margin-top: 6rpx;
|
|
|
+ /* Align with first line of text */
|
|
|
+}
|
|
|
+
|
|
|
+.card-content {
|
|
|
+ flex: 1;
|
|
|
+ overflow: hidden;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
}
|
|
|
|
|
|
.card-item:active {
|