Ver código fonte

网页端:参培记录新增可自行添加参培记录,不走流程;

移动端:新增工作填报列表
余思翰 1 mês atrás
pai
commit
94cd74cf9d

+ 8
- 0
oa-ui-app/pages.json Ver arquivo

@@ -167,6 +167,14 @@
167 167
 				"navigationBarTitleText" : "仪器设备管理"
168 168
 			}
169 169
 		},
170
+		{
171
+			"path" : "pages/oa/declare/declareList",
172
+			"style" : 
173
+			{
174
+				"navigationBarTitleText" : "工作填报列表",
175
+				"enablePullDownRefresh": true
176
+			}
177
+		},
170 178
 		{
171 179
 			"path" : "pages/components/formInfo",
172 180
 			"style" : 

+ 7
- 2
oa-ui-app/pages/components/formInfo.vue Ver arquivo

@@ -2,22 +2,27 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-06-16 16:26:24
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-06-16 17:09:31
5
+ * @LastEditTime: 2025-08-04 14:47:18
6 6
 -->
7 7
 <template>
8 8
   <view>
9 9
     <borrow :taskForm="taskForm" :taskName="''" v-if="types == 'borrow'"></borrow>
10
+    <declare :taskForm="taskForm" :taskName="''" v-if="types == 'declare'"></declare>
10 11
   </view>
11 12
 </template>
12 13
 
13 14
 <script>
14 15
 import borrow from '@/pages/form/borrow/borrow.vue'
16
+import declare from '@/pages/form/declare/declare.vue'
15 17
 export default {
16
-  components: { borrow },
18
+  components: { borrow, declare },
17 19
   created() {
18 20
     if (this.$route.query.formType == 'borrow') {
19 21
       this.taskForm.formId = this.$route.query.formId
20 22
       this.types = 'borrow'
23
+    }else if (this.$route.query.formType == 'declare') {
24
+      this.taskForm.formId = this.$route.query.formId
25
+      this.types = 'declare'
21 26
     }
22 27
   },
23 28
   data() {

+ 459
- 0
oa-ui-app/pages/oa/declare/declareList.vue Ver arquivo

@@ -0,0 +1,459 @@
1
+<!--
2
+ * @Author: ysh
3
+ * @Date: 2025-08-04 11:04:25
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-08-04 14:50:13
6
+-->
7
+<template>
8
+  <view class="container">
9
+    <mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
10
+      :up="upOption" :fixed="false" :top="10" style="padding: 30rpx;">
11
+      <view>
12
+        <view v-for="(item, index) in declareList" :key="index" class="modern-card">
13
+          <view class="modern-card-header">
14
+            <view class="modern-card-title">
15
+              <text class="project-number">{{ formatterProjectNumber(item) }}</text>
16
+              <text class="project-name">{{ formatterProjectName(item) }}</text>
17
+            </view>
18
+          </view>
19
+
20
+          <view class="modern-card-body">
21
+            <view class="avatar-section">
22
+              <view class="avatar">
23
+                <text>{{ item.user && item.user.nickName ? item.user.nickName[0] : '' }}</text>
24
+              </view>
25
+              <view>
26
+                <text class="applier-name">{{ item.user ? item.user.nickName : '' }}</text>
27
+                <text class="dept-name">填报人</text>
28
+              </view>
29
+            </view>
30
+
31
+            <view class="info-row">
32
+              <text class="info-label">工作类别:</text>
33
+              <text class="info-value">{{ item.workType }}</text>
34
+            </view>
35
+            <view class="info-row">
36
+              <text class="info-label">工作项目:</text>
37
+              <text class="info-value">{{ item.workItem }}</text>
38
+            </view>
39
+            <view class="info-row">
40
+              <text class="info-label">工作内容:</text>
41
+              <text class="info-value">{{ item.workContent }}</text>
42
+            </view>
43
+            <view class="info-row">
44
+              <text class="info-label">工作时间:</text>
45
+              <text class="info-value">{{ item.beginDate }} ~ {{ item.endDate }}</text>
46
+            </view>
47
+            <view class="info-row">
48
+              <text class="info-label">工天数量:</text>
49
+              <text class="info-value">{{ item.workLoad }}天</text>
50
+            </view>
51
+            <view class="info-row">
52
+              <text class="info-label">工天单价:</text>
53
+              <text>{{ item.price }}</text>
54
+            </view>
55
+            <view class="info-row">
56
+              <text class="info-label">系数:</text>
57
+              <text class="info-value">{{ item.coefficient }}</text>
58
+            </view>
59
+            <view class="info-row">
60
+              <text class="info-label">预估绩效:</text>
61
+              <text class="amount-highlight">¥{{ predictMoney(item) }}</text>
62
+            </view>
63
+            <view class="info-row">
64
+              <text class="info-label">填报时间:</text>
65
+              <text class="info-value">{{ parseTime(item.submitTime, '{y}-{m}-{d}') }}</text>
66
+            </view>
67
+
68
+            <!-- 审核状态 -->
69
+            <view class="status-section">
70
+              <view class="status-item">
71
+                <text class="status-label">一审:</text>
72
+                <view class="status-tag" :class="formatFn(item, 'checkStatus') ? 'success' : 'warning'">
73
+                  <text>{{ formatFn(item, 'checkStatus') ? '已通过' : '待审核' }}</text>
74
+                </view>
75
+              </view>
76
+              <view class="status-item">
77
+                <text class="status-label">二审:</text>
78
+                <view class="status-tag" :class="formatFn(item, 'auditStatus') ? 'success' : 'warning'">
79
+                  <text>{{ formatFn(item, 'auditStatus') ? '已通过' : '待审核' }}</text>
80
+                </view>
81
+              </view>
82
+              <view class="status-item">
83
+                <text class="status-label">确认:</text>
84
+                <view class="status-tag" :class="formatFn(item, 'confirmStatus') ? 'success' : 'warning'">
85
+                  <text>{{ formatFn(item, 'confirmStatus') ? '已确认' : '待确认' }}</text>
86
+                </view>
87
+              </view>
88
+            </view>
89
+          </view>
90
+
91
+          <view class="modern-card-footer">
92
+            <button class="footer-btn detail-btn" @click="openFormInfo(item)">表单信息</button>
93
+            <button class="footer-btn form-btn" @click="handleUpdate(item)">修改</button>
94
+          </view>
95
+        </view>
96
+      </view>
97
+    </mescroll-uni>
98
+  </view>
99
+</template>
100
+
101
+<script>
102
+import { listDeclare, getDeclare, updateDeclare } from '@/api/oa/declare/declare';
103
+import MescrollMixin from '@/uni_modules/mescroll/components/mescroll-uni/mescroll-mixins.js'
104
+
105
+export default {
106
+  mixins: [MescrollMixin],
107
+  data() {
108
+    return {
109
+      mescroll: null, // mescroll实例
110
+      downOption: {
111
+        auto: true,
112
+        textOutOffset: '下拉刷新',
113
+        textLoading: '加载中...'
114
+      },
115
+      upOption: {
116
+        auto: false,
117
+        page: { num: 1, size: 10 },
118
+        noMoreSize: 5,
119
+        empty: { tip: '暂无工作填报数据' },
120
+        textNoMore: '~ 没有更多数据了 ~'
121
+      },
122
+      declareList: [],
123
+      queryParams: {
124
+        pageNum: 1,
125
+        pageSize: 10,
126
+        projectId: null,
127
+        userId: null,
128
+        submitTime: null
129
+      }
130
+    }
131
+  },
132
+
133
+  onLoad: function (options) {
134
+    uni.startPullDownRefresh();
135
+  },
136
+
137
+  methods: {
138
+    // 加载数据
139
+    async loadData() {
140
+      try {
141
+        // 权限控制逻辑
142
+        if (this.$store.getters.deptId > 103) {
143
+          this.queryParams.deptId = this.$store.getters.deptId;
144
+        }
145
+        if (this.$store.getters.roles.includes('projectLeader')) {
146
+          this.queryParams.projectLeader = this.$store.getters.userId
147
+        }
148
+        if (this.$store.getters.roles.includes('dept')) {
149
+          this.queryParams.projectLeader = null
150
+        }
151
+        if (this.$store.getters.userId == 1) {
152
+          this.queryParams.deptId = null
153
+          this.queryParams.projectLeader = null
154
+        }
155
+
156
+        const res = await listDeclare(this.queryParams)
157
+        return { data: res.rows, total: res.total }
158
+      } catch (e) {
159
+        return { data: [], error: true }
160
+      }
161
+    },
162
+
163
+    mescrollInit(mescroll) {
164
+      this.mescroll = mescroll;
165
+    },
166
+
167
+    // mescroll下拉刷新回调
168
+    async downCallback() {
169
+      this.declareList = []; // 清空列表
170
+      this.queryParams.pageNum = 1
171
+      const res = await this.loadData()
172
+      if (res.error) {
173
+        this.mescroll.endErr();
174
+      } else {
175
+        this.declareList = res.data;
176
+        this.mescroll.endSuccess(res.data.length, res.total);
177
+      }
178
+      uni.stopPullDownRefresh();
179
+    },
180
+
181
+    // mescroll上拉加载回调
182
+    async upCallback(page) {
183
+      this.queryParams.pageNum = page.num
184
+      const res = await this.loadData()
185
+      if (res.error) {
186
+        this.mescroll.endErr();
187
+      } else {
188
+        this.declareList = this.declareList.concat(res.data)
189
+        this.mescroll.endSuccess(res.data.length, res.total);
190
+      }
191
+    },
192
+
193
+    // 格式化项目编号
194
+    formatterProjectNumber(row) {
195
+      if (!row.projectId) {
196
+        return '无'
197
+      } else {
198
+        return row.project.projectNumber
199
+      }
200
+    },
201
+
202
+    // 格式化项目名称
203
+    formatterProjectName(row) {
204
+      if (!row.projectId) {
205
+        let year = row.submitTime.substring(0, 4)
206
+        return year + '年借调、零星项目'
207
+      } else {
208
+        return row.project.projectName
209
+      }
210
+    },
211
+
212
+    // 格式化状态
213
+    formatFn(row, type) {
214
+      if (row[type] && row[type] == '1') {
215
+        return true
216
+      } else {
217
+        return false
218
+      }
219
+    },
220
+
221
+    // 计算预估绩效
222
+    predictMoney(row) {
223
+      let result = parseFloat(row.price * row.workLoad * row.coefficient)
224
+      return result.toFixed(2)
225
+    },
226
+
227
+    // 时间解析函数
228
+    parseTime(time, cFormat) {
229
+      if (!time) return ''
230
+
231
+      const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
232
+      let date
233
+      if (typeof time === 'object') {
234
+        date = time
235
+      } else {
236
+        if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
237
+          time = parseInt(time)
238
+        }
239
+        date = new Date(time)
240
+      }
241
+      const formatObj = {
242
+        y: date.getFullYear(),
243
+        m: date.getMonth() + 1,
244
+        d: date.getDate(),
245
+        h: date.getHours(),
246
+        i: date.getMinutes(),
247
+        s: date.getSeconds(),
248
+        a: date.getDay()
249
+      }
250
+      const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
251
+        const value = formatObj[key]
252
+        if (key === 'a') {
253
+          return ['日', '一', '二', '三', '四', '五', '六'][value]
254
+        }
255
+        return value.toString().padStart(2, '0')
256
+      })
257
+      return time_str
258
+    },
259
+
260
+    // 打开表单信息
261
+    openFormInfo(item) {
262
+      console.log(item)
263
+      uni.navigateTo({
264
+        url: `/pages/components/formInfo?formId=${item.formId}&formType=declare`
265
+      });
266
+    },
267
+
268
+    // 修改操作
269
+    handleUpdate(item) {
270
+      
271
+    }
272
+  }
273
+}
274
+</script>
275
+
276
+<style lang="scss" scoped>
277
+.container {
278
+  padding: 20rpx;
279
+  background-color: #f5f5f5;
280
+  height: 100vh;
281
+  display: flex;
282
+  flex-direction: column;
283
+}
284
+
285
+.modern-card {
286
+  background: #fff;
287
+  border-radius: 18rpx;
288
+  margin-bottom: 32rpx;
289
+  box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.08);
290
+  overflow: hidden;
291
+  transition: box-shadow 0.2s;
292
+  border: 1rpx solid #f0f0f0;
293
+}
294
+
295
+.modern-card-header {
296
+  display: flex;
297
+  justify-content: space-between;
298
+  align-items: center;
299
+  background: linear-gradient(90deg, #e0e7ff 0%, #f8fafc 100%);
300
+  padding: 24rpx 28rpx 16rpx 28rpx;
301
+}
302
+
303
+.modern-card-title {
304
+  display: flex;
305
+  flex-direction: column;
306
+}
307
+
308
+.project-number {
309
+  font-size: 26rpx;
310
+  color: #6366f1;
311
+  font-weight: 600;
312
+}
313
+
314
+.project-name {
315
+  font-size: 28rpx;
316
+  color: #22223b;
317
+  font-weight: bold;
318
+  margin-top: 4rpx;
319
+}
320
+
321
+.amount-highlight {
322
+  background: #fef3c7;
323
+  color: #d97706;
324
+  font-size: 28rpx;
325
+  font-weight: bold;
326
+  border-radius: 10rpx;
327
+  padding: 8rpx 20rpx;
328
+  min-width: 120rpx;
329
+  text-align: center;
330
+}
331
+
332
+.amount-highlight-detail {
333
+  background: #fef3c7;
334
+  color: #d97706;
335
+  font-size: 28rpx;
336
+  font-weight: bold;
337
+  border-radius: 10rpx;
338
+  padding: 4rpx 18rpx;
339
+  margin-left: 8rpx;
340
+}
341
+
342
+.modern-card-body {
343
+  padding: 20rpx 28rpx 24rpx 28rpx;
344
+}
345
+
346
+.avatar-section {
347
+  display: flex;
348
+  align-items: center;
349
+  margin-bottom: 18rpx;
350
+}
351
+
352
+.avatar {
353
+  width: 56rpx;
354
+  height: 56rpx;
355
+  border-radius: 50%;
356
+  background: #a5b4fc;
357
+  color: #fff;
358
+  display: flex;
359
+  align-items: center;
360
+  justify-content: center;
361
+  font-size: 28rpx;
362
+  font-weight: bold;
363
+  margin-right: 18rpx;
364
+}
365
+
366
+.applier-name {
367
+  font-size: 28rpx;
368
+  color: #22223b;
369
+  font-weight: 500;
370
+}
371
+
372
+.dept-name {
373
+  font-size: 24rpx;
374
+  color: #64748b;
375
+  margin-left: 8rpx;
376
+}
377
+
378
+.info-row {
379
+  display: flex;
380
+  margin-bottom: 10rpx;
381
+}
382
+
383
+.info-label {
384
+  color: #64748b;
385
+  font-size: 24rpx;
386
+  min-width: 120rpx;
387
+}
388
+
389
+.info-value {
390
+  color: #22223b;
391
+  font-size: 24rpx;
392
+  flex: 1;
393
+  word-break: break-all;
394
+}
395
+
396
+// 审核状态样式
397
+.status-section {
398
+  margin-top: 16rpx;
399
+  padding-top: 16rpx;
400
+  border-top: 1px solid #f0f0f0;
401
+}
402
+
403
+.status-item {
404
+  display: flex;
405
+  align-items: center;
406
+  margin-bottom: 8rpx;
407
+}
408
+
409
+.status-label {
410
+  color: #64748b;
411
+  font-size: 24rpx;
412
+  min-width: 80rpx;
413
+}
414
+
415
+.status-tag {
416
+  padding: 4rpx 12rpx;
417
+  border-radius: 6rpx;
418
+  font-size: 22rpx;
419
+  margin-left: 8rpx;
420
+}
421
+
422
+.status-tag.success {
423
+  background: #dcfce7;
424
+  color: #16a34a;
425
+}
426
+
427
+.status-tag.warning {
428
+  background: #fef3c7;
429
+  color: #d97706;
430
+}
431
+
432
+.modern-card-footer {
433
+  display: flex;
434
+  justify-content: flex-end;
435
+  gap: 20rpx;
436
+  padding: 16rpx 28rpx 20rpx 28rpx;
437
+  border-top: 1px solid #f0f0f0;
438
+  background: #fafbfc;
439
+}
440
+
441
+.footer-btn {
442
+  font-size: 26rpx;
443
+  border-radius: 8rpx;
444
+  border: none;
445
+  outline: none;
446
+  background: #e0e7ff;
447
+  color: #3b3b4f;
448
+}
449
+
450
+.detail-btn {
451
+  background: #6366f1;
452
+  color: #fff;
453
+}
454
+
455
+.form-btn {
456
+  background: #f4c542;
457
+  color: #fff;
458
+}
459
+</style>

+ 104
- 40
oa-ui-app/pages/oa/device/instrumentsList.vue Ver arquivo

@@ -2,7 +2,7 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-07-08 14:00:47
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-07-16 15:22:13
5
+ * @LastEditTime: 2025-08-04 10:15:13
6 6
 -->
7 7
 <template>
8 8
   <view class="device-container">
@@ -27,17 +27,32 @@
27 27
           </view>
28 28
           <view class="filter-item">
29 29
             <text class="filter-label">设备品牌</text>
30
-            <input v-model="queryParams.brand" placeholder="品牌筛选" @confirm="handleQuery" />
30
+            <uni-easyinput v-model="queryParams.brand" placeholder="品牌筛选" @confirm="handleQuery" @blur="handleQuery"
31
+              :inputBorder="false" :styles="{
32
+                backgroundColor: '#fff',
33
+                borderRadius: '8px',
34
+                border: '1px solid #e0e0e0'
35
+              }" />
31 36
           </view>
32 37
         </view>
33 38
         <view class="filter-row">
34 39
           <view class="filter-item">
35 40
             <text class="filter-label">出厂编号</text>
36
-            <input v-model="queryParams.code" placeholder="编号筛选" @confirm="handleQuery" />
41
+            <uni-easyinput v-model="queryParams.code" placeholder="编号筛选" @confirm="handleQuery" @blur="handleQuery"
42
+              :inputBorder="false" :styles="{
43
+                backgroundColor: '#fff',
44
+                borderRadius: '8px',
45
+                border: '1px solid #e0e0e0'
46
+              }" />
37 47
           </view>
38 48
           <view class="filter-item">
39 49
             <text class="filter-label">规格型号</text>
40
-            <input v-model="queryParams.series" placeholder="型号筛选" @confirm="handleQuery" />
50
+            <uni-easyinput v-model="queryParams.series" placeholder="型号筛选" @confirm="handleQuery" @blur="handleQuery"
51
+              :inputBorder="false" :styles="{
52
+                backgroundColor: '#fff',
53
+                borderRadius: '8px',
54
+                border: '1px solid #e0e0e0'
55
+              }" />
41 56
           </view>
42 57
         </view>
43 58
       </view>
@@ -210,11 +225,11 @@
210 225
         <scroll-view scroll-y="true" class="popup-body">
211 226
           <view class="form-item">
212 227
             <text class="label">设备品牌 *</text>
213
-            <input v-model="form.brand" placeholder="请输入设备品牌" />
228
+            <uni-easyinput v-model="form.brand" placeholder="请输入设备品牌" />
214 229
           </view>
215 230
           <view class="form-item">
216 231
             <text class="label">设备名称 *</text>
217
-            <input v-model="form.name" placeholder="请输入设备名称" />
232
+            <uni-easyinput v-model="form.name" placeholder="请输入设备名称" />
218 233
           </view>
219 234
           <view class="form-item">
220 235
             <text class="label">设备类别</text>
@@ -222,7 +237,7 @@
222 237
           </view>
223 238
           <view class="form-item">
224 239
             <text class="label">规格型号</text>
225
-            <input v-model="form.series" placeholder="请输入规格型号" />
240
+            <uni-easyinput v-model="form.series" placeholder="请输入规格型号" />
226 241
           </view>
227 242
           <view class="form-item">
228 243
             <text class="label">购置时间</text>
@@ -233,23 +248,23 @@
233 248
           </view>
234 249
           <view class="form-item">
235 250
             <text class="label">存放地点</text>
236
-            <input v-model="form.place" placeholder="请输入存放地点" />
251
+            <uni-easyinput v-model="form.place" placeholder="请输入存放地点" />
237 252
           </view>
238 253
           <view class="form-item">
239 254
             <text class="label">购买价格(元)</text>
240
-            <input v-model="form.cost" type="number" placeholder="请输入购买价格" />
255
+            <uni-easyinput v-model="form.cost" type="number" placeholder="请输入购买价格" />
241 256
           </view>
242 257
           <view class="form-item">
243 258
             <text class="label">预计使用年限(年)</text>
244
-            <input v-model="form.expectLife" type="number" placeholder="请输入使用年限" />
259
+            <uni-easyinput v-model="form.expectLife" type="number" placeholder="请输入使用年限" />
245 260
           </view>
246 261
           <view class="form-item">
247 262
             <text class="label">出厂编号</text>
248
-            <textarea v-model="form.code" placeholder="请输入出厂编号" />
263
+            <uni-easyinput v-model="form.code" type="textarea" placeholder="请输入出厂编号" :autoHeight="true" />
249 264
           </view>
250 265
           <view class="form-item">
251 266
             <text class="label">单日成本(元)</text>
252
-            <input v-model="form.dayCost" type="number" placeholder="请输入单日成本" />
267
+            <uni-easyinput v-model="form.dayCost" type="number" placeholder="请输入单日成本" />
253 268
           </view>
254 269
           <view class="form-item">
255 270
             <text class="label">管理部门</text>
@@ -261,11 +276,11 @@
261 276
           </view>
262 277
           <view class="form-item">
263 278
             <text class="label">设备编号</text>
264
-            <input v-model="form.deviceNumber" placeholder="请输入设备编号" />
279
+            <uni-easyinput v-model="form.deviceNumber" placeholder="请输入设备编号" />
265 280
           </view>
266 281
           <view class="form-item">
267 282
             <text class="label">备注</text>
268
-            <textarea v-model="form.remark" placeholder="请输入备注信息" />
283
+            <uni-easyinput v-model="form.remark" type="textarea" placeholder="请输入备注信息" :autoHeight="true" />
269 284
           </view>
270 285
         </scroll-view>
271 286
         <view class="popup-footer">
@@ -427,7 +442,7 @@ export default {
427 442
 
428 443
     handleViewLogs() {
429 444
       this.closeDetailPopup();
430
-      
445
+
431 446
     },
432 447
 
433 448
     handleAdd() {
@@ -634,18 +649,41 @@ export default {
634 649
   flex: 1;
635 650
 
636 651
   .filter-label {
637
-    font-size: 12px;
652
+    font-size: 13px;
638 653
     color: #666;
639
-    margin-bottom: 5px;
654
+    margin-bottom: 8px;
640 655
     display: block;
656
+    font-weight: 500;
641 657
   }
642 658
 
643
-  input {
644
-    width: 100%;
645
-    padding: 6px 10px;
646
-    border: 1px solid #ddd;
647
-    border-radius: 4px;
648
-    font-size: 12px;
659
+  // uni-easyinput 样式
660
+  :deep(.uni-easyinput) {
661
+    height: 40px !important;
662
+    border-radius: 8px !important;
663
+
664
+    .uni-easyinput__content {
665
+      height: 100% !important;
666
+      border-radius: 8px !important;
667
+      border: 1px solid #e0e0e0 !important;
668
+      background: #fff !important;
669
+      transition: all 0.3s ease;
670
+
671
+      &:focus-within {
672
+        border-color: #6366f1 !important;
673
+        box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.1) !important;
674
+      }
675
+    }
676
+
677
+    .uni-easyinput__content-input {
678
+      padding: 0 12px !important;
679
+      font-size: 14px !important;
680
+      color: #333 !important;
681
+
682
+      &::placeholder {
683
+        color: #a0a0a0 !important;
684
+        font-size: 13px !important;
685
+      }
686
+    }
649 687
   }
650 688
 }
651 689
 
@@ -879,24 +917,50 @@ export default {
879 917
     display: block;
880 918
   }
881 919
 
882
-  input,
883
-  textarea {
884
-    width: 100%;
885
-    padding: 12px;
886
-    border: 1px solid #ddd;
887
-    border-radius: 8px;
888
-    font-size: 14px;
889
-    background: #f8f8f8;
890
-
891
-    &:focus {
892
-      border-color: #667eea;
893
-      background: #fff;
920
+  // uni-easyinput 样式
921
+  :deep(.uni-easyinput) {
922
+    .uni-easyinput__content {
923
+      height: 45px !important;
924
+      border: 1px solid #ddd !important;
925
+      border-radius: 8px !important;
926
+      background: #f8f8f8 !important;
927
+      transition: all 0.3s ease;
928
+      
929
+      &:focus-within {
930
+        border-color: #667eea !important;
931
+        background: #fff !important;
932
+        box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1) !important;
933
+      }
934
+    }
935
+    
936
+    .uni-easyinput__content-input {
937
+      padding: 0 12px !important;
938
+      font-size: 14px !important;
939
+      color: #333 !important;
940
+      
941
+      &::placeholder {
942
+        color: #999 !important;
943
+      }
944
+    }
945
+    
946
+    // textarea 样式
947
+    &.uni-easyinput--textarea {
948
+      .uni-easyinput__content {
949
+        height: auto !important;
950
+        min-height: 80px !important;
951
+      }
952
+      
953
+      .uni-easyinput__content-textarea {
954
+        padding: 12px !important;
955
+        font-size: 14px !important;
956
+        color: #333 !important;
957
+        min-height: 60px !important;
958
+        
959
+        &::placeholder {
960
+          color: #999 !important;
961
+        }
962
+      }
894 963
     }
895
-  }
896
-
897
-  textarea {
898
-    min-height: 80px;
899
-    resize: none;
900 964
   }
901 965
 
902 966
   .date-picker {

+ 11
- 1
oa-ui-app/pages/work/index.vue Ver arquivo

@@ -2,7 +2,7 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-01-16 11:17:08
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-07-08 14:42:51
5
+ * @LastEditTime: 2025-08-04 11:12:27
6 6
 -->
7 7
 <template>
8 8
   <view class="work-container">
@@ -90,6 +90,12 @@ export default {
90 90
           icon: '/static/images/work/workDay.png',
91 91
           url: 'deviceLog',
92 92
           hasPermi: checkPermi(['oa:deviceLog:list'])
93
+        },
94
+        {
95
+          name: '工作填报',
96
+          icon: '/static/images/work/declare.png',
97
+          url: 'declare',
98
+          hasPermi: checkPermi(['oa:declare:list'])
93 99
         }
94 100
       ],
95 101
       flowList: ['借款审批', '用车审批', '设备审批', '工作填报'],
@@ -142,6 +148,10 @@ export default {
142 148
         uni.navigateTo({
143 149
           url: '/pages/oa/device/instrumentsList'
144 150
         })
151
+      } else if (type == 'declare') {
152
+        uni.navigateTo({
153
+          url: '/pages/oa/declare/declareList'
154
+        })
145 155
       }
146 156
       else {
147 157
         this.$modal.showToast('模块建设中~')

BIN
oa-ui-app/static/images/work/declare.png Ver arquivo


+ 209
- 30
oa-ui/src/views/oa/study/approval.vue Ver arquivo

@@ -1,7 +1,7 @@
1 1
 <template>
2 2
   <div class="app-container">
3 3
     <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
4
-      <el-form-item label="上报人" prop="userId">
4
+      <el-form-item label="参会人" prop="userId">
5 5
         <el-select v-model="queryParams.userId" filterable clearable @change="handleQuery">
6 6
           <el-option v-for="item in $store.state.user.userList" :key="item.userId" :label="item.nickName"
7 7
             :value="item.userId">
@@ -15,9 +15,9 @@
15 15
           </el-option>
16 16
         </el-select>
17 17
       </el-form-item>
18
-      <el-form-item label="上报日期" prop="reportTime">
18
+      <el-form-item label="参会日期" prop="reportTime">
19 19
         <el-date-picker clearable v-model="queryParams.reportTime" type="date" value-format="yyyy-MM-dd"
20
-          placeholder="请选择上报日期">
20
+          placeholder="请选择参会日期">
21 21
         </el-date-picker>
22 22
       </el-form-item>
23 23
       <el-form-item>
@@ -27,9 +27,13 @@
27 27
     </el-form>
28 28
 
29 29
     <el-row :gutter="10" class="mb8">
30
+      <el-col :span="1.5">
31
+        <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
32
+          v-hasPermi="['oa:trainApproval:add']">新增</el-button>
33
+      </el-col>
30 34
       <el-col :span="1.5">
31 35
         <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
32
-          v-hasPermi="['oa:study:export']">导出</el-button>
36
+          v-hasPermi="['oa:trainApproval:export']">导出</el-button>
33 37
       </el-col>
34 38
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
35 39
     </el-row>
@@ -37,9 +41,9 @@
37 41
     <el-table v-loading="loading" :data="trainApprovalList" @selection-change="handleSelectionChange">
38 42
       <el-table-column type="selection" width="55" align="center" />
39 43
       <!-- <el-table-column label="参培id" align="center" prop="participateId" /> -->
40
-      <el-table-column label="上报人" align="center" prop="reportUser.nickName" />
44
+      <el-table-column label="参会人" align="center" prop="reportUser.nickName" />
41 45
       <el-table-column label="部门" align="center" prop="dept.deptName" />
42
-      <el-table-column label="上报日期" align="center" prop="reportTime" width="180">
46
+      <el-table-column label="参会日期" align="center" prop="reportTime" width="180">
43 47
         <template slot-scope="scope">
44 48
           <span>{{ parseTime(scope.row.reportTime, '{y}-{m}-{d}') }}</span>
45 49
         </template>
@@ -47,19 +51,21 @@
47 51
       <el-table-column label="培训名称" align="center" prop="trainName" />
48 52
       <el-table-column label="主要内容" align="center" prop="content" />
49 53
       <el-table-column label="学时" align="center" prop="hours" />
50
-      <el-table-column label="部门审核人" align="center" prop="deptUser.nickName" />
51
-      <el-table-column label="部门审核时间" align="center" prop="deptTime" width="180">
54
+      <el-table-column label="审核人" align="center" prop="deptUser.nickName" />
55
+      <el-table-column label="审核时间" align="center" prop="deptTime" width="180">
52 56
         <template slot-scope="scope">
53 57
           <span>{{ parseTime(scope.row.deptTime, '{y}-{m}-{d}') }}</span>
54 58
         </template>
55 59
       </el-table-column>
56
-      <el-table-column label="部门审核意见" align="center" prop="deptComment" />
60
+      <el-table-column label="审核意见" align="center" prop="deptComment" />
57 61
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
58 62
         <template slot-scope="scope">
59 63
           <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
60
-            v-hasPermi="['oa:study:edit']">修改</el-button>
64
+            v-hasPermi="['oa:trainApproval:edit']"
65
+            :disabled="scope.row.deptUserId !== $store.state.user.id">修改</el-button>
61 66
           <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
62
-            v-hasPermi="['oa:study:remove']">删除</el-button>
67
+            v-hasPermi="['oa:trainApproval:remove']"
68
+            :disabled="scope.row.deptUserId !== $store.state.user.id">删除</el-button>
63 69
         </template>
64 70
       </el-table-column>
65 71
     </el-table>
@@ -70,33 +76,32 @@
70 76
     <!-- 添加或修改参培审核对话框 -->
71 77
     <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
72 78
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
73
-        <el-form-item label="上报人" prop="userId">
74
-          <el-input v-model="form.userId" placeholder="请输入上报人" />
79
+        <el-form-item label="参会人" prop="userId">
80
+          {{ getUserName(form.userId) }}
75 81
         </el-form-item>
76
-        <el-form-item label="上报日期" prop="reportTime">
82
+        <el-form-item label="参会日期" prop="reportTime">
77 83
           <el-date-picker clearable v-model="form.reportTime" type="date" value-format="yyyy-MM-dd"
78
-            placeholder="请选择上报日期">
84
+            placeholder="请选择参会日期">
79 85
           </el-date-picker>
80 86
         </el-form-item>
81 87
         <el-form-item label="培训名称" prop="trainName">
82 88
           <el-input v-model="form.trainName" placeholder="请输入培训名称" />
83 89
         </el-form-item>
84
-        <el-form-item label="主要内容">
85
-          <editor v-model="form.content" :min-height="192" />
90
+        <el-form-item label="主要内容" prop="content">
91
+          <el-input type="textarea" v-model="form.content" placeholder="请输入主要内容" />
86 92
         </el-form-item>
87
-        <el-form-item label="时" prop="hours">
88
-          <el-input v-model="form.hours" placeholder="请输入时" />
93
+        <el-form-item label="时" prop="hours">
94
+          <el-input-number v-model="form.hours" :min="0" :max="100" :precision="2" :step="0.1" placeholder="请输入时" />
89 95
         </el-form-item>
90
-        <el-form-item label="部门审核人" prop="deptUserId">
91
-          <el-input v-model="form.deptUserId" placeholder="请输入部门审核人" />
96
+        <el-form-item label="审核人" prop="deptUserId">
97
+          {{ getUserName(form.deptUserId) }}
92 98
         </el-form-item>
93
-        <el-form-item label="部门审核时间" prop="deptTime">
94
-          <el-date-picker clearable v-model="form.deptTime" type="date" value-format="yyyy-MM-dd"
95
-            placeholder="请选择部门审核时间">
99
+        <el-form-item label="审核时间" prop="deptTime">
100
+          <el-date-picker clearable v-model="form.deptTime" type="date" value-format="yyyy-MM-dd" placeholder="请选择审核时间">
96 101
           </el-date-picker>
97 102
         </el-form-item>
98
-        <el-form-item label="部门审核意见" prop="deptComment">
99
-          <el-input v-model="form.deptComment" placeholder="请输入部门审核意见" />
103
+        <el-form-item label="审核意见" prop="deptComment">
104
+          <el-input type="textarea" v-model="form.deptComment" placeholder="请输入审核意见" />
100 105
         </el-form-item>
101 106
       </el-form>
102 107
       <div slot="footer" class="dialog-footer">
@@ -104,12 +109,56 @@
104 109
         <el-button @click="cancel">取 消</el-button>
105 110
       </div>
106 111
     </el-dialog>
112
+
113
+    <el-dialog :title="title" :visible.sync="addOpen" width="500px" append-to-body>
114
+      <el-form ref="addForm" :model="addForm" :rules="addRules" label-width="80px">
115
+        <el-form-item label="培训名称" prop="trainName">
116
+          <el-input v-model="addForm.trainName" placeholder="请输入培训名称" />
117
+        </el-form-item>
118
+        <el-form-item label="主要内容" prop="content">
119
+          <el-input type="textarea" v-model="addForm.content" placeholder="请输入主要内容" />
120
+        </el-form-item>
121
+        <el-form-item label="参会人" prop="userIdList">
122
+          <el-select v-model="addForm.userIdList" multiple filterable clearable placeholder="请选择参会人"
123
+            style="width: 100%;">
124
+            <el-option v-for="item in $store.state.user.userList" :key="item.userId" :label="item.nickName"
125
+              :value="item.userId">
126
+            </el-option>
127
+          </el-select>
128
+        </el-form-item>
129
+        <el-form-item label="参会日期" prop="reportTime">
130
+          <el-date-picker clearable v-model="addForm.reportTime" type="date" value-format="yyyy-MM-dd"
131
+            placeholder="请选择参会日期">
132
+          </el-date-picker>
133
+        </el-form-item>
134
+        <el-form-item label="学时" prop="hours">
135
+          <el-input-number v-model="addForm.hours" :min="0" :max="100" :precision="2" :step="0.1" placeholder="请输入学时" />
136
+        </el-form-item>
137
+        <el-form-item label="审核人" prop="deptUserId">
138
+          {{ addForm.deptUserName }}
139
+        </el-form-item>
140
+        <el-form-item label="审核时间" prop="deptTime">
141
+          <el-date-picker clearable v-model="addForm.deptTime" type="date" value-format="yyyy-MM-dd"
142
+            placeholder="请选择审核时间">
143
+          </el-date-picker>
144
+        </el-form-item>
145
+        <el-form-item label="审核意见" prop="deptComment">
146
+          <el-input type="textarea" v-model="addForm.deptComment" placeholder="请输入审核意见" />
147
+        </el-form-item>
148
+      </el-form>
149
+      <div slot="footer" class="dialog-footer">
150
+        <el-button type="primary" @click="submitAddForm">确 定</el-button>
151
+        <el-button @click="cancelAdd">取 消</el-button>
152
+      </div>
153
+    </el-dialog>
107 154
   </div>
108 155
 </template>
109 156
 
110 157
 <script>
111 158
 import { listTrainApproval, getTrainApproval, delTrainApproval, addTrainApproval, updateTrainApproval } from "@/api/oa/study/trainApproval";
112 159
 
160
+import { Snowflake } from '@/utils/snowFlake.js'
161
+
113 162
 export default {
114 163
   name: "TrainApproval",
115 164
   data() {
@@ -149,7 +198,63 @@ export default {
149 198
       form: {},
150 199
       // 表单校验
151 200
       rules: {
152
-      }
201
+        reportTime: [
202
+          { required: true, message: "参会日期不能为空", trigger: "change" }
203
+        ],
204
+        trainName: [
205
+          { required: true, message: "培训名称不能为空", trigger: "blur" }
206
+        ],
207
+        content: [
208
+          { required: true, message: "主要内容不能为空", trigger: "blur" }
209
+        ],
210
+        hours: [
211
+          { required: true, message: "学时不能为空", trigger: "blur" },
212
+          { type: 'number', min: 0.1, message: "学时必须大于0", trigger: "blur" }
213
+        ],
214
+        deptTime: [
215
+          { required: true, message: "审核时间不能为空", trigger: "change" }
216
+        ],
217
+        deptComment: [
218
+          { required: true, message: "审核意见不能为空", trigger: "blur" }
219
+        ]
220
+      },
221
+      // 新增表单验证规则
222
+      addRules: {
223
+        userIdList: [
224
+          { required: true, type: 'array', min: 1, message: "请至少选择一位参会人", trigger: "change" }
225
+        ],
226
+        reportTime: [
227
+          { required: true, message: "参会日期不能为空", trigger: "change" }
228
+        ],
229
+        trainName: [
230
+          { required: true, message: "培训名称不能为空", trigger: "blur" }
231
+        ],
232
+        content: [
233
+          { required: true, message: "主要内容不能为空", trigger: "blur" }
234
+        ],
235
+        hours: [
236
+          { required: true, message: "学时不能为空", trigger: "blur" },
237
+          { type: 'number', min: 0.1, message: "学时必须大于0", trigger: "blur" }
238
+        ],
239
+        deptTime: [
240
+          { required: true, message: "审核时间不能为空", trigger: "change" }
241
+        ],
242
+        deptComment: [
243
+          { required: true, message: "审核意见不能为空", trigger: "blur" }
244
+        ]
245
+      },
246
+      addOpen: false,
247
+      addForm: {
248
+        userIdList: [],
249
+        reportTime: null,
250
+        trainName: null,
251
+        content: null,
252
+        hours: 0,
253
+        deptUserId: null,
254
+        deptUserName: null,
255
+        deptTime: null,
256
+        deptComment: null
257
+      },
153 258
     };
154 259
   },
155 260
   created() {
@@ -203,9 +308,12 @@ export default {
203 308
     },
204 309
     /** 新增按钮操作 */
205 310
     handleAdd() {
206
-      this.reset();
207
-      this.open = true;
208
-      this.title = "添加参培审核";
311
+      this.resetAddForm("addForm");
312
+      // 设置当前登录用户为审核人
313
+      this.addForm.deptUserId = this.$store.state.user.id;
314
+      this.addForm.deptUserName = this.$store.state.user.name;
315
+      this.addOpen = true;
316
+      this.title = "添加参培记录";
209 317
     },
210 318
     /** 修改按钮操作 */
211 319
     handleUpdate(row) {
@@ -252,6 +360,77 @@ export default {
252 360
       this.download('oa/trainApproval/export', {
253 361
         ...this.queryParams
254 362
       }, `trainApproval_${new Date().getTime()}.xlsx`)
363
+    },
364
+    submitAddForm() {
365
+      this.$refs["addForm"].validate(valid => {
366
+        if (valid) {
367
+          // 检查是否选择了参会人员
368
+          if (!this.addForm.userIdList || this.addForm.userIdList.length === 0) {
369
+            this.$modal.msgError("请选择至少一位参会人员");
370
+            return;
371
+          }
372
+          // 获取当前登录用户ID作为审核人
373
+          const currentUserId = this.$store.state.user.id;
374
+          const userList = this.$store.state.user.userList;
375
+          // 为每个参会人员创建一条记录
376
+          const promises = this.addForm.userIdList.map((userId, index) => {
377
+            // 为每个记录使用不同的workerId确保ID唯一性
378
+            const workerId = BigInt((index % 31) + 1); // workerId范围: 1-31
379
+            const snowflake = new Snowflake(workerId, 1n, 0n);
380
+            // 从userList中获取用户的deptId
381
+            const user = userList.find(u => u.userId === userId);
382
+            const deptId = user ? user.deptId : null;
383
+            const recordData = {
384
+              participateId: snowflake.nextId().toString(),
385
+              userId: userId, // 参会人员ID
386
+              deptId: deptId, // 用户的部门ID
387
+              reportTime: this.addForm.reportTime, // 当前日期作为参会日期
388
+              trainName: this.addForm.trainName,
389
+              content: this.addForm.content,
390
+              hours: this.addForm.hours,
391
+              deptUserId: currentUserId, // 当前登录用户作为审核人
392
+              deptTime: this.addForm.deptTime,
393
+              deptComment: this.addForm.deptComment
394
+            };
395
+            return addTrainApproval(recordData);
396
+          });
397
+
398
+          // 执行所有新增操作
399
+          Promise.all(promises).then(() => {
400
+            this.$modal.msgSuccess(`成功为 ${this.addForm.userIdList.length} 位参会人员添加培训记录`);
401
+            this.addOpen = false;
402
+            this.resetAddForm("addForm");
403
+            this.getList(); // 刷新列表
404
+          }).catch(error => {
405
+            console.error('添加培训记录失败:', error);
406
+            this.$modal.msgError("添加培训记录失败,请检查网络连接或联系管理员");
407
+          });
408
+        }
409
+      });
410
+    },
411
+    cancelAdd() {
412
+      this.addOpen = false;
413
+      this.resetAddForm("addForm");
414
+    },
415
+    resetAddForm(formName) {
416
+      if (this.$refs[formName]) {
417
+        this.$refs[formName].resetFields();
418
+      }
419
+      this.addForm = {
420
+        userIdList: [],
421
+        reportTime: null,
422
+        trainName: null,
423
+        content: null,
424
+        hours: 0,
425
+        deptUserId: this.$store.state.user.id,
426
+        deptUserName: this.$store.state.user.name,
427
+        deptTime: null,
428
+        deptComment: null
429
+      };
430
+    },
431
+    getUserName(userId) {
432
+      const user = this.$store.state.user.userList.find(item => item.userId === userId);
433
+      return user ? user.nickName : '';
255 434
     }
256 435
   }
257 436
 };

Carregando…
Cancelar
Salvar