893 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			893 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Vue
		
	
	
	
| <template>
 | |
|   <view class="point-list-container">
 | |
|     <!-- 搜索筛选栏 -->
 | |
|     <view class="search-bar">
 | |
|       <view class="search-item">
 | |
|         <text class="label">阶段</text>
 | |
|         <view class="select-box" @click="stageListVisible = true">
 | |
|           <text class="select-text">{{ stageName || '请选择阶段' }}</text>
 | |
|           <u-icon name="arrow-right" size="24rpx" color="#999"></u-icon>
 | |
|         </view>
 | |
|       </view>
 | |
|       <!-- <view class="filter-btn" @click="showFilter = true">
 | |
|         <u-icon name="list" size="28rpx" color="#005bac"></u-icon>
 | |
|         <text>筛选</text>
 | |
|       </view> -->
 | |
|     </view>
 | |
| 
 | |
|     <!-- 状态筛选按钮组 -->
 | |
|     <view class="status-filter-bar">
 | |
|       <view class="filter-title">状态</view>
 | |
|       <view class="status-buttons">
 | |
|         <view
 | |
|           v-for="(item, index) in statusOptions[0]"
 | |
|           :key="index"
 | |
|           class="status-btn"
 | |
|           :class="{ active: selectedStatusValue === item.value }"
 | |
|           @click="onStatusButtonClick(item)"
 | |
|         >
 | |
|           <text>{{ item.label }}</text>
 | |
|         </view>
 | |
|       </view>
 | |
|     </view>
 | |
| 
 | |
|     <!-- 点位列表 -->
 | |
|     <view class="point-list-content">
 | |
|       <view v-if="pointList.length > 0" class="list-wrapper">
 | |
|         <view
 | |
|           v-for="(item, index) in pointList"
 | |
|           :key="index"
 | |
|           class="point-item"
 | |
|         >
 | |
|           <!-- 基本信息 -->
 | |
|           <view class="item-header">
 | |
|             <view class="member-info">
 | |
|               <text class="member-code">{{
 | |
|                 `${currentUser.memberCode}-${item.childNode}`
 | |
|               }}</text>
 | |
|             </view>
 | |
|             <!-- 点位展示区域 -->
 | |
|             <view class="points-container">
 | |
|               <!-- 第一层 (1-1, 1-2) -->
 | |
|               <view class="layer-wrapper">
 | |
|                 <view class="points-row">
 | |
|                   <view
 | |
|                     v-for="pos in 2"
 | |
|                     :key="`1-${pos}`"
 | |
|                     class="point-slot"
 | |
|                     :class="getPointClass(item[`pointMember1${pos}`])"
 | |
|                   >
 | |
|                   </view>
 | |
|                 </view>
 | |
|               </view>
 | |
| 
 | |
|               <!-- 第二层 (2-1, 2-2, 2-3, 2-4) -->
 | |
|               <view class="layer-wrapper">
 | |
|                 <view class="points-row">
 | |
|                   <view
 | |
|                     v-for="pos in 4"
 | |
|                     :key="`2-${pos}`"
 | |
|                     class="point-slot"
 | |
|                     :class="getPointClass(item[`pointMember2${pos}`])"
 | |
|                   >
 | |
|                   </view>
 | |
|                 </view>
 | |
|               </view>
 | |
| 
 | |
|               <!-- 第三层 (3-1 到 3-8) -->
 | |
|               <view class="layer-wrapper">
 | |
|                 <view class="points-row multi-row">
 | |
|                   <view
 | |
|                     v-for="pos in 8"
 | |
|                     :key="`3-${pos}`"
 | |
|                     class="point-slot small"
 | |
|                     :class="getPointClass(item[`pointMember3${pos}`])"
 | |
|                   >
 | |
|                   </view>
 | |
|                 </view>
 | |
|               </view>
 | |
|             </view>
 | |
|           </view>
 | |
|         </view>
 | |
|       </view>
 | |
| 
 | |
|       <!-- 空状态 -->
 | |
|       <view v-else class="empty-state">
 | |
|         <u-empty mode="data" text="暂无点位数据"></u-empty>
 | |
|       </view>
 | |
| 
 | |
|       <!-- 加载更多提示 -->
 | |
|       <view v-if="pointList.length > 0" class="load-more">
 | |
|         <view v-if="loading && currentPage > 1" class="loading-text">
 | |
|           <u-loading-icon mode="spinner"></u-loading-icon>
 | |
|           <text>加载中...</text>
 | |
|         </view>
 | |
|         <view v-else-if="!hasMore" class="no-more-text">
 | |
|           <text>没有更多数据了</text>
 | |
|         </view>
 | |
|         <view v-else class="pull-up-text">
 | |
|           <text>上拉加载更多</text>
 | |
|         </view>
 | |
|       </view>
 | |
|     </view>
 | |
| 
 | |
|     <!-- 点位详情弹窗 -->
 | |
|     <u-popup
 | |
|       :show="showPointDetail"
 | |
|       mode="center"
 | |
|       @close="showPointDetail = false"
 | |
|       :closeOnClickOverlay="true"
 | |
|       borderRadius="20"
 | |
|     >
 | |
|       <view class="point-detail-popup">
 | |
|         <view class="popup-header">
 | |
|           <text class="popup-title">点位详情</text>
 | |
|           <u-icon
 | |
|             name="close"
 | |
|             @click="showPointDetail = false"
 | |
|             size="32rpx"
 | |
|           ></u-icon>
 | |
|         </view>
 | |
|         <view class="popup-content">
 | |
|           <view v-if="pointDetailLoading" class="loading-detail">
 | |
|             <u-loading-icon mode="spinner"></u-loading-icon>
 | |
|             <text>加载详情中...</text>
 | |
|           </view>
 | |
|           <view v-else>
 | |
|             <view class="detail-row">
 | |
|               <text class="detail-label">点位信息:</text>
 | |
|               <text class="detail-value">{{
 | |
|                 selectedPoint.position || ''
 | |
|               }}</text>
 | |
|             </view>
 | |
|             <view class="detail-row">
 | |
|               <text class="detail-label">会员编号:</text>
 | |
|               <text class="detail-value">{{ pointDetail.memberCode }}</text>
 | |
|             </view>
 | |
|             <view class="detail-row">
 | |
|               <text class="detail-label">会员姓名:</text>
 | |
|               <text class="detail-value">{{ pointDetail.memberName }}</text>
 | |
|             </view>
 | |
| 
 | |
|             <view v-if="pointDetail" class="detail-row">
 | |
|               <text class="detail-label">隶属体系:</text>
 | |
|               <text class="detail-value">{{
 | |
|                 pointDetail.system || pointDetail.parentSystem || 'BF30720213'
 | |
|               }}</text>
 | |
|             </view>
 | |
|             <view v-if="pointDetail" class="detail-row">
 | |
|               <text class="detail-label">荣誉奖衔:</text>
 | |
|               <text class="detail-value">{{
 | |
|                 pointDetail.honorTitle || '无'
 | |
|               }}</text>
 | |
|             </view>
 | |
|             <view v-if="pointDetail" class="detail-row">
 | |
|               <text class="detail-label">创建时间:</text>
 | |
|               <text class="detail-value">{{
 | |
|                 formatDate(pointDetail.creationTime || pointDetail.createTime)
 | |
|               }}</text>
 | |
|             </view>
 | |
|           </view>
 | |
|         </view>
 | |
|       </view>
 | |
|     </u-popup>
 | |
| 
 | |
|     <!-- 筛选弹窗 -->
 | |
|     <u-popup
 | |
|       :show="showFilter"
 | |
|       mode="right"
 | |
|       @close="showFilter = false"
 | |
|       :closeOnClickOverlay="false"
 | |
|     >
 | |
|       <view class="filter-popup">
 | |
|         <view class="popup-header">
 | |
|           <text @click="applyFilter">筛选</text>
 | |
|           <text class="close-btn" @click="showFilter = false">返回</text>
 | |
|         </view>
 | |
| 
 | |
|         <view class="filter-actions">
 | |
|           <u-button type="info" @click="clearFilter">清空筛选条件</u-button>
 | |
|           <u-button type="primary" @click="confirmFilter">确定</u-button>
 | |
|         </view>
 | |
|       </view>
 | |
|     </u-popup>
 | |
| 
 | |
|     <!-- 选择器 -->
 | |
|     <u-picker
 | |
|       :show="stageListVisible"
 | |
|       :columns="stageOptions"
 | |
|       @confirm="onStageConfirm"
 | |
|       @cancel="stageListVisible = false"
 | |
|       keyName="label"
 | |
|     ></u-picker>
 | |
| 
 | |
|     <u-picker
 | |
|       :show="statusListVisible"
 | |
|       :columns="statusOptions"
 | |
|       @confirm="onStatusConfirm"
 | |
|       @cancel="statusListVisible = false"
 | |
|       keyName="label"
 | |
|     ></u-picker>
 | |
|   </view>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| import * as arc from '@/config/architecture.js'
 | |
| 
 | |
| export default {
 | |
|   data() {
 | |
|     return {
 | |
|       // 列表数据
 | |
|       pointList: [],
 | |
|       total: 0,
 | |
|       currentPage: 1,
 | |
|       pageSize: 10,
 | |
|       hasMore: true,
 | |
| 
 | |
|       // 筛选条件
 | |
|       queryParams: {
 | |
|         pageNum: 1,
 | |
|         pageSize: 10,
 | |
|         stage: 1,
 | |
|         stageStatus: null,
 | |
|         childNode: '',
 | |
|       },
 | |
| 
 | |
|       // 选择器数据
 | |
|       stageOptions: [
 | |
|         [
 | |
|           { value: 1, label: '阶段一' },
 | |
|           { value: 2, label: '阶段二' },
 | |
|           { value: 3, label: '阶段三' },
 | |
|         ],
 | |
|       ],
 | |
|       statusOptions: [
 | |
|         [
 | |
|           { value: '', label: '全部' },
 | |
|           { value: 0, label: '已完成' },
 | |
|           { value: 1, label: '未完成' },
 | |
|         ],
 | |
|       ],
 | |
| 
 | |
|       // UI状态
 | |
|       showFilter: false,
 | |
|       showPointDetail: false,
 | |
|       stageListVisible: false,
 | |
|       statusListVisible: false,
 | |
|       loading: false,
 | |
| 
 | |
|       // 选中的值
 | |
|       stageName: '阶段一',
 | |
|       statusName: '全部',
 | |
|       selectedStatusValue: '', // 当前选中的状态值
 | |
| 
 | |
|       // 点位详情
 | |
|       selectedPoint: {},
 | |
|       pointDetail: {},
 | |
|       pointDetailLoading: false,
 | |
|       currentUser: {},
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   onLoad() {
 | |
|     this.init()
 | |
|     this.$store.dispatch('GetInfo').then(res => {
 | |
|       this.currentUser = res.data
 | |
|     })
 | |
|   },
 | |
|   onShow() {},
 | |
| 
 | |
|   onPullDownRefresh() {
 | |
|     this.refreshData()
 | |
|   },
 | |
| 
 | |
|   onReachBottom() {
 | |
|     this.loadMore()
 | |
|   },
 | |
| 
 | |
|   methods: {
 | |
|     init() {
 | |
|       this.stageName = this.stageOptions[0][0].label
 | |
|       this.queryParams.stage = this.stageOptions[0][0].value
 | |
|       // 设置默认状态为"全部"
 | |
|       this.selectedStatusValue = this.statusOptions[0][0].value
 | |
|       this.statusName = this.statusOptions[0][0].label
 | |
|       this.queryParams.stageStatus = this.selectedStatusValue || null
 | |
|       this.loadPointList()
 | |
|     },
 | |
| 
 | |
|     // 加载点位列表数据
 | |
|     async loadPointList() {
 | |
|       try {
 | |
|         this.loading = true
 | |
|         // 只在首次加载或刷新时显示全局loading
 | |
|         if (this.currentPage === 1) {
 | |
|           uni.showLoading({ title: '加载中...' })
 | |
|         }
 | |
| 
 | |
|         const res = await arc.getAzFrameworkList(this.queryParams)
 | |
| 
 | |
|         if (res.code === 200 && res.data) {
 | |
|           const newData = res.data.rows || []
 | |
| 
 | |
|           if (this.currentPage === 1) {
 | |
|             this.pointList = newData
 | |
|           } else {
 | |
|             this.pointList = [...this.pointList, ...newData]
 | |
|           }
 | |
|           this.total = res.data.total || 0
 | |
| 
 | |
|           // 优化hasMore判断逻辑
 | |
|           this.hasMore =
 | |
|             newData.length >= this.pageSize &&
 | |
|             this.pointList.length < this.total
 | |
|         } else {
 | |
|           uni.showToast({
 | |
|             title: res.msg || '加载失败',
 | |
|             icon: 'none',
 | |
|           })
 | |
|         }
 | |
|       } catch (error) {
 | |
|         console.error('加载点位列表失败:', error)
 | |
|         uni.showToast({
 | |
|           title: '网络异常,请稍后重试',
 | |
|           icon: 'none',
 | |
|         })
 | |
|       } finally {
 | |
|         this.loading = false
 | |
|         // 只在首次加载或刷新时隐藏全局loading
 | |
|         if (this.currentPage <= 1) {
 | |
|           uni.hideLoading()
 | |
|         }
 | |
|         uni.stopPullDownRefresh()
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     // 刷新数据
 | |
|     refreshData() {
 | |
|       this.currentPage = 1
 | |
|       this.queryParams.pageNum = 1
 | |
|       this.hasMore = true
 | |
|       this.loadPointList()
 | |
|     },
 | |
| 
 | |
|     // 加载更多数据
 | |
|     async loadMore() {
 | |
|       if (this.loading || !this.hasMore) return
 | |
| 
 | |
|       this.currentPage++
 | |
|       this.queryParams.pageNum = this.currentPage
 | |
|       await this.loadPointList()
 | |
|     },
 | |
| 
 | |
|     // 检查某一层是否有点位
 | |
|     hasLayerPoints(item, layer) {
 | |
|       const layerPoints = {
 | |
|         1: ['pointMember11', 'pointMember12'],
 | |
|         2: ['pointMember21', 'pointMember22', 'pointMember23', 'pointMember24'],
 | |
|         3: [
 | |
|           'pointMember31',
 | |
|           'pointMember32',
 | |
|           'pointMember33',
 | |
|           'pointMember34',
 | |
|           'pointMember35',
 | |
|           'pointMember36',
 | |
|           'pointMember37',
 | |
|           'pointMember38',
 | |
|         ],
 | |
|       }
 | |
| 
 | |
|       return layerPoints[layer].some(key => item.hasOwnProperty(key))
 | |
|     },
 | |
| 
 | |
|     // 获取点位样式类
 | |
|     getPointClass(pointMember) {
 | |
|       return {
 | |
|         occupied: pointMember && pointMember !== '',
 | |
|         empty: !pointMember || pointMember === '',
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     // 点位点击处理
 | |
|     async handlePointClick(item, layer, position, pointMember) {
 | |
|       if (!pointMember || pointMember === '') {
 | |
|         // 空点位提示
 | |
|         uni.showToast({
 | |
|           title: '当前点位为空位',
 | |
|           icon: 'none',
 | |
|         })
 | |
|         return
 | |
|       }
 | |
| 
 | |
|       // 设置选中的点位信息
 | |
|       this.selectedPoint = {
 | |
|         ...item,
 | |
|         position: `${layer}-${position}`,
 | |
|         pointMember: pointMember,
 | |
|       }
 | |
| 
 | |
|       // 获取点位详情
 | |
|       await this.getPointDetail({
 | |
|         point: item[`point${layer}${position}`] || position,
 | |
|         pointMember: pointMember,
 | |
|         stage: item.stage,
 | |
|       })
 | |
|       // 显示弹窗
 | |
|       this.showPointDetail = true
 | |
|     },
 | |
| 
 | |
|     // 获取点位详情
 | |
|     async getPointDetail(params) {
 | |
|       try {
 | |
|         this.pointDetailLoading = true
 | |
|         this.pointDetail = {}
 | |
| 
 | |
|         const res = await arc.getMemberPointDetail(params)
 | |
| 
 | |
|         if (res.code === 200 && res.data) {
 | |
|           this.pointDetail = res.data
 | |
|         } else {
 | |
|           uni.showToast({
 | |
|             title: res.msg || '获取详情失败',
 | |
|             icon: 'none',
 | |
|           })
 | |
|         }
 | |
|       } catch (error) {
 | |
|         console.error('获取点位详情失败:', error)
 | |
|         uni.showToast({
 | |
|           title: '网络异常,请稍后重试',
 | |
|           icon: 'none',
 | |
|         })
 | |
|       } finally {
 | |
|         this.pointDetailLoading = false
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     // 阶段选择确认
 | |
|     onStageConfirm(e) {
 | |
|       this.queryParams.stage = e.value[0].value
 | |
|       this.stageName = e.value[0].label
 | |
|       this.stageListVisible = false
 | |
|       this.refreshData()
 | |
|     },
 | |
| 
 | |
|     // 状态选择确认
 | |
|     onStatusConfirm(e) {
 | |
|       this.queryParams.stageStatus = e.value[0].value
 | |
|       this.statusName = e.value[0].label
 | |
|       this.selectedStatusValue = e.value[0].value
 | |
|       this.statusListVisible = false
 | |
|     },
 | |
| 
 | |
|     // 状态按钮点击处理
 | |
|     onStatusButtonClick(item) {
 | |
|       this.selectedStatusValue = item.value
 | |
|       this.statusName = item.label
 | |
|       this.queryParams.stageStatus = item.value
 | |
|       this.refreshData()
 | |
|     },
 | |
| 
 | |
|     // 应用筛选
 | |
|     applyFilter() {
 | |
|       this.refreshData()
 | |
|       this.showFilter = false
 | |
|     },
 | |
| 
 | |
|     // 确认筛选
 | |
|     confirmFilter() {
 | |
|       this.refreshData()
 | |
|       this.showFilter = false
 | |
|     },
 | |
| 
 | |
|     // 清空筛选
 | |
|     clearFilter() {
 | |
|       this.queryParams.stageStatus = null
 | |
|       this.statusName = '全部'
 | |
|       this.selectedStatusValue = ''
 | |
|       this.refreshData()
 | |
|       this.showFilter = false
 | |
|     },
 | |
| 
 | |
|     // 日期格式化
 | |
|     formatDate(dateStr) {
 | |
|       if (!dateStr) return ''
 | |
|       const date = new Date(dateStr)
 | |
|       return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
 | |
|     },
 | |
|   },
 | |
| }
 | |
| </script>
 | |
| 
 | |
| <style lang="scss" scoped>
 | |
| .point-list-container {
 | |
|   min-height: 100vh;
 | |
|   background-color: #f5f6f8;
 | |
| }
 | |
| 
 | |
| /* 搜索筛选栏 */
 | |
| .search-bar {
 | |
|   background: #fff;
 | |
|   padding: 20rpx 24rpx;
 | |
|   display: flex;
 | |
|   align-items: center;
 | |
|   justify-content: space-between;
 | |
|   border-bottom: 2rpx solid #eee;
 | |
| 
 | |
|   .search-item {
 | |
|     flex: 1;
 | |
|     display: flex;
 | |
|     align-items: center;
 | |
| 
 | |
|     .label {
 | |
|       font-size: 28rpx;
 | |
|       color: #333;
 | |
|       margin-right: 20rpx;
 | |
|     }
 | |
| 
 | |
|     .select-box {
 | |
|       flex: 1;
 | |
|       background: #f5f6f8;
 | |
|       border-radius: 34rpx;
 | |
|       padding: 20rpx 24rpx;
 | |
|       display: flex;
 | |
|       justify-content: space-between;
 | |
|       align-items: center;
 | |
| 
 | |
|       .select-text {
 | |
|         font-size: 26rpx;
 | |
|         color: #333;
 | |
|       }
 | |
| 
 | |
|       &:active {
 | |
|         background-color: #e8e9eb;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .filter-btn {
 | |
|     display: flex;
 | |
|     align-items: center;
 | |
|     margin-left: 20rpx;
 | |
|     padding: 12rpx 20rpx;
 | |
| 
 | |
|     text {
 | |
|       font-size: 26rpx;
 | |
|       color: #005bac;
 | |
|       margin-left: 8rpx;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* 状态筛选按钮组 */
 | |
| .status-filter-bar {
 | |
|   background: #fff;
 | |
|   padding: 20rpx 24rpx;
 | |
|   border-bottom: 2rpx solid #eee;
 | |
| 
 | |
|   .filter-title {
 | |
|     font-size: 28rpx;
 | |
|     color: #333;
 | |
|     font-weight: bold;
 | |
|     margin-bottom: 20rpx;
 | |
|   }
 | |
| 
 | |
|   .status-buttons {
 | |
|     display: flex;
 | |
|     gap: 16rpx;
 | |
| 
 | |
|     .status-btn {
 | |
|       flex: 1;
 | |
|       background: #f5f6f8;
 | |
|       border-radius: 24rpx;
 | |
|       padding: 16rpx 24rpx;
 | |
|       display: flex;
 | |
|       align-items: center;
 | |
|       justify-content: center;
 | |
|       transition: all 0.2s ease;
 | |
|       border: 2rpx solid transparent;
 | |
| 
 | |
|       text {
 | |
|         font-size: 26rpx;
 | |
|         color: #666;
 | |
|         transition: color 0.2s ease;
 | |
|       }
 | |
| 
 | |
|       &.active {
 | |
|         background: #e8f4fd;
 | |
|         border-color: #005bac;
 | |
| 
 | |
|         text {
 | |
|           color: #005bac;
 | |
|           font-weight: bold;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       &:active {
 | |
|         background-color: #e8e9eb;
 | |
|       }
 | |
| 
 | |
|       &.active:active {
 | |
|         background-color: #d1e9f7;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* 点位列表内容 */
 | |
| .point-list-content {
 | |
|   padding: 20rpx 16rpx;
 | |
| }
 | |
| 
 | |
| .list-wrapper {
 | |
|   .point-item {
 | |
|     background: #fff;
 | |
|     border-radius: 20rpx;
 | |
|     padding: 20rpx 20rpx;
 | |
|     margin-bottom: 20rpx;
 | |
|     box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
 | |
| 
 | |
|     .item-header {
 | |
|       display: flex;
 | |
|       justify-content: space-between;
 | |
|       align-items: flex-start;
 | |
| 
 | |
|       .member-info {
 | |
|         display: flex;
 | |
|         align-items: center;
 | |
|         width: 36%;
 | |
|         .member-code {
 | |
|           font-size: 24rpx;
 | |
|           font-weight: bold;
 | |
|           color: #333;
 | |
|           flex-shrink: 0;
 | |
|           margin-right: 30rpx;
 | |
|         }
 | |
| 
 | |
|         .status-badge {
 | |
|           padding: 8rpx 16rpx;
 | |
|           border-radius: 20rpx;
 | |
|           font-size: 22rpx;
 | |
| 
 | |
|           &.completed {
 | |
|             background: #e8f5e8;
 | |
|             color: #52c41a;
 | |
|           }
 | |
| 
 | |
|           &.pending {
 | |
|             background: #fff2e8;
 | |
|             color: #fa8c16;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       .stage-info {
 | |
|         display: flex;
 | |
|         flex-direction: column;
 | |
|         align-items: flex-end;
 | |
| 
 | |
|         .stage {
 | |
|           font-size: 26rpx;
 | |
|           color: #005bac;
 | |
|           font-weight: bold;
 | |
|         }
 | |
| 
 | |
|         .date {
 | |
|           font-size: 22rpx;
 | |
|           color: #999;
 | |
|           margin-top: 8rpx;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     .item-footer {
 | |
|       display: flex;
 | |
|       justify-content: space-between;
 | |
|       align-items: center;
 | |
|       margin-top: 30rpx;
 | |
|       padding-top: 20rpx;
 | |
|       border-top: 2rpx solid #f5f6f8;
 | |
| 
 | |
|       .point-type {
 | |
|         font-size: 24rpx;
 | |
|         color: #666;
 | |
|       }
 | |
| 
 | |
|       .creation-time {
 | |
|         font-size: 22rpx;
 | |
|         color: #999;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* 点位展示区域 - 紧凑布局 */
 | |
| .points-container {
 | |
|   display: flex;
 | |
|   gap: 20rpx;
 | |
|   .layer-wrapper {
 | |
|     .points-row {
 | |
|       display: flex;
 | |
|       flex-wrap: wrap;
 | |
|       gap: 4rpx;
 | |
| 
 | |
|       .point-slot {
 | |
|         flex: 1;
 | |
|         width: 26rpx !important;
 | |
|         height: 26rpx !important;
 | |
|         border-radius: 50%;
 | |
|         display: flex;
 | |
|         align-items: center;
 | |
|         justify-content: center;
 | |
|         position: relative;
 | |
|         transition: all 0.2s ease;
 | |
| 
 | |
|         &.occupied {
 | |
|           background: linear-gradient(135deg, #005bac 0%, #0066cc 100%);
 | |
|           color: #fff;
 | |
|           box-shadow: 0 2rpx 8rpx rgba(0, 91, 172, 0.25);
 | |
|         }
 | |
| 
 | |
|         &.empty {
 | |
|           background: #fafbfc;
 | |
|           border: 1rpx solid #000;
 | |
|           color: #000;
 | |
| 
 | |
|           &:active {
 | |
|             background: #f0f0f0;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* 加载更多 */
 | |
| .load-more {
 | |
|   padding: 40rpx 0;
 | |
|   text-align: center;
 | |
| 
 | |
|   .loading-text,
 | |
|   .no-more-text,
 | |
|   .pull-up-text {
 | |
|     display: flex;
 | |
|     align-items: center;
 | |
|     justify-content: center;
 | |
|     font-size: 26rpx;
 | |
|     color: #999;
 | |
| 
 | |
|     text {
 | |
|       margin-left: 12rpx;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .no-more-text {
 | |
|     color: #ccc;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* 空状态 */
 | |
| .empty-state {
 | |
|   padding: 100rpx 0;
 | |
|   text-align: center;
 | |
| }
 | |
| 
 | |
| /* 弹窗样式 */
 | |
| .point-detail-popup {
 | |
|   width: 600rpx;
 | |
|   background: #fff;
 | |
|   border-radius: 20rpx;
 | |
|   overflow: hidden;
 | |
| 
 | |
|   .popup-header {
 | |
|     display: flex;
 | |
|     justify-content: space-between;
 | |
|     align-items: center;
 | |
|     padding: 30rpx;
 | |
|     background: #f5f6f8;
 | |
| 
 | |
|     .popup-title {
 | |
|       font-size: 32rpx;
 | |
|       font-weight: bold;
 | |
|       color: #333;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .popup-content {
 | |
|     padding: 30rpx;
 | |
| 
 | |
|     .loading-detail {
 | |
|       display: flex;
 | |
|       align-items: center;
 | |
|       justify-content: center;
 | |
|       padding: 60rpx 0;
 | |
|       font-size: 26rpx;
 | |
|       color: #999;
 | |
| 
 | |
|       text {
 | |
|         margin-left: 12rpx;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     .detail-row {
 | |
|       display: flex;
 | |
|       margin-bottom: 25rpx;
 | |
| 
 | |
|       &:last-child {
 | |
|         margin-bottom: 0;
 | |
|       }
 | |
| 
 | |
|       .detail-label {
 | |
|         width: 180rpx;
 | |
|         font-size: 26rpx;
 | |
|         color: #666;
 | |
|         flex-shrink: 0;
 | |
|       }
 | |
| 
 | |
|       .detail-value {
 | |
|         flex: 1;
 | |
|         font-size: 26rpx;
 | |
|         color: #333;
 | |
|         word-break: break-all;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| .filter-popup {
 | |
|   width: 600rpx;
 | |
|   height: 100vh;
 | |
|   background: #fff;
 | |
| 
 | |
|   .popup-header {
 | |
|     display: flex;
 | |
|     justify-content: space-between;
 | |
|     align-items: center;
 | |
|     padding: 30rpx;
 | |
|     background: rgba(176, 196, 222, 0.45);
 | |
| 
 | |
|     .close-btn {
 | |
|       color: #005bac;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .filter-section {
 | |
|     padding: 30rpx;
 | |
| 
 | |
|     .section-title {
 | |
|       display: block;
 | |
|       font-size: 30rpx;
 | |
|       font-weight: bold;
 | |
|       color: #333;
 | |
|       margin-bottom: 20rpx;
 | |
|     }
 | |
| 
 | |
|     .select-box {
 | |
|       background: rgba(176, 196, 222, 0.45);
 | |
|       border-radius: 20rpx;
 | |
|       padding: 20rpx 30rpx;
 | |
|       display: flex;
 | |
|       justify-content: space-between;
 | |
|       align-items: center;
 | |
| 
 | |
|       .select-text {
 | |
|         font-size: 26rpx;
 | |
|         color: #333;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .filter-actions {
 | |
|     position: absolute;
 | |
|     bottom: 0;
 | |
|     left: 0;
 | |
|     right: 0;
 | |
|     display: flex;
 | |
| 
 | |
|     .u-button {
 | |
|       flex: 1;
 | |
|       border-radius: 0;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| </style>
 |