-
admin
8 天以前 9ee8fbbecf45f58fd2469d9049b7938e363d9a4f
提交 | 用户 | 时间
ac315c 1 <template>
2d516c 2     <view class="container">
A 3         <!-- 搜索区域 -->
4         <view class="search-container">
5             <view class="search-box">
6                 <view class="search-item" @click="showProjectCodeSelector">
7                     <text class="search-label">{{ searchParams.projectCode || '项目编码' }}</text>
8                     <text class="triangle-down">▼</text>
9                 </view>
10                 <view class="search-item" @click="showProjectNameSelector">
11                     <text class="search-label">{{ searchParams.projectName || '项目名称' }}</text>
12                     <text class="triangle-down">▼</text>
13                 </view>
14                 <!-- 添加重置按钮 -->
15                 <view class="reset-btn" @click="resetSearch" v-if="searchParams.projectCode || searchParams.projectName">
16                     <uni-icons type="clear" size="14"></uni-icons>
17                 </view>
ac315c 18             </view>
A 19         </view>
2d516c 20
A 21         <!-- 项目编码选择弹出层 -->
22         <uni-popup 
23             ref="projectCodePopup" 
24             type="bottom"
25             @change="(e) => onPopupChange('projectCode', e.show)"
26         >
27             <view class="popup-content">
28                 <view class="popup-header">
29                     <text class="popup-title">选择项目编码</text>
30                     <text class="popup-close" @click="closeProjectCodePopup">×</text>
31                 </view>
32                 <!-- 添加搜索框 -->
33                 <view class="popup-search">
34                     <uni-easyinput
35                         v-model="projectCodeKeyword"
36                         placeholder="搜索项目编码"
37                         prefixIcon="search"
38                     />
39                 </view>
40                 <scroll-view scroll-y class="popup-list">
41                     <view v-if="!allProjectCodes.size" class="popup-loading">
42                         <uni-load-more status="loading" :content-text="loadingText"></uni-load-more>
43                     </view>
44                     <view 
45                         v-else
46                         class="popup-item" 
47                         v-for="item in filteredProjectCodes" 
48                         :key="item"
49                         @click="selectProjectCode(item)"
50                     >
51                         {{ item }}
52                     </view>
53                 </scroll-view>
54             </view>
55         </uni-popup>
56
57         <!-- 项目名称选择弹出层 -->
58         <uni-popup 
59             ref="projectNamePopup" 
60             type="bottom"
61             @change="(e) => onPopupChange('projectName', e.show)"
62         >
63             <view class="popup-content">
64                 <view class="popup-header">
65                     <text class="popup-title">选择项目名称</text>
66                     <text class="popup-close" @click="closeProjectNamePopup">×</text>
67                 </view>
68                 <!-- 添加搜索框 -->
69                 <view class="popup-search">
70                     <uni-easyinput
71                         v-model="projectNameKeyword"
72                         placeholder="搜索项目名称"
73                         prefixIcon="search"
74                     />
75                 </view>
76                 <scroll-view scroll-y class="popup-list">
77                     <view v-if="!allProjectNames.size" class="popup-loading">
78                         <uni-load-more status="loading" :content-text="loadingText"></uni-load-more>
79                     </view>
80                     <view 
81                         v-else
82                         class="popup-item" 
83                         v-for="item in filteredProjectNames" 
84                         :key="item"
85                         @click="selectProjectName(item)"
86                     >
87                         {{ item }}
88                     </view>
89                 </scroll-view>
90             </view>
91         </uni-popup>
92
93         <!-- 表格内容区域 -->
94         <view class="table-container">
95             <view class="table-header">
96                 <text class="header-title">工时统计列表</text>
97                 <text class="total-count">共 {{ total }} 条</text>
98             </view>
99             
100             <!-- 表格内容 -->
101             <view class="table-content" v-if="!loading">
102                 <view v-for="(item, index) in tableData" 
103                       :key="index" 
104                       class="card"
105                       @click="handleCardClick(item)"
106                 >
107                     <view class="card-content">
108                         <view class="item-row">
109                             <text class="label">项目编码:</text>
110                             <text class="value">{{ item.projectCode }}</text>
111                         </view>
112                         <view class="item-row">
113                             <text class="label">项目名称:</text>
114                             <text class="value">{{ item.projectName }}</text>
115                         </view>
116                         <!-- <view class="item-row">
117                             <text class="label">工时:</text>
118                             <text class="value highlight">{{ item.workTime }} 小时</text>
119                         </view> -->
120                         <view class="item-row">
121                             <text class="label">工时:</text>
122                             <text class="value highlight">{{ Number(item.workTime).toFixed(1) }} 小时</text>
123                         </view>
124                         <view class="item-row">
125                             <text class="label">工种:</text>
126                             <text class="value">{{ item.workType }}</text>
127                         </view>
128                         <view class="item-row">
129                             <text class="label">项目阶段:</text>
130                             <text class="value">{{ item.projectPhase }}</text>
131                         </view>
132                         <view class="item-row">
133                             <text class="label">报工时间:</text>
134                             <text class="value">{{ item.reportTime }}</text>
135                         </view>
136                     </view>
137                 </view>
138             </view>
139             
140             <!-- 加载状态 -->
141             <view class="loading-container" v-if="loading">
142                 <uni-load-more status="loading"></uni-load-more>
143             </view>
144             
145             <!-- 空状态 -->
146             <view class="empty-container" v-if="!loading && (!tableData || tableData.length === 0)">
147                 <image src="/static/empty.png" mode="aspectFit" class="empty-image"></image>
148                 <text class="empty-text">暂无数据</text>
149             </view>
150         </view>
151     </view>
ac315c 152 </template>
A 153
154 <script>
2d516c 155     import { listWorkHourInfoNoPage } from '@/api/dema/workHourInfo.js'
A 156     
157     export default {
158         data() {
159             return {
160                     searchParams: {
161                         projectCode: '',
162                         projectName: '',
163                         pageNum: 1,
164                         pageSize: 10
165                     },
166                     loading: false,
167                     tableData: [],
168                     total: 0,
169                     // 项目编码和名称列表
170                     projectCodes: [],
171                     projectNames: [],
172                     // 搜索关键词
173                     projectCodeKeyword: '',
174                     projectNameKeyword: '',
175                     // 用于存储所有不重复的项目编码和名称
176                     allProjectCodes: new Set(),
177                     allProjectNames: new Set(),
178                     loadingText: {
179                         contentdown: '加载中...',
180                         contentrefresh: '加载中...',
181                         contentnomore: '没有更多数据'
182                     },
183                     // 添加弹出层状态控制
184                     popupStatus: {
185                         projectCode: false,
186                         projectName: false
187                     },
188                     // 添加工种和项目阶段的选项数据
189                     typeSelect: [
190                         { value: '软件', text: "机械设计" },
191                         { value: '电气设计', text: "电气设计" },
192                         { value: '软件', text: "软件" },
193                         { value: '软件', text: "视觉" },
194                         { value: '电气调试', text: "电气调试" },
195                         { value: '电工', text: "电工" },
196                         { value: '钳工', text: "钳工" },
197                         { value: '现场经理', text: "现场经理" },
198                     ],
199                     phaseSelect: [
200                         { value: '场内设计', text: "场内设计" },
201                         { value: '场内装配', text: "场内装配" },
202                         { value: '场内调试', text: "场内调试" },
203                         { value: '场外装配', text: "场外装配" },
204                         { value: '场外调试', text: "场外调试" },
205                         { value: '试生产', text: "试生产" },
206                         { value: '陪产', text: "陪产" },
207                         { value: '终验收', text: "终验收" },
208                     ],
209             }
210         },
211         computed: {
212             // 过滤后的项目编码列表
213             filteredProjectCodes() {
214                 if (!this.projectCodeKeyword) return Array.from(this.allProjectCodes);
215                 return Array.from(this.allProjectCodes).filter(code => 
216                     code.toLowerCase().includes(this.projectCodeKeyword.toLowerCase())
217                 );
218             },
219             // 过滤后的项目名称列表
220             filteredProjectNames() {
221                 if (!this.projectNameKeyword) return Array.from(this.allProjectNames);
222                 return Array.from(this.allProjectNames).filter(name => 
223                     name.toLowerCase().includes(this.projectNameKeyword.toLowerCase())
224                 );
225             }
226         },
227         methods: {
228             // 修改显示项目编码选择器的方法
229             showProjectCodeSelector() {
230                 if (this.popupStatus.projectCode) {
231                     // 如果当前是打开状态,则关闭
232                     this.closeProjectCodePopup();
233                 } else {
234                     // 如果当前是关闭状态,则打开(同时关闭另一个)
235                     this.closeProjectNamePopup(); // 关闭项目名称选择器
236                     this.$refs.projectCodePopup.open();
237                     this.popupStatus.projectCode = true;
238                 }
239             },
240
241             // 修改显示项目名称选择器的方法
242             showProjectNameSelector() {
243                 if (this.popupStatus.projectName) {
244                     // 如果当前是打开状态,则关闭
245                     this.closeProjectNamePopup();
246                 } else {
247                     // 如果当前是关闭状态,则打开(同时关闭另一个)
248                     this.closeProjectCodePopup(); // 关闭项目编码选择器
249                     this.$refs.projectNamePopup.open();
250                     this.popupStatus.projectName = true;
251                 }
252             },
253
254             // 修改关闭方法
255             closeProjectCodePopup() {
256                 this.$refs.projectCodePopup.close();
257                 this.projectCodeKeyword = '';
258                 this.popupStatus.projectCode = false;
259             },
260
261             closeProjectNamePopup() {
262                 this.$refs.projectNamePopup.close();
263                 this.projectNameKeyword = '';
264                 this.popupStatus.projectName = false;
265             },
266
267             // 选择项目编码
268             selectProjectCode(code) {
269                 this.searchParams.projectCode = code
270                 this.closeProjectCodePopup()
271                 this.getList()
272             },
273             // 选择项目名称
274             selectProjectName(name) {
275                 this.searchParams.projectName = name
276                 this.closeProjectNamePopup()
277                 this.getList()
278             },
279             // 获取列表数据
280             getList() {
281                 this.loading = true
282                 this.tableData = [] // 清空现有数据
283                 
284                 listWorkHourInfoNoPage(this.searchParams).then(response => {
285                     this.tableData = response.rows || []
286                     this.total = response.total || 0
287                     
288                     // 更新项目编码和名称集合
289                     this.tableData.forEach(item => {
290                         if (item.projectCode) this.allProjectCodes.add(item.projectCode);
291                         if (item.projectName) this.allProjectNames.add(item.projectName);
292                     });
293                     
294                     this.loading = false
295                 }).catch(() => {
296                     this.loading = false
297                     this.$modal.msgError('获取数据失败')
298                 })
299             },
300             // 初始化
301             async init() {
302                 try {
303                     // 先获取所有项目数据用于检索
304                     await this.getAllProjects();
305                     // 然后获取当前页的数据
306                     await this.getList();
307                 } catch (error) {
308                     console.error('初始化失败:', error);
309                 }
310             },
311             // 重置搜索
312             resetSearch() {
313                 this.searchParams.projectCode = ''
314                 this.searchParams.projectName = ''
315                 this.projectCodeKeyword = ''
316                 this.projectNameKeyword = ''
317                 this.getList()
318             },
319             // 获取所有项目数据
320             async getAllProjects() {
321                 try {
322                     this.loading = true;
323                     const response = await listWorkHourInfoNoPage({
324                         pageNum: 1,
325                         pageSize: 999 // 获取足够多的数据以获取所有不重复的项目
326                     });
327                     
328                     if (response && response.rows) {
329                         // 清空现有集合
330                         this.allProjectCodes.clear();
331                         this.allProjectNames.clear();
332                         
333                         // 添加新数据
334                         response.rows.forEach(item => {
335                             if (item.projectCode) this.allProjectCodes.add(item.projectCode);
336                             if (item.projectName) this.allProjectNames.add(item.projectName);
337                         });
338                     }
339                     this.loading = false;
340                 } catch (error) {
341                     console.error('获取项目列表失败:', error);
342                     this.loading = false;
343                     this.$modal.msgError('获取项目列表失败');
344                 }
345             },
346             // 添加弹出层状态监听
347             onPopupChange(type, show) {
348                 this.popupStatus[type] = show;
349             },
350             // 修改卡片点击处理方法
351             handleCardClick(item) {
352                 // 检查是否是当天的记录
353                 const today = new Date().toISOString().split('T')[0]; // 获取当前日期 YYYY-MM-DD
354                 const reportDate = item.reportTime.split(' ')[0]; // 获取报工日期部分 YYYY-MM-DD
355                 
356                 if (reportDate === today) {
357                     // 是当天的记录,允许编辑
358                     const params = {
359                         workHourId: item.workHourId,
360                         projectCode: item.projectCode,
361                         projectName: item.projectName,
362                         projectPhase: item.projectPhase,
363                         workType: item.workType,
364                         workTime: item.workTime,
365                         details: item.details,
366                         isEdit: true
367                     };
368                     
369                     uni.navigateTo({
370                         url: '/pages/dema/workHour/workHour?' + this.parseParams(params)
371                     });
372                 } else {
373                     // 不是当天的记录,显示警告
374                     this.$modal.msgError("只能修改当天的工时记录!");
375                 }
376             },
377             
378             // 添加参数解析方法
379             parseParams(params) {
380                 return Object.keys(params)
381                     .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
382                     .join('&');
383             },
384             // 获取工种文本
385             getWorkTypeText(value) {
386                 const item = this.typeSelect.find(item => item.value === value);
387                 return item ? item.text : value;
388             },
389             // 获取项目阶段文本
390             getPhaseText(value) {
391                 const item = this.phaseSelect.find(item => item.value === value);
392                 return item ? item.text : value;
393             },
394             // 添加日期格式化辅助方法
395             formatDate(date) {
396                 const d = new Date(date);
397                 const year = d.getFullYear();
398                 const month = String(d.getMonth() + 1).padStart(2, '0');
399                 const day = String(d.getDate()).padStart(2, '0');
400                 return `${year}-${month}-${day}`;
401             }
402         },
403         async created() {
404             // 在组件创建时就开始初始化
405             await this.init();
406         },
407         mounted() {
408             // 如果需要其他初始化操作,放在这里
409         }
410     }
ac315c 411 </script>
A 412
413 <style>
2d516c 414     .container {
A 415         display: flex;
416         flex-direction: column;
417         width: 100%;
418     }
ac315c 419
2d516c 420     .search-container {
A 421         position: sticky;
422         top: 0;
423         z-index: 100;
424         background-color: #fff;
425         padding: 12px 0;
426         border-bottom: 1px solid #eee;
427         box-shadow: 0 2px 4px rgba(0,0,0,0.05);
428     }
ac315c 429
2d516c 430     .search-box {
A 431         position: relative;
432         display: flex;
433         justify-content: center;
434         align-items: center;
435         gap: 15px;
436     }
ac315c 437
2d516c 438     .search-item {
A 439         min-width: 120px;
440         padding: 8px 15px;
441         background-color: #f5f7fa;
442         border-radius: 4px;
443         transition: all 0.3s;
444     }
ac315c 445
2d516c 446     .search-item:hover {
A 447         background-color: #e8f4ff;
448     }
ac315c 449
2d516c 450     .search-label {
A 451         color: #333;
452         font-size: 14px;
453     }
454
455     .triangle-down {
456         margin-left: 5px;
457         color: #909399;
458     }
459
460     .reset-btn {
461         position: absolute;
462         right: 20px;
463         padding: 5px;
464         cursor: pointer;
465     }
466
467     .popup-content {
468         background-color: #fff;
469         border-radius: 16px 16px 0 0;
470         padding: 20px;
471     }
472
473     .popup-header {
474         display: flex;
475         justify-content: space-between;
476         align-items: center;
477         padding-bottom: 10px;
478         border-bottom: 1px solid #eee;
479     }
480
481     .popup-title {
482         font-size: 16px;
483         font-weight: bold;
484     }
485
486     .popup-close {
487         font-size: 20px;
488         color: #666;
489         padding: 0 10px;
490         cursor: pointer;
491     }
492
493     .popup-search {
494         padding: 10px 0;
495         border-bottom: 1px solid #ebeef5;
496     }
497
498     .popup-list {
499         margin-top: 10px;
500         max-height: 40vh;
501     }
502
503     .popup-item {
504         padding: 12px 15px;
505         border-bottom: 1px solid #ebeef5;
506         transition: all 0.3s;
507     }
508
509     .popup-item:hover {
510         background-color: #f5f7fa;
511     }
512
513     .popup-item:active {
514         background-color: #e8f4ff;
515     }
516
517     .table-container {
518         flex: 1;
519         padding: 15px;
520         background-color: #f5f7fa;
521     }
522
523     .table-header {
524         display: flex;
525         justify-content: space-between;
526         align-items: center;
527         margin-bottom: 15px;
528         padding: 0 10px;
529     }
530
531     .header-title {
532         font-size: 16px;
533         font-weight: bold;
534         color: #333;
535     }
536
537     .total-count {
538         font-size: 14px;
539         color: #909399;
540     }
541
542     .table-content {
543         display: flex;
544         flex-direction: column;
545         gap: 12px;
546     }
547
548     .card {
549         background-color: #fff;
550         border-radius: 8px;
551         padding: 15px;
552         box-shadow: 0 2px 12px 0 rgba(0,0,0,0.05);
553         transition: all 0.3s;
554     }
555
556     .card:hover {
557         transform: translateY(-2px);
558         box-shadow: 0 4px 12px 0 rgba(0,0,0,0.1);
559     }
560
561     .item-row {
562         display: flex;
563         justify-content: space-between;
564         align-items: center;
565         padding: 8px 0;
566         border-bottom: 1px solid #ebeef5;
567     }
568
569     .item-row:last-child {
570         border-bottom: none;
571     }
572
573     .label {
574         color: #606266;
575         font-size: 14px;
576     }
577
578     .value {
579         color: #333;
580         font-size: 14px;
581     }
582
583     .highlight {
584         color: #409eff;
585         font-weight: bold;
586     }
587
588     .loading-container {
589         padding: 20px 0;
590         display: flex;
591         justify-content: center;
592     }
593
594     .empty-container {
595         padding: 40px 0;
596         display: flex;
597         flex-direction: column;
598         align-items: center;
599         justify-content: center;
600     }
601
602     .empty-image {
603         width: 120px;
604         height: 120px;
605         margin-bottom: 15px;
606     }
607
608     .empty-text {
609         color: #909399;
610         font-size: 14px;
611     }
612
613     .popup-loading {
614         padding: 20px 0;
615         display: flex;
616         justify-content: center;
617         align-items: center;
618     }
ac315c 619 </style>