Parcourir la source

更新移动端,新增设备审批页面

余思翰 il y a 5 jours
Parent
révision
89bf259b6a

+ 4
- 3
oa-ui-app/config.js Voir le fichier

@@ -1,13 +1,14 @@
1 1
 /*
2 2
  * @Author: ysh
3 3
  * @Date: 2025-01-16 11:17:07
4
- * @LastEditors: 
5
- * @LastEditTime: 2025-02-27 10:25:33
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-04-11 09:21:58
6 6
  */
7 7
 // 应用全局配置
8 8
 module.exports = {
9 9
   // baseUrl: 'https://vue.ruoyi.vip/prod-api',
10
-  baseUrl: 'http://192.168.28.107:8080',
10
+  baseUrl: 'http://192.168.28.108:8080',
11
+  // baseUrl: '/prod-api',
11 12
   // 应用信息
12 13
   appInfo: {
13 14
     // 应用名称

+ 2
- 2
oa-ui-app/manifest.json Voir le fichier

@@ -1,6 +1,6 @@
1 1
 {
2
-    "name" : "CMC综合办公系统移动端",
3
-    "appid" : "__UNI__25A9D80",
2
+    "name" : "CMC智联云枢办公系统移动端",
3
+    "appid" : "__UNI__9ED53E4",
4 4
     "description" : "",
5 5
     "versionName" : "1.1.0",
6 6
     "versionCode" : "100",

+ 1
- 2
oa-ui-app/pages.json Voir le fichier

@@ -96,8 +96,7 @@
96 96
 		{
97 97
 			"path": "pages/message/myProcess/index",
98 98
 			"style": {
99
-				"navigationBarTitleText": "我的流程",
100
-				"enablePullDownRefresh": true
99
+				"navigationBarTitleText": "我的流程"
101 100
 			}
102 101
 		},
103 102
 		{

+ 302
- 0
oa-ui-app/pages/components/DevicePicker.vue Voir le fichier

@@ -0,0 +1,302 @@
1
+<template>
2
+  <uni-popup ref="devicePopup" type="bottom" @maskClick="close">
3
+    <view class="modal-container">
4
+      <!-- 搜索表单 -->
5
+      <view class="search-box">
6
+        <uni-data-select v-model="queryParams.status" :localdata="statusOptions" style="width: 200rpx;"
7
+          :clearable="false" @change="handleSearch">
8
+        </uni-data-select>
9
+
10
+        <uni-easyinput v-model="queryParams.code" placeholder="出厂编号" @confirm="handleSearch" clearable>
11
+        </uni-easyinput>
12
+
13
+        <uni-easyinput v-model="queryParams.name" placeholder="设备名称" @confirm="handleSearch" clearable>
14
+        </uni-easyinput>
15
+      </view>
16
+
17
+      <!-- 设备列表 -->
18
+      <scroll-view scroll-y class="list-container" @scrolltolower="loadMore" :scroll-top="scrollTop">
19
+        <view v-for="(item, index) in list" :key="index" class="list-item" :class="{ selected: isSelected(item) }"
20
+          @click="handleSelect(item)">
21
+          <view class="item-content">
22
+            <view class="item-header">
23
+              <uni-tag :text="statusText(item.status)" :type="statusType(item.status)" size="small">
24
+              </uni-tag>
25
+              <text class="device-code">{{ item.code }}</text>
26
+            </view>
27
+            <view class="item-body">
28
+              <text class="device-name">{{ item.name }}</text>
29
+              <text class="device-info">{{ item.brand }} {{ item.series }}</text>
30
+              <text class="device-place">📍 {{ item.place }}</text>
31
+            </view>
32
+          </view>
33
+
34
+          <uni-icons v-if="isSelected(item)" type="checkmarkempty" color="#007AFF" size="18" />
35
+        </view>
36
+
37
+        <!-- 加载状态 -->
38
+        <view class="loading-status">
39
+          <uni-load-more :status="loading ? 'loading' : (hasMore ? 'more' : 'noMore')" :contentText="{
40
+            contentdown: '上拉加载更多',
41
+            contentrefresh: '正在加载',
42
+            contentnomore: '没有更多设备'
43
+          }">
44
+          </uni-load-more>
45
+        </view>
46
+      </scroll-view>
47
+
48
+      <!-- 底部操作栏 -->
49
+      <view class="footer-actions">
50
+        <button class="action-btn cancel" @click="close">取消</button>
51
+        <button class="action-btn confirm" @click="confirm" :disabled="!selectedList.length">确定选择</button>
52
+      </view>
53
+    </view>
54
+  </uni-popup>
55
+</template>
56
+
57
+<script>
58
+import { listDevice, listDeviceName } from "@/api/oa/device/device";
59
+import { debounce } from 'lodash-es';
60
+
61
+export default {
62
+  props: {
63
+    visible: Boolean,
64
+    selected: {
65
+      type: [Array, Object],
66
+      default: () => ([])
67
+    },
68
+    multiple: {
69
+      type: Boolean,
70
+      default: true
71
+    }
72
+  },
73
+
74
+  data() {
75
+    return {
76
+      queryParams: {
77
+        pageNum: 1,
78
+        pageSize: 10,
79
+        status: '1',
80
+        type: '仪器设备',
81
+        code: '',
82
+        name: ''
83
+      },
84
+      statusOptions: [
85
+        { value: '0', text: '被领用' },
86
+        { value: '1', text: '可领用' },
87
+        { value: '2', text: '维修中' },
88
+        { value: '3', text: '已停用' },
89
+        { value: '4', text: '已报废' }
90
+      ],
91
+      list: [],
92
+      selectedList: [],
93
+      hasMore: true,
94
+      loading: false,
95
+      scrollTop: 0
96
+    }
97
+  },
98
+
99
+  watch: {
100
+    visible(val) {
101
+      val ? this.$refs.devicePopup.open() : this.$refs.devicePopup.close();
102
+      if (val) this.initData();
103
+    },
104
+    selected: {
105
+      immediate: true,
106
+      handler(val) {
107
+        this.selectedList = Array.isArray(val) ? [...val] : [val];
108
+      }
109
+    }
110
+  },
111
+
112
+  created() {
113
+    this.debouncedSearch = debounce(this.loadData, 300);
114
+  },
115
+
116
+  methods: {
117
+    // 初始化数据
118
+    initData() {
119
+      this.queryParams.pageNum = 1;
120
+      this.hasMore = true;
121
+      this.list = [];
122
+      this.loadData();
123
+    },
124
+
125
+    // 加载设备数据
126
+    async loadData() {
127
+      if (this.loading || !this.hasMore) return;
128
+
129
+      this.loading = true;
130
+      try {
131
+        const res = await listDevice(this.queryParams);
132
+        this.list = this.queryParams.pageNum === 1
133
+          ? res.rows
134
+          : [...this.list, ...res.rows];
135
+
136
+        this.hasMore = res.total > this.queryParams.pageNum * this.queryParams.pageSize;
137
+        this.queryParams.pageNum++;
138
+      } finally {
139
+        this.loading = false;
140
+      }
141
+    },
142
+
143
+    // 搜索处理
144
+    handleSearch() {
145
+      this.queryParams.pageNum = 1;
146
+      this.hasMore = true;
147
+      this.list = [];
148
+      this.loadData();
149
+    },
150
+
151
+    // 选择设备
152
+    handleSelect(item) {
153
+      if (this.multiple) {
154
+        const index = this.selectedList.findIndex(d => d.deviceId === item.deviceId);
155
+        index === -1
156
+          ? this.selectedList.push(item)
157
+          : this.selectedList.splice(index, 1);
158
+      } else {
159
+        this.selectedList = [item];
160
+      }
161
+    },
162
+
163
+    // 确认选择
164
+    confirm() {
165
+      const result = this.multiple ? this.selectedList : this.selectedList[0];
166
+      this.$emit('update:selected', result);
167
+      this.$emit('confirm', result);
168
+      this.close();
169
+    },
170
+
171
+    // 状态显示逻辑
172
+    statusText(status) {
173
+      const map = { '0': '被领用', '1': '可领用', '2': '维修中', '3': '已停用', '4': '已报废' };
174
+      return map[status] || '未知状态';
175
+    },
176
+
177
+    statusType(status) {
178
+      const typeMap = { '0': 'warning', '1': 'success', '2': 'info', '3': 'error', '4': 'default' };
179
+      return typeMap[status] || 'default';
180
+    },
181
+
182
+    isSelected(item) {
183
+      return this.selectedList.some(d => d.deviceId === item.deviceId);
184
+    },
185
+
186
+    // 加载更多
187
+    loadMore() {
188
+      this.loadData();
189
+    },
190
+
191
+    close() {
192
+      this.$emit('update:visible', false);
193
+    }
194
+  }
195
+}
196
+</script>
197
+
198
+<style lang="scss" scoped>
199
+.modal-container {
200
+  background: #fff;
201
+  border-radius: 24rpx 24rpx 0 0;
202
+  padding: 32rpx;
203
+  max-height: 80vh;
204
+}
205
+
206
+.search-box {
207
+  display: flex;
208
+  flex-wrap: wrap;
209
+  gap: 16rpx;
210
+  margin-bottom: 32rpx;
211
+
212
+  ::v-deep .uni-easyinput__content {
213
+    border-radius: 16rpx;
214
+  }
215
+}
216
+
217
+.list-container {
218
+  max-height: 60vh;
219
+  margin-bottom: 32rpx;
220
+
221
+  .list-item {
222
+    display: flex;
223
+    align-items: center;
224
+    padding: 24rpx;
225
+    border-bottom: 1rpx solid #eee;
226
+
227
+    &.selected {
228
+      background-color: #f5f7fa;
229
+    }
230
+
231
+    .item-content {
232
+      flex: 1;
233
+      display: flex;
234
+      flex-direction: column;
235
+      gap: 8rpx;
236
+
237
+      .item-header {
238
+        display: flex;
239
+        align-items: center;
240
+        gap: 16rpx;
241
+
242
+        .device-code {
243
+          color: #666;
244
+          font-size: 24rpx;
245
+        }
246
+      }
247
+
248
+      .item-body {
249
+        display: flex;
250
+        flex-direction: column;
251
+        gap: 4rpx;
252
+
253
+        .device-name {
254
+          color: #333;
255
+          font-size: 28rpx;
256
+          font-weight: 500;
257
+        }
258
+
259
+        .device-info {
260
+          color: #666;
261
+          font-size: 24rpx;
262
+        }
263
+
264
+        .device-place {
265
+          color: #999;
266
+          font-size: 24rpx;
267
+        }
268
+      }
269
+    }
270
+  }
271
+}
272
+
273
+.footer-actions {
274
+  display: flex;
275
+  gap: 32rpx;
276
+
277
+  .action-btn {
278
+    flex: 1;
279
+    border-radius: 12rpx;
280
+    font-size: 28rpx;
281
+
282
+    &.confirm {
283
+      background: #007AFF;
284
+      color: #fff;
285
+
286
+      &:disabled {
287
+        background: #ccc;
288
+      }
289
+    }
290
+
291
+    &.cancel {
292
+      background: #f5f5f5;
293
+      color: #666;
294
+    }
295
+  }
296
+}
297
+
298
+.loading-status {
299
+  padding: 32rpx 0;
300
+  text-align: center;
301
+}
302
+</style>

+ 10
- 2
oa-ui-app/pages/components/flowNote.vue Voir le fichier

@@ -2,7 +2,7 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-03-17 10:04:51
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-03-18 10:45:52
5
+ * @LastEditTime: 2025-03-28 14:29:11
6 6
 -->
7 7
 <template>
8 8
   <view>
@@ -42,6 +42,7 @@ export default {
42 42
   methods: {
43 43
     /** 流程流转记录 */
44 44
     getFlowRecordList(procInsId, deployId) {
45
+      this.currentNum = 0;
45 46
       const params = { procInsId: procInsId, deployId: deployId }
46 47
       flowRecordFull(params).then(res => {
47 48
         if (res.data.flowList.length > 1 && res.data.flowList[1].comment && res.data.flowList[1].comment.type == '退回意见') {
@@ -52,7 +53,14 @@ export default {
52 53
           this.isRetrun = false
53 54
         }
54 55
         this.flowRecordList = res.data.flowList.reverse();
55
-        this.currentNum = this.flowRecordList.length - 1;
56
+        for (let i = 0; i < this.flowRecordList.length; i++) {
57
+          if (!this.flowRecordList[i].finishTime) {
58
+            this.currentNum = i
59
+          }
60
+        }
61
+        if (this.currentNum == 0) {
62
+          this.currentNum = this.flowRecordList.length
63
+        }
56 64
       })
57 65
     },
58 66
   },

+ 5
- 4
oa-ui-app/pages/form/borrow/borrow.vue Voir le fichier

@@ -2,7 +2,7 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-02-20 10:20:22
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-03-26 16:08:38
5
+ * @LastEditTime: 2025-03-28 14:27:15
6 6
 -->
7 7
 <template>
8 8
   <view class="form-container">
@@ -13,7 +13,7 @@
13 13
     </view>
14 14
     <!-- 表单内容 -->
15 15
     <uni-forms ref="form" :modelValue="form" :rules="rules" label-position="top" label-width="150" class="custom-form">
16
-      <flow-note :taskForm="taskForm" v-if="taskName"></flow-note>
16
+      <flow-note :taskForm="taskForm"></flow-note>
17 17
       <!-- 当前节点 -->
18 18
       <uni-forms-item label="当前节点" class="form-item" v-if="taskName">
19 19
         <uni-tag :inverted="true" type="primary" :text="taskName"></uni-tag>
@@ -21,7 +21,7 @@
21 21
 
22 22
       <!-- 流程发起人 -->
23 23
       <uni-forms-item label="填报人" class="form-item">
24
-        <b style="font-size:35rpx;">{{ applierUserName }}</b>
24
+        <b style="font-size:30rpx;">{{ applierUserName }}</b>
25 25
       </uni-forms-item>
26 26
 
27 27
       <!-- 填报日期 -->
@@ -193,7 +193,7 @@ export default {
193 193
   },
194 194
   data() {
195 195
     return {
196
-      applierUserName:'',
196
+      applierUserName: '',
197 197
       form: {
198 198
         submitTime: '',
199 199
         borrowUsage: '0',
@@ -591,6 +591,7 @@ export default {
591 591
 </script>
592 592
 
593 593
 <style lang="scss" scoped>
594
+
594 595
 .amount-text {
595 596
   font-size: 16px;
596 597
   font-weight: bold;

+ 19
- 7
oa-ui-app/pages/form/declare/declare.vue Voir le fichier

@@ -10,13 +10,13 @@
10 10
 			class="custom-form">
11 11
 			<flow-note :taskForm="taskForm"></flow-note>
12 12
 			<!-- 当前节点 -->
13
-			<uni-forms-item label="当前节点" class="form-item">
13
+			<uni-forms-item label="当前节点" class="form-item" v-if="taskName">
14 14
 				<uni-tag :inverted="true" type="primary" :text="taskName"></uni-tag>
15 15
 			</uni-forms-item>
16 16
 
17 17
 			<!-- 流程发起人 -->
18 18
 			<uni-forms-item label="填报人" class="form-item">
19
-				<b style="font-size:35rpx;">{{ startUserName }}</b>
19
+				<b style="font-size:30rpx;">{{ applierUserName }}</b>
20 20
 			</uni-forms-item>
21 21
 
22 22
 			<!-- 填报日期 -->
@@ -72,7 +72,7 @@
72 72
 
73 73
 			<uni-forms-item label="系数" required class="form-item" name="coefficient" v-if="taskName != '工作填报'">
74 74
 				<uni-easyinput type="number" v-model="formData.coefficient" placeholder="请输入系数" :styles="inputStyle"
75
-					@blur="countMoney" @clear="countMoney" :disabled="taskName == '填报人确认'">
75
+					@blur="countMoney" @clear="countMoney" :disabled="!taskName || taskName == '填报人确认'">
76 76
 				</uni-easyinput>
77 77
 			</uni-forms-item>
78 78
 
@@ -84,9 +84,12 @@
84 84
 				<u-tag :text="'¥' + money" type="primary" plain></u-tag>
85 85
 			</uni-forms-item>
86 86
 			<!-- 提交按钮 -->
87
-			<button class="save-btn" @click="save">保存</button>
88
-			<button class="submit-btn margin-top-xs" type="primary" @click="submitForm" v-if="taskName == '工作填报'">提交</button>
89
-			<button class="submit-btn margin-top-xs" type="primary" @click="submitForm" v-else>确认审核</button>
87
+			<view v-if="taskName">
88
+				<button class="save-btn" @click="save">保存</button>
89
+				<button class="submit-btn margin-top-xs" type="primary" @click="submitForm"
90
+					v-if="taskName == '工作填报'">提交</button>
91
+				<button class="submit-btn margin-top-xs" type="primary" @click="submitForm" v-else>确认审核</button>
92
+			</view>
90 93
 		</uni-forms>
91 94
 
92 95
 		<uv-modal ref="popModal" title="提示" content='是否提交表单?' :showCancelButton="true" @confirm="confirmSubmit"></uv-modal>
@@ -115,6 +118,7 @@ export default {
115 118
 		startUserName: String, // 流程发起人
116 119
 	},
117 120
 	created() {
121
+    this.applierUserName = this.startUserName;
118 122
 		this.getProjectList();
119 123
 		this.initForm();
120 124
 		if (this.taskName != '工作填报') {
@@ -126,6 +130,7 @@ export default {
126 130
 	},
127 131
 	data() {
128 132
 		return {
133
+			applierUserName: '',
129 134
 			openProject: false,
130 135
 			selectedProject: null,
131 136
 			isScattered: 0,
@@ -230,6 +235,11 @@ export default {
230 235
 				if (res.data) {
231 236
 					this.hasForm = true;
232 237
 					this.formData = res.data;
238
+					console.log(this.formData);
239
+
240
+					if (!this.applierUserName) {
241
+						this.applierUserName = this.formData.user.nickName;
242
+					}
233 243
 					if (res.data.projectId) {
234 244
 						this.isScattered = 0;
235 245
 					} else {
@@ -394,7 +404,9 @@ export default {
394 404
 						icon: 'success'
395 405
 					});
396 406
 					setTimeout(() => {
397
-						this.$emit('goBack')
407
+						uni.switchTab({
408
+							url: '/pages/message/index'
409
+						})
398 410
 					}, 500);
399 411
 				})
400 412
 			})

+ 338
- 0
oa-ui-app/pages/form/device/device.vue Voir le fichier

@@ -0,0 +1,338 @@
1
+<template>
2
+  <view class="form-container">
3
+    <!-- 表单标题 -->
4
+    <view class="form-title">
5
+      <text class="title-text">设备申请表</text>
6
+      <view class="title-line"></view>
7
+    </view>
8
+    <!-- 表单内容 -->
9
+    <uni-forms ref="form" :modelValue="form" :rules="rules" label-position="top" label-width="150" class="custom-form">
10
+      <flow-note :taskForm="taskForm"></flow-note>
11
+      <!-- 当前节点 -->
12
+      <uni-forms-item label="当前节点" class="form-item" v-if="taskName">
13
+        <uni-tag :inverted="true" type="primary" :text="taskName"></uni-tag>
14
+      </uni-forms-item>
15
+
16
+      <!-- 流程发起人 -->
17
+      <uni-forms-item label="填报人" class="form-item">
18
+        <b style="font-size:30rpx;">{{ applierUserName }}</b>
19
+      </uni-forms-item>
20
+
21
+      <!-- 填报日期 -->
22
+      <uni-forms-item label="填报日期" class="form-item">
23
+        <uni-datetime-picker v-model="form.applyDate" type="date" :disabled="taskName != '设备申请'" />
24
+      </uni-forms-item>
25
+
26
+      <!-- 选择项目 -->
27
+      <uni-forms-item label="选择项目" required class="form-item" name="projectId">
28
+        <uv-button type="primary" @click="openProject = true" v-if="taskName == '设备申请'">+ 选择项目</uv-button>
29
+        <ProjectPicker :visible.sync="openProject" :selected.sync="selectedProject" @confirm="handleConfirm" />
30
+        <ProjectInfo :project="projectObj"></ProjectInfo>
31
+      </uni-forms-item>
32
+
33
+      <!-- 设备选择 -->
34
+      <uni-forms-item label="选择设备" required class="form-item" name="devices">
35
+        <uv-button type="primary" @click="openDevice = true" v-if="taskName == '设备申请'">+ 选择设备</uv-button>
36
+        <device-picker :visible.sync="openDevice" :selected.sync="selectDevice" @confirm="handleDeviceConfirm"></device-picker>
37
+        <uni-table v-if="deviceList.length > 0">
38
+          <uni-tr>
39
+            <uni-th>序号</uni-th>
40
+            <uni-th>出厂编号</uni-th>
41
+            <uni-th>设备品牌</uni-th>
42
+            <uni-th>设备名称</uni-th>
43
+            <uni-th>规格型号</uni-th>
44
+            <uni-th>存放地址</uni-th>
45
+          </uni-tr>
46
+          <uni-tr v-for="(item, index) in deviceList" :key="index">
47
+            <uni-td>{{ index + 1 }}</uni-td>
48
+            <uni-td>{{ item.code }}</uni-td>
49
+            <uni-td>{{ item.brand }}</uni-td>
50
+            <uni-td>{{ item.name }}</uni-td>
51
+            <uni-td>{{ item.series }}</uni-td>
52
+            <uni-td>{{ item.place }}</uni-td>
53
+          </uni-tr>
54
+        </uni-table>
55
+      </uni-forms-item>
56
+
57
+      <!-- 申领事由 -->
58
+      <uni-forms-item label="申领事由" required class="form-item" name="applyReason">
59
+        <uni-easyinput type="textarea" v-model="form.applyReason" placeholder="请输入申领事由" :disabled="taskName != '设备申请'" />
60
+      </uni-forms-item>
61
+
62
+      <!-- 日期选择 -->
63
+      <view class="date-range">
64
+        <uni-forms-item label="开始日期" required class="form-item" name="beginDate">
65
+          <uni-datetime-picker v-model="form.beginDate" type="date" :disabled="taskName != '设备申请'" @change="calculateDay" />
66
+        </uni-forms-item>
67
+        <uni-forms-item label="结束日期" required class="form-item" name="endDate">
68
+          <uni-datetime-picker v-model="form.endDate" type="date" :disabled="taskName != '设备申请'" @change="calculateDay" />
69
+        </uni-forms-item>
70
+        <uni-forms-item label="共计" class="form-item">
71
+          <text>{{ form.days + '天' }}</text>
72
+        </uni-forms-item>
73
+      </view>
74
+
75
+      <!-- 安排设备意见 -->
76
+      <uni-forms-item label="拟发放设备" class="form-item" v-if="taskName == '安排设备'">
77
+        <uni-table v-if="modifyDeviceList.length > 0">
78
+          <uni-tr>
79
+            <uni-th>序号</uni-th>
80
+            <uni-th>出厂编号</uni-th>
81
+            <uni-th>设备品牌</uni-th>
82
+            <uni-th>设备名称</uni-th>
83
+            <uni-th>规格型号</uni-th>
84
+            <uni-th>存放地址</uni-th>
85
+            <uni-th>操作</uni-th>
86
+          </uni-tr>
87
+          <uni-tr v-for="(item, index) in modifyDeviceList" :key="index">
88
+            <uni-td>{{ index + 1 }}</uni-td>
89
+            <uni-td>{{ item.code }}</uni-td>
90
+            <uni-td>{{ item.brand }}</uni-td>
91
+            <uni-td>{{ item.name }}</uni-td>
92
+            <uni-td>{{ item.series }}</uni-td>
93
+            <uni-td>{{ item.place }}</uni-td>
94
+            <uni-td>
95
+              <uv-button type="primary" size="mini" @click="replaceRowData(item, index)">重新选择</uv-button>
96
+              <uv-button type="error" size="mini" @click="deleteRowData(item, index)">删除</uv-button>
97
+            </uni-td>
98
+          </uni-tr>
99
+        </uni-table>
100
+        <uv-button type="primary" @click="addRowdata()" v-if="taskName == '安排设备'">新增设备</uv-button>
101
+      </uni-forms-item>
102
+
103
+      <!-- 安排设备意见 -->
104
+      <uni-forms-item label="安排设备意见" class="form-item" v-if="taskName == '安排设备'">
105
+        <uni-easyinput type="textarea" v-model="form.dispatchComment" placeholder="请输入安排设备意见" :disabled="taskName != '安排设备'" />
106
+      </uni-forms-item>
107
+
108
+      <!-- 分管审核意见 -->
109
+      <uni-forms-item label="分管审核意见" class="form-item" v-if="taskName == '分管审核'">
110
+        <uni-easyinput type="textarea" v-model="form.managerComment" placeholder="请输入分管审核意见" :disabled="taskName != '分管审核'" />
111
+      </uni-forms-item>
112
+
113
+      <!-- 归还确认 -->
114
+      <uni-forms-item label="已归还设备" class="form-item" v-if="taskName == '归还确认'">
115
+        <uni-table v-if="returnDevicesList.length > 0">
116
+          <uni-tr>
117
+            <uni-th>序号</uni-th>
118
+            <uni-th>出厂编号</uni-th>
119
+            <uni-th>设备品牌</uni-th>
120
+            <uni-th>设备名称</uni-th>
121
+            <uni-th>规格型号</uni-th>
122
+            <uni-th>存放地址</uni-th>
123
+          </uni-tr>
124
+          <uni-tr v-for="(item, index) in returnDevicesList" :key="index">
125
+            <uni-td>{{ index + 1 }}</uni-td>
126
+            <uni-td>{{ item.code }}</uni-td>
127
+            <uni-td>{{ item.brand }}</uni-td>
128
+            <uni-td>{{ item.name }}</uni-td>
129
+            <uni-td>{{ item.series }}</uni-td>
130
+            <uni-td>{{ item.place }}</uni-td>
131
+          </uni-tr>
132
+        </uni-table>
133
+      </uni-forms-item>
134
+
135
+      <!-- 需维修设备 -->
136
+      <uni-forms-item label="需维修设备" class="form-item" v-if="taskName == '归还确认'">
137
+        <uni-data-select v-model="form.repairDevices" :localdata="modifyDeviceList" multiple :disabled="taskName != '归还确认'" />
138
+      </uni-forms-item>
139
+
140
+      <!-- 备注 -->
141
+      <uni-forms-item label="备注" class="form-item" v-if="taskName == '归还确认'">
142
+        <uni-easyinput type="textarea" v-model="form.remark" placeholder="请输入备注" :disabled="taskName != '归还确认'" />
143
+      </uni-forms-item>
144
+
145
+      <!-- 归还日期 -->
146
+      <uni-forms-item label="归还日期" class="form-item" v-if="taskName == '归还确认'">
147
+        <uni-datetime-picker v-model="form.returnDate" type="date" :disabled="taskName != '归还确认'" />
148
+      </uni-forms-item>
149
+
150
+      <!-- 操作按钮 -->
151
+      <view class="form-actions">
152
+        <uv-button type="primary" @click="submit" v-if="taskName == '设备申请'">提交申请</uv-button>
153
+        <template v-else>
154
+          <uv-button type="warning" @click="saves">保存</uv-button>
155
+          <uv-button type="primary" @click="completeApply">完成审批</uv-button>
156
+        </template>
157
+      </view>
158
+    </uni-forms>
159
+  </view>
160
+</template>
161
+
162
+<script>
163
+import { parseTime } from "@/utils/common.js"
164
+import { listDeviceApproval, getDeviceApproval, updateDeviceApproval, submitDeviceApproval, modifyDeviceApproval } from '@/api/oa/device/deviceApproval'
165
+import FlowNote from '@/pages/components/flowNote.vue';
166
+import ProjectPicker from '@/pages/components/ProjectPicker.vue';
167
+import ProjectInfo from '@/pages/components/ProjectInfo.vue';
168
+import DevicePicker from "@/pages/components/DevicePicker.vue";
169
+
170
+export default {
171
+  components: {
172
+    FlowNote,
173
+    ProjectPicker,
174
+    ProjectInfo,
175
+    DevicePicker
176
+  },
177
+  props: {
178
+    taskForm: Object,
179
+    taskName: String,
180
+    startUserName: String,
181
+  },
182
+  created() {
183
+    this.applierUserName = this.startUserName;
184
+    this.initForm();
185
+  },
186
+  data() {
187
+    return {
188
+      form: {
189
+        projectId: '',
190
+        applyDate: '',
191
+        applyReason: '',
192
+        beginDate: '',
193
+        endDate: '',
194
+        days: 0,
195
+        dispatchComment: '',
196
+        managerComment: '',
197
+        repairDevices: [],
198
+        remark: '',
199
+        returnDate: ''
200
+      },
201
+      rules: {
202
+        projectId: {
203
+          rules: [{ required: true, errorMessage: '请选择项目' }]
204
+        },
205
+        devices: {
206
+          rules: [{ required: true, errorMessage: '请选择设备' }]
207
+        },
208
+        applyReason: {
209
+          rules: [{ required: true, errorMessage: '请输入申领事由' }]
210
+        },
211
+        beginDate: {
212
+          rules: [{ required: true, errorMessage: '请选择开始日期' }]
213
+        },
214
+        endDate: {
215
+          rules: [{ required: true, errorMessage: '请选择结束日期' }]
216
+        }
217
+      },
218
+      applierUserName: '',
219
+      openProject: false,
220
+      selectedProject: null,
221
+      projectObj: {},
222
+      openDevice: false,
223
+      selectDevice: [],
224
+      deviceList: [],
225
+      modifyDeviceList: [],
226
+      returnDevicesList: []
227
+    }
228
+  },
229
+  methods: {
230
+    initForm() {
231
+      getDeviceApproval(this.taskForm.formId).then(res => {
232
+        if (res.data) {
233
+          this.form = res.data;
234
+          this.deviceList = res.data.devices || [];
235
+          this.modifyDeviceList = res.data.modifyDevices || [];
236
+          this.returnDevicesList = res.data.returnDevices || [];
237
+        } else {
238
+          this.form.applyDate = parseTime(new Date(), "{y}-{m}-{d}");
239
+        }
240
+      })
241
+    },
242
+    handleConfirm(project) {
243
+      this.selectedProject = project;
244
+      this.projectObj = project;
245
+      this.form.projectId = project.projectId;
246
+    },
247
+    handleDeviceConfirm(devices) {
248
+      this.deviceList = Array.isArray(devices) ? devices : [devices];
249
+      this.form.devices = this.deviceList.map(item => item.deviceId);
250
+    },
251
+    calculateDay() {
252
+      if (this.form.beginDate && this.form.endDate) {
253
+        const begin = new Date(this.form.beginDate);
254
+        const end = new Date(this.form.endDate);
255
+        this.form.days = Math.ceil((end - begin) / (1000 * 60 * 60 * 24)) + 1;
256
+      }
257
+    },
258
+    replaceRowData(row, index) {
259
+      this.openDevice = true;
260
+      this.currentIndex = index;
261
+    },
262
+    deleteRowData(row, index) {
263
+      this.modifyDeviceList.splice(index, 1);
264
+    },
265
+    addRowdata() {
266
+      this.openDevice = true;
267
+      this.currentIndex = -1;
268
+    },
269
+    submit() {
270
+      this.$refs.form.validate().then(() => {
271
+        submitDeviceApproval(this.form).then(res => {
272
+          uni.showToast({
273
+            title: '提交成功',
274
+            icon: 'success'
275
+          });
276
+          uni.navigateBack();
277
+        });
278
+      });
279
+    },
280
+    saves() {
281
+      updateDeviceApproval(this.form).then(res => {
282
+        uni.showToast({
283
+          title: '保存成功',
284
+          icon: 'success'
285
+        });
286
+      });
287
+    },
288
+    completeApply() {
289
+      modifyDeviceApproval(this.form).then(res => {
290
+        uni.showToast({
291
+          title: '审批完成',
292
+          icon: 'success'
293
+        });
294
+        uni.navigateBack();
295
+      });
296
+    }
297
+  }
298
+}
299
+</script>
300
+
301
+<style lang="scss" scoped>
302
+.form-container {
303
+  padding: 32rpx;
304
+}
305
+
306
+.form-title {
307
+  text-align: center;
308
+  margin-bottom: 32rpx;
309
+
310
+  .title-text {
311
+    font-size: 36rpx;
312
+    font-weight: bold;
313
+  }
314
+
315
+  .title-line {
316
+    height: 2rpx;
317
+    background: #eee;
318
+    margin-top: 16rpx;
319
+  }
320
+}
321
+
322
+.date-range {
323
+  display: flex;
324
+  flex-wrap: wrap;
325
+  gap: 32rpx;
326
+}
327
+
328
+.form-actions {
329
+  display: flex;
330
+  justify-content: center;
331
+  gap: 32rpx;
332
+  margin-top: 32rpx;
333
+}
334
+
335
+::v-deep .uni-forms-item__label {
336
+  font-weight: bold;
337
+}
338
+</style>

+ 137
- 24
oa-ui-app/pages/message/apply/apply.vue Voir le fichier

@@ -2,41 +2,73 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-01-21 10:01:39
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-02-19 15:55:13
5
+ * @LastEditTime: 2025-04-11 15:36:40
6 6
 -->
7 7
 <template>
8 8
 	<view>
9
-		<view class="tag-view text-center margin-top-xs margin-bottom-xs" style="padding: 10rpx;">
10
-			<uni-tag :text="'您还有' + total + '个任务待审'" />
11
-		</view>
12
-		<u-list v-if="todoList.length > 0">
13
-			<u-list-item v-for="(item, index) in todoList" :key="index">
14
-				<view @click="goToDetail(item)">
15
-					<view class="box">
16
-						<view style="text-align: right;">
17
-							<span style="font-size: 26rpx;color: #999999;">{{ item.createTime }}</span>
9
+		<mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
10
+			:up="upOption" :top="20">
11
+
12
+			<view class="tag-view text-center margin-top-xs margin-bottom-xs" style="padding: 10rpx;">
13
+				<uni-tag :text="'您还有' + total + '个任务待审'" />
14
+			</view>
15
+
16
+			<view v-if="todoList.length > 0">
17
+				<view v-for="(item, index) in todoList" :key="index">
18
+					<view @click="goToDetail(item)" class="box">
19
+						<!-- 当前节点标签 -->
20
+						<view class="node-tag">
21
+							<uni-tag :text="'流程名称:' + item.procDefName" inverted="true" type="primary" size="small" />
22
+						</view>
23
+
24
+						<!-- 时间与标题区域 -->
25
+						<view class="header-area">
26
+							<text class="time-text">{{ item.createTime }}</text>
27
+							<uni-title type="h4" :title="item.title || '暂无标题'" class="title-text" />
28
+						</view>
29
+
30
+						<!-- 流程信息 -->
31
+						<view class="process-info">
32
+							<view class="info-item">
33
+								<text class="info-label">当前节点:</text>
34
+								<text class="info-value">{{ item.taskName }}</text>
35
+							</view>
36
+							<view class="info-item">
37
+								<text class="info-label">发起人:</text>
38
+								<text class="info-value">{{ item.startUserName }}</text>
39
+							</view>
18 40
 						</view>
19
-						<uni-title type="h3" :title="item.title ? item.title : '暂无标题'"></uni-title>
20
-						<view>流程名称:{{ item.procDefName }}</view>
21
-						<view>当前节点:{{ item.taskName }}</view>
22
-						<view>流程发起人:{{ item.startUserName }}</view>
23 41
 					</view>
24 42
 				</view>
25
-			</u-list-item>
26
-		</u-list>
27
-		<u-empty v-else></u-empty>
43
+			</view>
44
+
45
+			<u-empty v-else mode="list"></u-empty>
46
+		</mescroll-uni>
28 47
 	</view>
29 48
 </template>
30 49
 
31 50
 <script>
32
-import {
33
-	todoList
34
-} from "@/api/flowable/todo";
51
+import { todoList } from "@/api/flowable/todo";
52
+import MescrollMixin from '@/uni_modules/mescroll/components/mescroll-uni/mescroll-mixins.js'
35 53
 export default {
54
+	mixins: [MescrollMixin],
36 55
 	data() {
37 56
 		return {
38 57
 			todoList: [],
39 58
 			total: 0,
59
+			mescroll: null,
60
+			downOption: {
61
+				auto: false, // 手动初始化后需要关闭自动加载
62
+				textOutOffset: '下拉刷新',
63
+				textLoading: '加载中...'
64
+			},
65
+			upOption: {
66
+				auto: true, // 自动加载初始页
67
+				page: { num: 0, size: 10 }, // 初始页码和每页数量
68
+				noMoreSize: 5, // 数据不足时显示"没有更多"
69
+				empty: { tip: '暂无待审任务' },
70
+				textNoMore: '~ 没有更多任务了 ~'
71
+			}
40 72
 		};
41 73
 	},
42 74
 	created() {
@@ -49,6 +81,35 @@ export default {
49 81
 		this.getTodoList();
50 82
 	},
51 83
 	methods: {
84
+		// 初始化mescroll实例
85
+		mescrollInit(mescroll) {
86
+			this.mescroll = mescroll;
87
+		},
88
+		// 下拉刷新回调
89
+		downCallback() {
90
+			this.mescroll.resetUpScroll(true);
91
+		},
92
+		async upCallback(page) {
93
+			try {
94
+				const res = await todoList({
95
+					pageNum: page.num,
96
+					pageSize: page.size,
97
+					name: null
98
+				});
99
+
100
+				const curPageLen = res.data.records.length;
101
+				this.total = res.data.total;
102
+
103
+				// 处理分页数据
104
+				if (page.num == 1) this.todoList = []; // 第一页清空数据
105
+				this.todoList = this.todoList.concat(res.data.records);
106
+
107
+				// 结束加载状态
108
+				this.mescroll.endSuccess(curPageLen, res.data.total <= this.todoList.length);
109
+			} catch (e) {
110
+				this.mescroll.endErr();
111
+			}
112
+		},
52 113
 		getTodoList() {
53 114
 			todoList({
54 115
 				pageNum: 1,
@@ -81,10 +142,62 @@ export default {
81 142
 </script>
82 143
 
83 144
 <style lang="scss">
145
+/* 新增mescroll布局样式 */
146
+mescroll-body {
147
+	height: calc(100vh - 100rpx);
148
+	box-sizing: border-box;
149
+}
150
+
84 151
 .box {
85
-	background-color: #fff;
86
-	border-radius: 4px;
87
-	padding: 10px 20px;
88
-	margin: 0 10px 10px;
152
+	position: relative;
153
+	background: #fff;
154
+	border-radius: 12rpx;
155
+	padding: 32rpx;
156
+	margin: 20rpx;
157
+	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
158
+
159
+	.node-tag {
160
+		position: absolute;
161
+		left: 20rpx;
162
+		top: 40rpx;
163
+		z-index: 2;
164
+	}
165
+
166
+	.header-area {
167
+		margin-top: 16rpx;
168
+		border-bottom: 1rpx solid #eee;
169
+
170
+		.time-text {
171
+			float: right;
172
+			font-size: 24rpx;
173
+			color: #999;
174
+		}
175
+
176
+		.title-text {
177
+			clear: both;
178
+			font-weight: 600;
179
+			color: #333;
180
+			margin: 16rpx 0;
181
+		}
182
+	}
183
+
184
+	.process-info {
185
+		margin-top: 24rpx;
186
+
187
+		.info-item {
188
+			margin-bottom: 12rpx;
189
+			font-size: 28rpx;
190
+
191
+			.info-label {
192
+				color: #666;
193
+				margin-right: 16rpx;
194
+			}
195
+
196
+			.info-value {
197
+				color: #333;
198
+				font-weight: 500;
199
+			}
200
+		}
201
+	}
89 202
 }
90 203
 </style>

+ 6
- 1
oa-ui-app/pages/message/apply/detail.vue Voir le fichier

@@ -2,7 +2,7 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-02-06 09:48:52
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-02-20 10:24:30
5
+ * @LastEditTime: 2025-04-11 15:37:23
6 6
 -->
7 7
 <template>
8 8
 	<view>
@@ -19,12 +19,14 @@ import {
19 19
 } from "@/api/flowable/definition";
20 20
 import Declare from '@/pages/form/declare/declare.vue';
21 21
 import Borrow from "@/pages/form/borrow/borrow.vue";
22
+import Device from "@/pages/form/device/device.vue";
22 23
 import EmptyBox from "../../emptyBox.vue";
23 24
 export default {
24 25
 	components: {
25 26
 		Declare,
26 27
 		EmptyBox,
27 28
 		Borrow,
29
+		Device
28 30
 	},
29 31
 	data() {
30 32
 		return {
@@ -78,6 +80,9 @@ export default {
78 80
 				case '借款审批':
79 81
 					this.currentForm = 'Borrow';
80 82
 					break;
83
+				case '设备审批':
84
+					this.currentForm = 'Device';
85
+					break;
81 86
 				default:
82 87
 					console.log('未知流程');
83 88
 					this.isEmpty = true;

+ 209
- 0
oa-ui-app/pages/message/myProcess/index copy.vue Voir le fichier

@@ -0,0 +1,209 @@
1
+<template>
2
+  <view class="page-container">
3
+    <!-- 搜索框 -->
4
+    <view class="search-box">
5
+      <uni-search-bar radius="34" placeholder="请输入流程名称或标题" @confirm="search" @input="inputChange"
6
+        v-model="queryParams.keyword" />
7
+    </view>
8
+
9
+    <!-- 流程列表 -->
10
+    <mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :fixed="false" :top="10">
11
+      <uni-list>
12
+        <uni-list-item v-for="(item, index) in list" :key="index" :title="item.procDefName"
13
+          :note="`提交时间:${item.createTime}`" clickable>
14
+          <template v-slot:body>
15
+            <view class="list-item">
16
+              <!-- 状态 -->
17
+              <view class="status">
18
+                <uni-tag :text="item.finishTime ? '已完成' : '进行中'" :type="item.finishTime ? 'success' : 'warning'"
19
+                  size="small" />
20
+              </view>
21
+
22
+              <!-- 主要内容 -->
23
+              <view class="content">
24
+                <view class="title-line">
25
+                  <text class="title"><b>标题:</b>{{ item.title }}</text>
26
+                </view>
27
+                <uv-divider :dashed="true"></uv-divider>
28
+                <view class="info-line">
29
+                  <view class="time">耗时:{{ item.duration }}</view>
30
+                  <view class="node">当前节点:{{ item.taskName }}</view>
31
+                  <view class="assignee">待办人:{{ item.assigneeName || '无' }}</view>
32
+                </view>
33
+              </view>
34
+
35
+              <!-- 操作按钮 -->
36
+              <view class="actions">
37
+                <uni-icons type="more-filled" size="24" @click.stop="showAction(item)"></uni-icons>
38
+              </view>
39
+            </view>
40
+          </template>
41
+        </uni-list-item>
42
+      </uni-list>
43
+    </mescroll-uni>
44
+
45
+    <!-- 操作菜单 -->
46
+    <uni-popup ref="actionPopup" type="bottom">
47
+      <view class="action-menu">
48
+        <view class="menu-item" @click="handleFlowRecord(selectedItem)">
49
+          <text>办理进度</text>
50
+        </view>
51
+        <view class="menu-item" @click="handleFlowNote(selectedItem)">
52
+          <text>表单信息</text>
53
+        </view>
54
+        <view class="menu-item danger" @click="handleDelete(selectedItem)"
55
+          :class="{ disabled: !beDeleted(selectedItem) }">
56
+          <text>取消流程</text>
57
+        </view>
58
+      </view>
59
+    </uni-popup>
60
+  </view>
61
+</template>
62
+
63
+<script>
64
+import MescrollMixin from '@/uni_modules/mescroll/components/mescroll-uni/mescroll-mixins.js'
65
+import { myProcessList, stopProcess } from "@/api/flowable/process";
66
+export default {
67
+  mixins: [MescrollMixin],
68
+  data() {
69
+    return {
70
+      // 我发起的流程列表数据
71
+      myProcessList: [],
72
+      list: [], // 列表数据
73
+      queryParams: {
74
+        keyword: '',
75
+        pageNum: 1,
76
+        pageSize: 10
77
+      },
78
+      selectedItem: null, // 当前选中项
79
+      debounceSearch: null // 初始化防抖方法
80
+    }
81
+  },
82
+  created() {
83
+    // 在生命周期中创建防抖方法
84
+    this.debounceSearch = uni.$u.debounce(() => {
85
+      this.search()
86
+    }, 500)
87
+  },
88
+  methods: {
89
+    // mescroll下拉刷新回调
90
+    async downCallback() {
91
+      this.queryParams.pageNum = 1
92
+      const res = await this.loadData()
93
+      this.list = res.data.list;
94
+      console.log(res.data.list);
95
+      this.mescroll.endSuccess(res.data.length)
96
+      this.mescroll.endErr()
97
+    },
98
+    // mescroll上拉加载回调
99
+    async upCallback(page) {
100
+      this.queryParams.pageNum = page.num
101
+      const res = await this.loadData()
102
+      this.list = this.list.concat(res.data.list)
103
+      this.mescroll.endSuccess(res.data.list.length, res.data.hasNextPage)
104
+    },
105
+    // 加载数据
106
+    async loadData() {
107
+      try {
108
+        const res = await myProcessList(this.queryParams)
109
+        return {
110
+          data: {
111
+            list: res.data.records,
112
+            hasNextPage: res.hasNextPage
113
+          }
114
+        }
115
+      } catch (e) {
116
+        this.mescroll.endErr()
117
+        return { data: { list: [], hasNextPage: false } }
118
+      }
119
+    },
120
+    // 显示操作菜单
121
+    showAction(item) {
122
+      this.selectedItem = item
123
+      this.$refs.actionPopup.open()
124
+    },
125
+    // 输入变化处理
126
+    inputChange() {
127
+      this.debounceSearch()
128
+    },
129
+
130
+    // 执行搜索
131
+    search() {
132
+      this.mescroll.resetUpScroll()
133
+    },
134
+
135
+    handleFlowRecord(row) { /* ... */ },
136
+    handleFlowNote(row) { /* ... */ },
137
+    handleDelete(row) { /* ... */ },
138
+    beDeleted(row) { /* ... */ },
139
+  },
140
+}
141
+</script>
142
+
143
+<style lang="scss" scoped>
144
+.list-item {
145
+  display: flex;
146
+  align-items: center;
147
+  padding: 12rpx 0;
148
+  width: 100%;
149
+  justify-content: space-between;
150
+}
151
+
152
+.status {
153
+  margin-right: 20rpx;
154
+}
155
+
156
+.content {
157
+  flex: 1;
158
+}
159
+
160
+.title-line {
161
+  // display: flex;
162
+  justify-content: space-between;
163
+  margin-bottom: 10rpx;
164
+}
165
+
166
+.title {
167
+  font-size: 28rpx;
168
+  color: #333;
169
+  max-width: 400rpx;
170
+  overflow: hidden;
171
+  text-overflow: ellipsis;
172
+}
173
+
174
+.time {
175
+  font-size: 24rpx;
176
+  color: #999;
177
+}
178
+
179
+.info-line {
180
+  // display: flex;
181
+  justify-content: space-between;
182
+  font-size: 24rpx;
183
+  color: #666;
184
+}
185
+
186
+.actions {
187
+  padding-left: 20rpx;
188
+}
189
+
190
+.action-menu {
191
+  background: #fff;
192
+  border-radius: 16rpx 16rpx 0 0;
193
+  padding: 30rpx;
194
+}
195
+
196
+.menu-item {
197
+  padding: 30rpx;
198
+  border-bottom: 1rpx solid #eee;
199
+}
200
+
201
+.menu-item.danger {
202
+  color: #f56c6c;
203
+}
204
+
205
+.menu-item.disabled {
206
+  color: #c0c4cc;
207
+  pointer-events: none;
208
+}
209
+</style>

+ 94
- 17
oa-ui-app/pages/message/myProcess/index.vue Voir le fichier

@@ -2,7 +2,7 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-02-19 15:36:34
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-03-26 15:35:31
5
+ * @LastEditTime: 2025-04-11 15:33:30
6 6
 -->
7 7
 <template>
8 8
   <view class="page-container">
@@ -11,11 +11,16 @@
11 11
       <uni-search-bar radius="34" placeholder="请输入流程名称" @confirm="search" @input="inputChange"
12 12
         v-model="queryParams.procDefName" />
13 13
     </view>
14
-
14
+    <view>
15
+      <!-- <uv-sticky bgColor="#fff">
16
+        <uv-tabs :list="[{ name: '进行中' }, { name: '已完成' }]" :current="activeTabs" @click="switchTabs"></uv-tabs>
17
+      </uv-sticky> -->
18
+    </view>
15 19
     <!-- 流程列表 -->
16
-    <mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :fixed="false" :top="10">
20
+    <mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
21
+      :up="upOption" :fixed="false" :top="10">
17 22
       <uni-list>
18
-        <uni-list-item v-for="(item, index) in list" :key="index" clickable>
23
+        <uni-list-item v-for="(item, index) in myProcessList" :key="index">
19 24
           <template v-slot:body>
20 25
             <view class="list-item">
21 26
               <!-- 状态与标题行 -->
@@ -82,7 +87,7 @@
82 87
           <text>表单信息</text>
83 88
         </view>
84 89
         <view class="menu-item danger" @click="handleDelete(selectedItem)"
85
-          :class="{ disabled: !beDeleted(selectedItem) }">
90
+          :class="{ disabled: selectedItem && beDeleted(selectedItem) }">
86 91
           <text>取消流程</text>
87 92
         </view>
88 93
       </view>
@@ -99,15 +104,20 @@
99 104
 import { debounce } from 'lodash-es';
100 105
 import MescrollMixin from '@/uni_modules/mescroll/components/mescroll-uni/mescroll-mixins.js'
101 106
 import { myProcessList, stopProcess } from "@/api/flowable/process";
102
-import FlowRecord from '../../components/flowable/FlowRecord.vue';
107
+import FlowRecord from '@/pages/components/flowable/FlowRecord.vue';
108
+import { getProcessVariables } from "@/api/flowable/definition";
109
+import { getDeployment, delDeployment, addDeployment, updateDeployment, exportDeployment, flowRecord } from "@/api/flowable/finished";
110
+import { deleteResources } from "@/utils/deleteResource";
103 111
 export default {
104 112
   components: { FlowRecord },
105 113
   mixins: [MescrollMixin],
106 114
   data() {
107 115
     return {
116
+      activeTabs: 0,
117
+      mescroll: null, // mescroll实例
108 118
       // 我发起的流程列表数据
109 119
       myProcessList: [],
110
-      list: [], // 列表数据
120
+      total: 0,
111 121
       queryParams: {
112 122
         keyword: '',
113 123
         pageNum: 1,
@@ -116,21 +126,48 @@ export default {
116 126
       selectedItem: null, // 当前选中项
117 127
       debounceSearch: null, // 初始化防抖方法
118 128
       clickRow: {},
119
-      active: 'record'
129
+      active: 'record',
130
+      downOption: {
131
+        auto: false, // 手动初始化后需要关闭自动加载
132
+        textOutOffset: '下拉刷新',
133
+        textLoading: '加载中...'
134
+      },
135
+      upOption: {
136
+        auto: true, // 自动加载初始页
137
+        page: { num: 1, size: 10 }, // 初始页码和每页数量
138
+        noMoreSize: 5, // 数据不足时显示"没有更多"
139
+        empty: { tip: '暂无待审任务' },
140
+        textNoMore: '~ 没有更多任务了 ~'
141
+      }
120 142
     }
121 143
   },
122 144
   created() {
123 145
     // 在生命周期中创建防抖方法
124
-    this.debounceSearch = debounce(() => {
125
-      this.search()
126
-    }, 500)
146
+    // this.debounceSearch = debounce(() => {
147
+    //   this.search()
148
+    // }, 500)
149
+    // this.getMyprogressList();
150
+  },
151
+  onLoad: function (options) {
152
+    uni.startPullDownRefresh();
153
+  },
154
+  mounted() {
127 155
   },
128 156
   methods: {
157
+    switchTabs(item) {
158
+      this.activeTabs = item.index;
159
+      this.queryParams.finishTime
160
+    },
161
+    // 初始化mescroll
162
+    mescrollInit(mescroll) {
163
+      this.mescroll = mescroll;
164
+    },
129 165
     // mescroll下拉刷新回调
130 166
     async downCallback() {
167
+      this.myProcessList = []; // 清空列表
131 168
       this.queryParams.pageNum = 1
132 169
       const res = await this.loadData()
133
-      this.list = res.data.list;
170
+      this.myProcessList = res.data.list;
134 171
       this.mescroll.endSuccess(res.data.length)
135 172
       this.mescroll.endErr()
136 173
     },
@@ -138,7 +175,7 @@ export default {
138 175
     async upCallback(page) {
139 176
       this.queryParams.pageNum = page.num
140 177
       const res = await this.loadData()
141
-      this.list = this.list.concat(res.data.list)
178
+      this.myProcessList = this.myProcessList.concat(res.data.list)
142 179
       this.mescroll.endSuccess(res.data.list.length, res.data.hasNextPage)
143 180
     },
144 181
     // 加载数据
@@ -156,6 +193,16 @@ export default {
156 193
         return { data: { list: [], hasNextPage: false } }
157 194
       }
158 195
     },
196
+    getMyprogressList() {
197
+      myProcessList({
198
+        pageNum: 1,
199
+        pageSize: 10,
200
+      }).then(response => {
201
+        this.total = response.data.total;
202
+        this.myProcessList = response.data.records;
203
+        uni.stopPullDownRefresh();
204
+      });
205
+    },
159 206
     // 显示操作菜单
160 207
     showAction(item) {
161 208
       this.selectedItem = item
@@ -173,12 +220,10 @@ export default {
173 220
     },
174 221
 
175 222
     handleFlowRecord(row) {
176
-      console.log(row);
177 223
       this.active = 'record';
178 224
       this.$refs.drawer.open();
179 225
     },
180 226
     handleFlowNote(row) {
181
-      console.log(row);
182 227
       const query = {
183 228
         procInsId: row.procInsId,
184 229
         executionId: row.executionId,
@@ -192,9 +237,41 @@ export default {
192 237
       uni.navigateTo({
193 238
         url: `/pages/message/apply/detail?params=${encodedParams}`
194 239
       })
240
+      this.$refs.actionPopup.close();
241
+    },
242
+    handleDelete(row) {
243
+      const ids = row.procInsId || this.ids;// 暂不支持删除多个流程
244
+      // 显示确认提交对话框
245
+      uni.showModal({
246
+        title: '提示',
247
+        content: '删除流程后不可撤回,确认删除该流程吗?',
248
+        success: (res) => {
249
+          if (!res.confirm) {
250
+            return;
251
+          }
252
+          getProcessVariables(row.taskId).then(res => {
253
+            if (res.data) {
254
+              let delId = res.data.formId;
255
+              deleteResources(row.procDefName, delId)
256
+            }
257
+            return delDeployment(ids);
258
+          }).then(() => {
259
+            this.upCallback();
260
+            this.$modal.msgSuccess("流程取消成功");
261
+            this.$refs.actionPopup.close()
262
+          })
263
+        }
264
+      });
265
+    },
266
+    beDeleted(row) {
267
+      if (row.procDefName == '项目预算' || row.procDefName == '技术方案' || row.procDefName == '安全交底' || row.procDefName == '技术交底' || row.finishTime != null) {
268
+        return true
269
+      } else if (row.procDefName == '项目流转' && row.taskName != '项目登记') {
270
+        return true
271
+      } else {
272
+        return false
273
+      }
195 274
     },
196
-    handleDelete(row) { /* ... */ },
197
-    beDeleted(row) { /* ... */ },
198 275
   },
199 276
 }
200 277
 </script>

+ 186
- 0
oa-ui-app/utils/deleteResource.js Voir le fichier

@@ -0,0 +1,186 @@
1
+/*
2
+ * @Author: ysh
3
+ * @Date: 2024-06-13 17:07:59
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-03-28 15:17:32
6
+ */
7
+import request from '@/utils/request'
8
+
9
+const apiEndpoints = [
10
+  {
11
+    procDefName: '承接合同评审',
12
+    apiUrl: [
13
+      '/oa/contract/:id',
14
+      '/oa/contractWork/:id',
15
+      '/oa/contractComment/:id',
16
+      '/oa/contractMeeting/:id',
17
+      '/oa/contractPayment/:id',
18
+      '/oa/projectContract/:id',
19
+      '/oa/contractSubcontract/contractIds/:id',
20
+    ]
21
+  },
22
+  {
23
+    procDefName: '分包合同评审',
24
+    apiUrl: [
25
+      '/oa/subContract/:id',
26
+      '/oa/contractWork/:id',
27
+      '/oa/contractComment/:id',
28
+      '/oa/contractMeeting/:id',
29
+      '/oa/contractPayment/:id',
30
+      '/oa/projectSubcontract/:id',
31
+      '/oa/contractSubcontract/:id',
32
+    ]
33
+  },
34
+  {
35
+    procDefName: '用车审批',
36
+    apiUrl: [
37
+      '/oa/carApproval/:id',
38
+    ]
39
+  },
40
+  {
41
+    procDefName: '设备审批',
42
+    apiUrl: [
43
+      '/oa/deviceApproval/:id',
44
+    ]
45
+  },
46
+  {
47
+    procDefName: '项目流转',
48
+    apiUrl: [
49
+      '/oa/project/:id',
50
+      '/oa/projectWork/:id',
51
+      '/oa/projectContract/delete/:id'
52
+    ]
53
+  },
54
+  {
55
+    procDefName: '项目结算',
56
+    apiUrl: [
57
+      '/oa/settle/:id',
58
+      '/oa/settleSummary/:id',
59
+      '/oa/settleWork/:id',
60
+    ]
61
+  },
62
+  {
63
+    procDefName: '借款审批',
64
+    apiUrl: [
65
+      '/oa/borrow/:id',
66
+      '/oa/borrowDetail/:id',
67
+    ]
68
+  },
69
+  {
70
+    procDefName: '项目变更',
71
+    apiUrl: [
72
+      '/oa/projectChange/:id',
73
+    ]
74
+  },
75
+  {
76
+    procDefName: '成果归档',
77
+    apiUrl: [
78
+      '/oa/archive/:id',
79
+    ]
80
+  },
81
+  {
82
+    procDefName: '其他结算',
83
+    apiUrl: [
84
+      '/oa/settle/:id',
85
+      '/oa/settleSummary/:id',
86
+      '/oa/settleWork/:id',
87
+    ]
88
+  },
89
+  {
90
+    procDefName: '品牌项目支付',
91
+    apiUrl: [
92
+      '/oa/brand/:id',
93
+      '/oa/brandPayment/:id'
94
+    ]
95
+  },
96
+  {
97
+    procDefName: '工作填报',
98
+    apiUrl: [
99
+      '/oa/declare/:id'
100
+    ]
101
+  },
102
+  {
103
+    procDefName: '采购审批',
104
+    apiUrl: [
105
+      '/oa/procureApproval/:id',
106
+      '/oa/procurePlan/:id'
107
+    ]
108
+  },
109
+  {
110
+    procDefName: '项目委外',
111
+    apiUrl: [
112
+      '/oa/outsource/:id',
113
+      '/oa/outsourceWork/:id'
114
+    ]
115
+  },
116
+  {
117
+    procDefName: '绩效审批',
118
+    apiUrl: [
119
+      '/oa/performance/:id',
120
+      '/oa/wage/batch/:id'
121
+    ]
122
+  },
123
+  {
124
+    procDefName: '项目预算',
125
+    apiUrl: [
126
+      '/oa/budget/:id',
127
+      '/oa/budgetSettle/:id',
128
+      '/oa/budgetCar/:id',
129
+      '/oa/budgetDevice/:id',
130
+      '/oa/budgetStaff/:id',
131
+    ]
132
+  },
133
+  {
134
+    procDefName: '技术方案',
135
+    apiUrl: [
136
+      '/oa/technical/:id',
137
+    ]
138
+  },
139
+  {
140
+    procDefName: '技术交底',
141
+    apiUrl: [
142
+      '/oa/technicalPlan/:id',
143
+    ]
144
+  },
145
+  {
146
+    procDefName: '安全交底',
147
+    apiUrl: [
148
+      '/oa/safe/:id',
149
+    ]
150
+  },
151
+  {
152
+    procDefName: '参培审核',
153
+    apiUrl: [
154
+      '/oa/trainApproval/:id',
155
+    ]
156
+  },
157
+]
158
+
159
+// 编写一个方法来处理删除请求,并同时发送所有API请求  
160
+export async function deleteResources(procDefName, id) {
161
+  // 查找对应的API端点  
162
+  const endpoint = apiEndpoints.find(endpoint => endpoint.procDefName === procDefName);
163
+  if (!endpoint) {
164
+    throw new Error(`No API endpoints found for process definition: ${procDefName}`);
165
+  }
166
+
167
+  // 构建所有请求的Promise数组  
168
+  const deletePromises = endpoint.apiUrl.map(apiUrl => {
169
+    // 替换URL中的:id占位符  
170
+    const url = apiUrl.replace(':id', id);
171
+    // 发送DELETE请求并返回Promise  
172
+    console.log(request.delete);
173
+    return request({
174
+      url: url,
175
+      method: 'delete'
176
+    })
177
+  });
178
+
179
+  // 等待所有请求完成  
180
+  try {
181
+    return await Promise.all(deletePromises);
182
+    console.log('删除完成!');
183
+  } catch (error) {
184
+    console.error('One or more deletion requests failed:', error);
185
+  }
186
+} 

Loading…
Annuler
Enregistrer