Sfoglia il codice sorgente

更新移动端用车审批、已办任务、项目列表

余思翰 2 settimane fa
parent
commit
6ab62d6e48

+ 60
- 0
oa-ui-app/api/system/dept.js Vedi File

@@ -0,0 +1,60 @@
1
+import request from '@/utils/request'
2
+
3
+// 查询部门列表
4
+export function listDept(query) {
5
+  return request({
6
+    url: '/system/dept/list',
7
+    method: 'get',
8
+    params: query
9
+  })
10
+}
11
+
12
+// 查询部门下拉树结构
13
+export function treeselect() {
14
+  return request({
15
+    url: '/system/dept/treeselect',
16
+    method: 'get'
17
+  })
18
+}
19
+
20
+// 查询部门列表(排除节点)
21
+export function listDeptExcludeChild(deptId) {
22
+  return request({
23
+    url: '/system/dept/list/exclude/' + deptId,
24
+    method: 'get'
25
+  })
26
+}
27
+
28
+// 查询部门详细
29
+export function getDept(deptId) {
30
+  return request({
31
+    url: '/system/dept/' + deptId,
32
+    method: 'get'
33
+  })
34
+}
35
+
36
+// 新增部门
37
+export function addDept(data) {
38
+  return request({
39
+    url: '/system/dept',
40
+    method: 'post',
41
+    data: data
42
+  })
43
+}
44
+
45
+// 修改部门
46
+export function updateDept(data) {
47
+  return request({
48
+    url: '/system/dept',
49
+    method: 'put',
50
+    data: data
51
+  })
52
+}
53
+
54
+// 删除部门
55
+export function delDept(deptId) {
56
+  return request({
57
+    url: '/system/dept/' + deptId,
58
+    method: 'delete'
59
+  })
60
+}

+ 45
- 0
oa-ui-app/api/system/user.js Vedi File

@@ -2,6 +2,51 @@ import upload from '@/utils/upload'
2 2
 import request from '@/utils/request'
3 3
 import { parseStrEmpty } from '@/utils/common.js'
4 4
 
5
+
6
+// 查询用户列表
7
+export function listUser(query) {
8
+  return request({
9
+    url: '/system/user/list',
10
+    method: 'get',
11
+    params: query
12
+  })
13
+}
14
+
15
+// 查询用户变更信息
16
+export function getUserChangeInfo(query) {
17
+  return request({
18
+    url: '/system/user/change',
19
+    method: 'get',
20
+    params: query
21
+  })
22
+}
23
+
24
+// 新增用户
25
+export function addUser(data) {
26
+  return request({
27
+    url: '/system/user',
28
+    method: 'post',
29
+    data: data
30
+  })
31
+}
32
+
33
+// 修改用户
34
+export function updateUser(data) {
35
+  return request({
36
+    url: '/system/user',
37
+    method: 'put',
38
+    data: data
39
+  })
40
+}
41
+
42
+// 删除用户
43
+export function delUser(userId) {
44
+  return request({
45
+    url: '/system/user/' + userId,
46
+    method: 'delete'
47
+  })
48
+}
49
+
5 50
 // 用户密码重置
6 51
 export function updateUserPwd(oldPassword, newPassword) {
7 52
   const data = {

+ 1
- 1
oa-ui-app/pages.json Vedi File

@@ -13,7 +13,7 @@
13 13
 		}, {
14 14
 			"path": "pages/index",
15 15
 			"style": {
16
-				"navigationBarTitleText": "CMC综合办公系统移动端",
16
+				"navigationBarTitleText": "CMC智联云枢办公系统移动端",
17 17
 				"navigationStyle": "custom"
18 18
 			}
19 19
 		}, {

+ 252
- 0
oa-ui-app/pages/components/ChoosePeople.vue Vedi File

@@ -0,0 +1,252 @@
1
+<template>
2
+  <view class="choose-people">
3
+    <view class="search-form">
4
+      <view class="search-item">
5
+        <text class="label">姓名:</text>
6
+        <uv-input v-model="queryParams.nickName" placeholder="请输入姓名" />
7
+      </view>
8
+      <view class="search-item">
9
+        <text class="label">部门:</text>
10
+        <uni-data-select style="width:270rpx;margin-right:30rpx;" v-model="queryParams.deptId" :localdata="deptList"
11
+          @change="getList" clearable />
12
+      </view>
13
+      <view class="search-item">
14
+        <u-button type="primary" @click="getList">搜索</u-button>
15
+      </view>
16
+    </view>
17
+
18
+    <!-- 选择人员 -->
19
+    <scroll-view scroll-y class="user-list" :style="{ height: '35vh' }">
20
+      <uv-checkbox-group v-model="selectedIds" iconPlacement="right" placement="column"
21
+        @change="(value) => handleCheckboxChange(value)">
22
+        <uv-checkbox v-for="item in userList" :label="item.nickName" :name="item.userId" :disabled="item.status == 1" />
23
+      </uv-checkbox-group>
24
+    </scroll-view>
25
+    
26
+    <view class="selected-users" v-if="multiple">
27
+      <text class="label">已选人员:</text>
28
+      <view class="tags">
29
+        <u-tag v-for="item in chooseUser" :key="item.userId" :text="item.nickName" style="margin: 5px;" />
30
+      </view>
31
+    </view>
32
+
33
+    <view class="selected-users" v-else>
34
+      <text class="label">已选人员:</text>
35
+      <u-tag v-if="chooseUser.nickName" :text="chooseUser.nickName" style="margin: 5px;" />
36
+    </view>
37
+
38
+    <view class="action-buttons">
39
+      <u-button type="primary" @click="confirmChoose">确认选择</u-button>
40
+      <u-button @click="clearChoose">清空选择</u-button>
41
+    </view>
42
+  </view>
43
+</template>
44
+
45
+<script>
46
+import { listUser } from "@/api/system/user";
47
+import { listDept } from "@/api/system/dept";
48
+
49
+export default {
50
+  name: 'ChoosePeople',
51
+  props: {
52
+    deptId: {
53
+      type: Number
54
+    },
55
+    multiple: {
56
+      type: Boolean,
57
+      default: true
58
+    },
59
+    selected: {
60
+      type: Array,
61
+      default: () => []
62
+    }
63
+  },
64
+  data() {
65
+    return {
66
+      queryParams: {
67
+        pageNum: 1,
68
+        pageSize: 100,
69
+        nickName: '',
70
+        deptId: undefined
71
+      },
72
+      userList: [],
73
+      deptList: [],
74
+      chooseUser: this.selected,
75
+      total: 0,
76
+      selectedIds: [],
77
+      allSelectedUsers: []
78
+    }
79
+  },
80
+  created() {
81
+    this.queryParams.deptId = this.deptId;
82
+    this.getList();
83
+    this.getDeptList();
84
+    this.selectedIds = this.selected.map(user => user.userId);
85
+    this.allSelectedUsers = [...this.selected];
86
+  },
87
+  methods: {
88
+    getList() {
89
+      listUser(this.queryParams).then(response => {
90
+        this.userList = response.rows.filter(item => item.status != '1' && item.status != '2');
91
+        this.total = response.total;
92
+        // 更新当前页面的选中状态,保持所有已选用户
93
+        this.selectedIds = this.userList
94
+          .filter(item => this.allSelectedUsers.some(selected => selected.userId === item.userId))
95
+          .map(item => item.userId);
96
+        this.updateSelectedUsers();
97
+      });
98
+    },
99
+    getDeptList() {
100
+      listDept().then(res => {
101
+        this.deptList = res.data
102
+          .filter(item => item.deptName != '四川中水成勘院测绘工程有限责任公司')
103
+          .map(item => ({
104
+            text: item.deptName,
105
+            value: item.deptId
106
+          }));
107
+      });
108
+    },
109
+    updateSelectedUsers() {
110
+      // 合并当前页面选中的用户和其他页面的已选用户
111
+      const currentPageUsers = this.userList.filter(item => this.selectedIds.includes(item.userId));
112
+      const otherPageUsers = this.allSelectedUsers.filter(user =>
113
+        !this.userList.some(item => item.userId === user.userId)
114
+      );
115
+      this.chooseUser = [...currentPageUsers, ...otherPageUsers];
116
+    },
117
+    isSelected(user) {
118
+      return this.selectedIds.includes(user.userId);
119
+    },
120
+    confirmChoose() {
121
+      this.$emit('chooseUser', this.chooseUser);
122
+    },
123
+    clearChoose() {
124
+      this.selectedIds = [];
125
+      this.allSelectedUsers = [];
126
+      this.chooseUser = this.multiple ? [] : {};
127
+      this.$emit('clear');
128
+    },
129
+    getAgeByIdCard(idCard) {
130
+      if (!idCard) return '';
131
+      const yearBirth = idCard.substring(6, 10);
132
+      const monthBirth = idCard.substring(10, 12);
133
+      const dayBirth = idCard.substring(12, 14);
134
+      const now = new Date();
135
+      const monthNow = now.getMonth() + 1;
136
+      const dayNow = now.getDate();
137
+      let age = now.getFullYear() - yearBirth;
138
+      if (monthNow < monthBirth || (monthNow == monthBirth && dayNow < dayBirth)) {
139
+        age--;
140
+      }
141
+      return age;
142
+    },
143
+    handleCheckboxChange(selectedNames) {
144
+      if (this.multiple) {
145
+        this.selectedIds = selectedNames;
146
+        // 更新当前页面的选中用户
147
+        const currentPageSelectedUsers = this.userList.filter(user => selectedNames.includes(user.userId));
148
+        // 获取其他页面的已选用户
149
+        const otherPageUsers = this.allSelectedUsers.filter(user =>
150
+          !this.userList.some(item => item.userId === user.userId)
151
+        );
152
+        // 更新所有已选用户列表
153
+        this.allSelectedUsers = [...currentPageSelectedUsers, ...otherPageUsers];
154
+        this.updateSelectedUsers();
155
+      } else {
156
+        const lastSelectedId = selectedNames[selectedNames.length - 1];
157
+        this.selectedIds = lastSelectedId ? [lastSelectedId] : [];
158
+        this.allSelectedUsers = lastSelectedId ? [this.userList.find(user => user.userId === lastSelectedId)] : [];
159
+        this.updateSelectedUsers();
160
+        if (lastSelectedId) {
161
+          this.confirmChoose();
162
+        }
163
+      }
164
+    }
165
+  }
166
+}
167
+</script>
168
+
169
+<style lang="scss" scoped>
170
+.choose-people {
171
+  padding: 20rpx;
172
+
173
+  .search-form {
174
+    display: flex;
175
+    flex-wrap: wrap;
176
+    gap: 20rpx;
177
+
178
+    .search-item {
179
+      display: flex;
180
+      align-items: center;
181
+      gap: 10rpx;
182
+
183
+      .label {
184
+        font-size: 28rpx;
185
+        color: #333;
186
+      }
187
+    }
188
+  }
189
+
190
+  .user-list {
191
+    margin-bottom: 30rpx;
192
+    background-color: #fff;
193
+    border-radius: 12rpx;
194
+    padding: 20rpx;
195
+
196
+    ::v-deep .uv-checkbox {
197
+      margin: 20rpx 0;
198
+      padding: 20rpx;
199
+      border-radius: 8rpx;
200
+      background-color: #f8f8f8;
201
+      transition: all 0.3s;
202
+
203
+      &:active {
204
+        background-color: #f0f0f0;
205
+      }
206
+
207
+      &.uv-checkbox--checked {
208
+        background-color: #e6f7ff;
209
+        border-color: #1890ff;
210
+        box-shadow: 0 2rpx 8rpx rgba(24, 144, 255, 0.15);
211
+      }
212
+
213
+      .uv-checkbox__label {
214
+        font-size: 28rpx;
215
+        color: #333;
216
+      }
217
+    }
218
+  }
219
+
220
+  .pagination {
221
+    display: flex;
222
+    justify-content: center;
223
+    margin: 20rpx 0;
224
+  }
225
+
226
+  .selected-users {
227
+    margin: 20rpx 0;
228
+    display: flex;
229
+    align-items: center;
230
+    flex-wrap: wrap;
231
+    gap: 10rpx;
232
+
233
+    .label {
234
+      font-size: 28rpx;
235
+      color: #333;
236
+    }
237
+
238
+    .tags {
239
+      display: flex;
240
+      flex-wrap: wrap;
241
+      gap: 10rpx;
242
+    }
243
+  }
244
+
245
+  .action-buttons {
246
+    display: flex;
247
+    justify-content: center;
248
+    gap: 20rpx;
249
+    margin-top: 30rpx;
250
+  }
251
+}
252
+</style>

+ 729
- 8
oa-ui-app/pages/form/car/car.vue Vedi File

@@ -34,7 +34,145 @@
34 34
         <ProjectPicker :visible.sync="openProject" :selected.sync="selectedProject" @confirm="handleConfirm" />
35 35
         <ProjectInfo :project="projectObj"></ProjectInfo>
36 36
       </uni-forms-item>
37
+
38
+      <!-- 用车事由 -->
39
+      <uni-forms-item label="用车事由" required class="form-item" name="applyReason">
40
+        <uv-textarea v-model="form.applyReason" placeholder="请输入用车事由" :disabled="taskName != '用车申请'"></uv-textarea>
41
+      </uni-forms-item>
42
+
43
+      <!-- 用车时间 -->
44
+      <view class="date-range">
45
+        <uni-forms-item label="使用日期" required class="form-item" name="dateRange">
46
+          <uni-datetime-picker v-model="form.dateRange" type="daterange" rangeSeparator="至" :clearIcon="false"
47
+            v-if="taskName == '用车申请'" @change="handleDateRangeChange" />
48
+          <view class="date-display" v-else>
49
+            <uni-icons type="calendar" size="16"></uni-icons>
50
+            <text class="date-text">{{ form.beginDate }}</text>
51
+            <text class="date-separator">至</text>
52
+            <text class="date-text">{{ form.endDate }}</text>
53
+          </view>
54
+        </uni-forms-item>
55
+        <uni-forms-item label="共计" class="form-item">
56
+          <text>{{ form.days + '天' }}</text>
57
+        </uni-forms-item>
58
+      </view>
59
+
60
+      <!-- 乘车人数 -->
61
+      <uni-forms-item label="乘车人数" required class="form-item" name="passengers">
62
+        <uni-number-box v-model="form.passengers" :min="1" :disabled="taskName != '用车申请'" />
63
+      </uni-forms-item>
64
+
65
+      <view v-if="taskName != '用车申请'">
66
+        <!-- 部门审核意见 -->
67
+        <uni-forms-item label="部门审核意见" required class="form-item" v-if="showFormItem('部门审核')" name="deptComment">
68
+          <uv-textarea v-model="form.deptComment" placeholder="请输入部门审核意见" :disabled="taskName != '部门审核'"></uv-textarea>
69
+          <auditor :name="form.deptUser ? form.deptUser.nickName : ''" :time="form.deptTime"></auditor>
70
+        </uni-forms-item>
71
+
72
+        <!-- 分管审核意见 -->
73
+        <uni-forms-item label="分管审核意见" required class="form-item" v-if="showFormItem('分管审核')" name="managerComment">
74
+          <uv-textarea v-model="form.managerComment" placeholder="请输入分管审核意见"
75
+            :disabled="taskName != '分管审核'"></uv-textarea>
76
+          <auditor :name="form.managerUser ? form.managerUser.nickName : ''" :time="form.managerTime"></auditor>
77
+        </uni-forms-item>
78
+
79
+        <!-- 党工团审核意见 -->
80
+        <uni-forms-item :label="dgtAdvice" required class="form-item" v-if="showFormItem('党工团审核')" name="unionComment">
81
+          <uv-textarea v-model="form.unionComment" placeholder="请输入审核意见" :disabled="taskName != '党工团审核'"></uv-textarea>
82
+          <auditor :name="form.unionUser ? form.unionUser.nickName : ''" :time="form.unionTime"></auditor>
83
+        </uni-forms-item>
84
+
85
+        <!-- 总经理审核意见 -->
86
+        <uni-forms-item label="总经理审核意见" required class="form-item" v-if="showFormItem('总经理审核')" name="gmComment">
87
+          <uv-textarea v-model="form.gmComment" placeholder="请输入总经理审核意见" :disabled="taskName != '总经理审核'"></uv-textarea>
88
+          <auditor :name="form.gmUser ? form.gmUser.nickName : ''" :time="form.gmTime"></auditor>
89
+        </uni-forms-item>
90
+
91
+        <!-- 董事长审核意见 -->
92
+        <uni-forms-item label="董事长审核意见" required class="form-item" v-if="showFormItem('董事长审核')" name="dszComment">
93
+          <uv-textarea v-model="form.dszComment" placeholder="请输入董事长审核意见" :disabled="taskName != '董事长审核'"></uv-textarea>
94
+          <auditor :name="form.dszUser ? form.dszUser.nickName : ''" :time="form.dszTime"></auditor>
95
+        </uni-forms-item>
96
+      </view>
97
+      <view v-if="taskName == '' || taskName == '安排用车'">
98
+        <!-- 安排用车意见 -->
99
+        <uni-forms-item label="安排用车意见" required class="form-item" name="dispatchComment">
100
+          <uv-textarea v-model="form.dispatchComment" placeholder="请输入安排用车意见"
101
+            :disabled="taskName != '安排用车'"></uv-textarea>
102
+          <auditor :name="form.dispatchUser ? form.dispatchUser.nickName : ''" :time="form.dispatchTime"></auditor>
103
+        </uni-forms-item>
104
+
105
+        <!-- 车牌号选择 -->
106
+        <uni-forms-item label="车牌号" class="form-item" name="cars">
107
+          <view class="car-list">
108
+            <uv-checkbox-group v-model="form.cars" v-if="taskName == '安排用车'">
109
+              <view class="car-row" v-for="(row, index) in Math.ceil(carList.length / 2)" :key="index">
110
+                <view class="car-item" v-for="item in carList.slice(index * 2, (index + 1) * 2)" :key="item.carId"
111
+                  :class="{ 'is-checked': form.cars.includes(item.carId) }">
112
+                  <uv-checkbox :name="item.carId" :disabled="item.status != 1">
113
+                    <view class="car-info">
114
+                      <text class="car-license">{{ item.licensePlate }}</text>
115
+                      <text class="car-model">{{ item.brand || '' }}{{ item.series || '' }}</text>
116
+                    </view>
117
+                  </uv-checkbox>
118
+                </view>
119
+              </view>
120
+            </uv-checkbox-group>
121
+            <view class="car-row" v-for="(row, index) in Math.ceil(carList.length / 2)" :key="index" v-else>
122
+              <view class="car-item" v-for="item in carList.slice(index * 2, (index + 1) * 2)" :key="item.carId"
123
+                :class="{ 'is-checked': form.cars.includes(item.carId) }">
124
+                <text class="car-license">{{ item.licensePlate }}</text>
125
+                <text class="car-model">{{ item.brand || '' }}{{ item.series || '' }}</text>
126
+              </view>
127
+            </view>
128
+          </view>
129
+        </uni-forms-item>
130
+
131
+        <!-- 驾驶员选择 -->
132
+        <uni-forms-item label="驾驶员" class="form-item" name="drivers">
133
+          <u-button type="primary" v-if="taskName == '安排用车'" @click="openCar = true">+ 选择人员</u-button>
134
+          <view class="driver-list" v-if="chooseDriver.length > 0">
135
+            <u-tag v-for="(item, index) in chooseDriver" :plain="true" :key="index" :text="item.nickName"
136
+              style="margin: 5px;" />
137
+          </view>
138
+        </uni-forms-item>
139
+
140
+        <!-- 返回日期和行驶公里 -->
141
+        <uni-forms-item label="返回日期" required class="form-item" name="returnDate">
142
+          <uni-datetime-picker v-model="form.returnDate" type="datetime" :disabled="taskName != '安排用车'" />
143
+        </uni-forms-item>
144
+
145
+        <uni-forms-item label="行驶公里" class="form-item" name="kilometers">
146
+          <uv-input v-model="form.kilometers" placeholder="请输入行驶公里" :disabled="taskName != '安排用车'"
147
+            @input="handleKilometersInput">
148
+            <template v-slot:suffix>
149
+              <text>公里</text>
150
+            </template>
151
+          </uv-input>
152
+        </uni-forms-item>
153
+      </view>
154
+
155
+      <!-- 提交按钮 -->
156
+      <view class="submit-btn" v-if="taskName == '用车申请'">
157
+        <u-button type="primary" @click="submit">提交申请</u-button>
158
+      </view>
159
+      <view v-else>
160
+        <u-button style="margin-bottom:5px;" type="warning" @click="saves" v-if="taskName == '安排用车'">保存</u-button>
161
+        <u-button type="primary" @click="completeApply"
162
+          :disabled="taskName == '安排用车' && !form.returnDate" v-if="taskName">完成审批</u-button>
163
+      </view>
37 164
     </uni-forms>
165
+
166
+    <!-- 选择人员弹窗 -->
167
+    <u-popup :show="openCar" mode="center" round="10" @close="openCar = false">
168
+      <view class="popup-content">
169
+        <view class="popup-header">
170
+          <text class="title">选择驾驶员</text>
171
+          <u-icon name="close" @click="openCar = false"></u-icon>
172
+        </view>
173
+        <choose-people :multiple="true" @chooseUser="getChooseDriver" :deptId="200" @clear="clearChooseDriver" :selected="chooseDriver"></choose-people>
174
+      </view>
175
+    </u-popup>
38 176
   </view>
39 177
 </template>
40 178
 
@@ -45,17 +183,21 @@ import { listCar, getCar } from "@/api/oa/car/car";
45 183
 import { listCarApproval, getCarApproval, updateCarApproval, addCarApproval, modifyCarApproval } from '@/api/oa/car/carApproval'
46 184
 import { listProject, getProject } from "@/api/oa/project/project";
47 185
 import { getUserByRole } from "@/api/system/role";
48
-import { getUsersManageLeader } from '@/api/system/post.js'
186
+import { getUsersManageLeader, getUserByPost, getUsersDeptLeader } from '@/api/system/post.js'
187
+import { getUser } from '@/api/system/user';
49 188
 import FlowNote from '@/pages/components/flowNote.vue';
50 189
 import ProjectPicker from '@/pages/components/ProjectPicker.vue';
51 190
 import ProjectInfo from '@/pages/components/ProjectInfo.vue';
52 191
 import Auditor from "@/pages/components/auditor.vue";
192
+import ChoosePeople from "@/pages/components/ChoosePeople.vue";
193
+
53 194
 export default {
54 195
   components: {
55 196
     FlowNote,
56 197
     ProjectPicker,
57 198
     ProjectInfo,
58
-    Auditor
199
+    Auditor,
200
+    ChoosePeople,
59 201
   },
60 202
   props: {
61 203
     taskForm: Object,
@@ -67,7 +209,30 @@ export default {
67 209
       form: {
68 210
         applyDate: '',
69 211
         projectId: '',
70
-        carUsage:'0',
212
+        carUsage: '0',
213
+        applyReason: '',
214
+        beginDate: '',
215
+        endDate: '',
216
+        days: 0,
217
+        passengers: 1,
218
+        deptComment: null,
219
+        managerComment: null,
220
+        unionComment: null,
221
+        gmComment: null,
222
+        dszComment: null,
223
+        dispatchComment: null,
224
+        cars: [],
225
+        drivers: [],
226
+        returnDate: '',
227
+        kilometers: '',
228
+        user: {
229
+          nickName: '',
230
+        },
231
+        dept: {
232
+          deptId: '',
233
+          deptName: '',
234
+        },
235
+        applier: '',
71 236
       },
72 237
       rules: {},
73 238
       openProject: false,
@@ -95,21 +260,192 @@ export default {
95 260
         disable: false
96 261
       }],
97 262
       dept: 0,
263
+      deptUser: '',
264
+      managerUser: '',
265
+      unionUser: '',
266
+      gmUser: '',
267
+      dszUser: '',
268
+      dispatchUser: '',
269
+      deptTime: undefined,
270
+      managerTime: undefined,
271
+      unionTime: undefined,
272
+      gmTime: undefined,
273
+      dszTime: undefined,
274
+      dispatchTime: undefined,
275
+      carList: [],
276
+      chooseDriver: [],
277
+      openCar: false,
278
+      dgtAdvice: '审核意见',
279
+    }
280
+  },
281
+  computed: {
282
+    formRules() {
283
+      return {
284
+        carUsage: {
285
+          rules: [{
286
+            required: this.taskName == '用车申请',
287
+            errorMessage: '请选择申请用途'
288
+          }]
289
+        },
290
+        projectId: {
291
+          rules: [{
292
+            required: this.taskName == '用车申请' && this.form.carUsage == '0',
293
+            errorMessage: '请选择项目'
294
+          }]
295
+        },
296
+        applyReason: {
297
+          rules: [{
298
+            required: this.taskName == '用车申请',
299
+            errorMessage: '请输入用车事由'
300
+          }]
301
+        },
302
+        dateRange: {
303
+          rules: [{
304
+            required: this.taskName == '用车申请',
305
+            errorMessage: '请选择用车时间'
306
+          }]
307
+        },
308
+        passengers: {
309
+          rules: [{
310
+            required: this.taskName == '用车申请',
311
+            errorMessage: '请输入乘车人数'
312
+          }]
313
+        },
314
+        deptComment: {
315
+          rules: [{
316
+            required: this.taskName == '部门审核',
317
+            errorMessage: '请输入部门审核意见'
318
+          }]
319
+        },
320
+        managerComment: {
321
+          rules: [{
322
+            required: this.taskName == '分管审核',
323
+            errorMessage: '请输入分管审核意见'
324
+          }]
325
+        },
326
+        unionComment: {
327
+          rules: [{
328
+            required: this.taskName == '党工团审核',
329
+            errorMessage: '请输入审核意见'
330
+          }]
331
+        },
332
+        gmComment: {
333
+          rules: [{
334
+            required: this.taskName == '总经理审核',
335
+            errorMessage: '请输入总经理审核意见'
336
+          }]
337
+        },
338
+        dszComment: {
339
+          rules: [{
340
+            required: this.taskName == '董事长审核',
341
+            errorMessage: '请输入董事长审核意见'
342
+          }]
343
+        },
344
+        dispatchComment: {
345
+          rules: [{
346
+            required: this.taskName == '安排用车',
347
+            errorMessage: '请输入安排用车意见'
348
+          }]
349
+        },
350
+        cars: {
351
+          rules: [{
352
+            required: this.taskName == '安排用车',
353
+            errorMessage: '请选择车牌号'
354
+          }]
355
+        },
356
+        drivers: {
357
+          rules: [{
358
+            required: this.taskName == '安排用车',
359
+            errorMessage: '请选择驾驶员'
360
+          }]
361
+        },
362
+        returnDate: {
363
+          rules: [{
364
+            required: true,
365
+            errorMessage: '请选择归还日期',
366
+            validateFunction: (rule, value, data, callback) => {
367
+              if (this.taskName === '安排用车' && !value) {
368
+                callback('请选择归还日期');
369
+              } else {
370
+                callback();
371
+              }
372
+            }
373
+          }]
374
+        },
375
+        kilometers: {
376
+          rules: [{
377
+            required: this.taskName == '安排用车',
378
+            errorMessage: '请输入行驶公里'
379
+          }]
380
+        }
381
+      }
382
+    }
383
+  },
384
+  watch: {
385
+    formRules: {
386
+      handler(newVal) {
387
+        this.rules = newVal;
388
+      },
389
+      immediate: true
98 390
     }
99 391
   },
100 392
   created() {
101 393
     this.applierUserName = this.startUserName;
102 394
     this.initForm();
395
+    this.getCarList();
396
+    this.getDriverList();
103 397
   },
104 398
   methods: {
105 399
     initForm() {
106 400
       getCarApproval(this.taskForm.formId).then(res => {
107 401
         if (res.data) {
108
-
402
+          let data = res.data;
403
+          if (data.carUsage == '2') {
404
+            this.dept = 0;
405
+            this.dgtAdvice = '工会审核意见'
406
+          } else if (data.carUsage == '3') {
407
+            this.dept = 0;
408
+            this.dgtAdvice = '党委审核意见'
409
+          } else if (data.carUsage == '4') {
410
+            this.dept = 0;
411
+            this.dgtAdvice = '团委审核意见'
412
+          }
413
+          if (res.data.drivers) {
414
+            data.drivers = data.drivers.split(',').map(Number);
415
+            this.chooseDriver = [];
416
+            for (let d of data.drivers) {
417
+              getUser(Number(d)).then(res => {
418
+                this.chooseDriver.push(res.data)
419
+              })
420
+            }
421
+          } else {
422
+            data.drivers = []
423
+          }
424
+          if (res.data.cars) {
425
+            data.cars = data.cars.split(',').map(Number);
426
+          } else {
427
+            data.cars = []
428
+          }
429
+          this.form = data;
430
+          if (data.projectId) {
431
+            getProject(data.projectId).then(response => {
432
+              this.projectObj = response.data;
433
+            })
434
+          }
435
+          this.calculateDay();
436
+          this.initAuditor();
109 437
         } else {
110 438
           this.form.applyDate = parseTime(new Date(), "{y}-{m}-{d}");
439
+          this.form.user.nickName = this.$store.getters.name;
440
+          this.form.applier = this.$store.getters.userId;
441
+          this.form.useDept = this.$store.getters.deptId;
442
+          this.dept = this.$store.getters.deptId;
443
+          this.form.dept.deptName = this.$store.getters.deptName;
111 444
         }
112 445
       })
446
+    },
447
+    initAuditor() {
448
+
113 449
     },
114 450
     handleConfirm(project) {
115 451
       this.selectedProject = project;
@@ -117,15 +453,400 @@ export default {
117 453
       this.form.projectId = project.projectId;
118 454
     },
119 455
     hanldeChangeType(val) {
120
-      if (val == '2' || val == '3' || val == '4') {
456
+      let value = val.detail.value
457
+      if (value == '2' || value == '3' || value == '4') {
121 458
         this.dept = 0;
459
+        this.form.projectId = '';
122 460
       }
123 461
       else
124 462
         this.dept = this.$store.getters.deptId;
125
-    }
463
+    },
464
+    handleDateRangeChange(e) {
465
+      if (Array.isArray(e) && e.length === 2) {
466
+        const [start, end] = e;
467
+        this.form.beginDate = start;
468
+        this.form.endDate = end;
469
+        this.form.dateRange = [start, end];
470
+        this.calculateDay();
471
+      }
472
+    },
473
+    calculateDay() {
474
+      if (this.form.beginDate && this.form.endDate) {
475
+        const begin = new Date(this.form.beginDate);
476
+        const end = new Date(this.form.endDate);
477
+        if (!isNaN(begin.getTime()) && !isNaN(end.getTime())) {
478
+          this.form.days = Math.ceil((end - begin) / (1000 * 60 * 60 * 24)) + 1;
479
+        }
480
+      }
481
+    },
482
+    getCarList() {
483
+      listCar({
484
+        pageNum: 1,
485
+        pageSize: 99999999,
486
+      }).then(response => {
487
+        this.carList = response.rows;
488
+      })
489
+    },
490
+    getDriverList() {
491
+      getUserByPost({ postName: '驾驶员' }).then(response => {
492
+        this.driverList = response.data;
493
+      })
494
+    },
495
+    getChooseDriver(val) {
496
+      this.chooseDriver = val;
497
+      if (val.length != 0) {
498
+        let drivers = []
499
+        for (let d of this.chooseDriver) {
500
+          drivers.push(d.userId)
501
+        }
502
+        this.form.drivers = drivers;
503
+      }
504
+      this.openCar = false;
505
+    },
506
+    clearChooseDriver() {
507
+      this.chooseDriver = [];
508
+      this.form.drivers = [];
509
+    },
510
+    submit() {
511
+      this.$refs.form.validate().then(() => {
512
+        this.$modal.confirm('是否提交用车申请?').then(() => {
513
+          this.form.carApplyId = this.taskForm.formId;
514
+          let jsonForm = JSON.stringify(this.form);
515
+          this.form.cars = '';
516
+          this.form.drivers = '';
517
+          addCarApproval(this.form).then((res) => {
518
+            if (res.code == 200) {
519
+              getNextFlowNode({ taskId: this.taskForm.taskId }).then(res => {
520
+                this.getNextFlowNodeApproval().then(() => {
521
+                  uni.showToast({
522
+                    title: '提交成功',
523
+                    icon: 'success'
524
+                  });
525
+                  setTimeout(() => {
526
+                    uni.switchTab({
527
+                      url: '/pages/message/index'
528
+                    });
529
+                  }, 500);
530
+                });
531
+              })
532
+            }
533
+          })
534
+        })
535
+      }).catch(error => {
536
+        console.log(error)
537
+      })
538
+    },
539
+    saves() {
540
+      if (this.taskName == '安排用车') {
541
+        if (!this.form.dispatchComment) {
542
+          uni.showToast({
543
+            title: '请填写安排用车意见',
544
+            icon: 'none'
545
+          });
546
+          return
547
+        }
548
+      }
549
+      let jsonForm = JSON.stringify(this.form);
550
+      modifyCarApproval(jsonForm).then(res => {
551
+        uni.showToast({
552
+          title: '保存成功',
553
+          icon: 'success'
554
+        });
555
+      });
556
+    },
557
+    completeApply() {
558
+      this.$refs.form.validate().then(() => {
559
+        if (this.form.deptComment === '') this.form.deptComment = null;
560
+        if (this.form.managerComment === '') this.form.managerComment = null;
561
+        if (this.form.unionComment === '') this.form.unionComment = null;
562
+        if (this.form.gmComment === '') this.form.gmComment = null;
563
+        if (this.form.dszComment === '') this.form.dszComment = null;
564
+        if (this.form.dispatchComment === '') this.form.dispatchComment = null;
565
+        this.form.formId = this.taskForm.formId;
566
+        this.form.carApplyId = this.taskForm.formId;
567
+        let jsonForm = JSON.stringify(this.form);
568
+        modifyCarApproval(jsonForm).then(res => {
569
+          if (res.code == 200) {
570
+            uni.showModal({
571
+              title: '提示',
572
+              content: '是否提交审批?',
573
+              success: (res) => {
574
+                if (res.confirm) {
575
+                  getNextFlowNode({ taskId: this.taskForm.taskId }).then(res => {
576
+                    const data = res.data;
577
+                    this.getNextFlowNodeApproval().then(() => {
578
+                      uni.showToast({
579
+                        title: '提交成功',
580
+                        icon: 'success'
581
+                      });
582
+                      setTimeout(() => {
583
+                        uni.switchTab({
584
+                          url: '/pages/message/index'
585
+                        });
586
+                      }, 500);
587
+                    });
588
+                  })
589
+                } else {
590
+                  reject(new Error('用户取消提交'));
591
+                }
592
+              }
593
+            });
594
+          }
595
+        })
596
+
597
+      }).catch(() => {
598
+        uni.showToast({
599
+          title: '必填项未填写完毕',
600
+          icon: 'none'
601
+        });
602
+      })
603
+    },
604
+    getNextFlowNodeApproval() {
605
+      return new Promise((resolve, reject) => {
606
+        const handleComplete = () => {
607
+          complete(this.taskForm).then(response => {
608
+            uni.showToast({
609
+              title: response.msg,
610
+              icon: 'success'
611
+            });
612
+            resolve();
613
+          }).catch(error => {
614
+            reject(error);
615
+          });
616
+        };
617
+
618
+        const setApprovalAndComplete = (approval) => {
619
+          this.$set(this.taskForm.variables, "approval", approval);
620
+          handleComplete();
621
+        };
622
+
623
+        const setApprovalListAndComplete = (approvalList) => {
624
+          this.$set(this.taskForm.variables, "approvalList", approvalList);
625
+          handleComplete();
626
+        };
627
+
628
+        if (this.taskName == '用车申请') {
629
+          this.$set(this.taskForm.variables, "dept", this.dept);
630
+
631
+          if (this.dept == 101) {
632
+            getUserByPost({ postName: '董事长' }).then(result => {
633
+              setApprovalAndComplete(result.data[0].userId);
634
+            }).catch(error => {
635
+              reject(error);
636
+            });
637
+          } else if (this.dept == 102) {
638
+            getUserByPost({ postName: '总经理' }).then(result => {
639
+              setApprovalAndComplete(result.data[0].userId);
640
+            }).catch(error => {
641
+              reject(error);
642
+            });
643
+          } else if (this.dept == 0) {
644
+            const postName = this.getChooseType();
645
+            getUserByPost({ postName }).then(result => {
646
+              setApprovalAndComplete(result.data[0].userId);
647
+            }).catch(error => {
648
+              reject(error);
649
+            });
650
+          } else {
651
+            getUsersDeptLeader({ userId: this.$store.getters.userId }).then(res => {
652
+              if (res.data) {
653
+                setApprovalAndComplete(res.data.userId);
654
+              } else {
655
+                reject(new Error('未找到部门领导'));
656
+              }
657
+            }).catch(error => {
658
+              reject(error);
659
+            });
660
+          }
661
+        } else if (this.taskName == '部门审核') {
662
+          getUsersManageLeader({ userId: this.$store.getters.userId }).then(res => {
663
+            const userId = res.data.map(user => user.userId);
664
+            setApprovalListAndComplete(userId);
665
+          }).catch(error => {
666
+            reject(error);
667
+          });
668
+        } else if (['分管审核', '党工团审核', '总经理审核', '董事长审核'].includes(this.taskName)) {
669
+          getUserByRole({ roleId: 5 }).then(result => {
670
+            setApprovalListAndComplete(result.data);
671
+          }).catch(error => {
672
+            reject(error);
673
+          });
674
+        } else if (this.taskName == '安排用车') {
675
+          uni.showModal({
676
+            title: '提示',
677
+            content: '最后一个节点,提交将结束流程,是否提交?',
678
+            success: (res) => {
679
+              if (res.confirm) {
680
+                handleComplete();
681
+              } else {
682
+                reject(new Error('用户取消提交'));
683
+              }
684
+            }
685
+          });
686
+        }
687
+      });
688
+    },
689
+    getChooseType() {
690
+      if (this.form.carUsage == '2') {
691
+        return '党总支书记'
692
+      }
693
+      else if (this.form.carUsage == '3') {
694
+        return '工会主席'
695
+      }
696
+      else
697
+        return '团委书记'
698
+    },
699
+    showFormItem(name) {
700
+      let isShow = false;
701
+      if (name == '部门审核')
702
+        isShow = (this.dept > 102 && this.taskName == '用车申请') || (this.taskName == '部门审核' || this.taskName == '分管审核') || ((this.taskName == '安排用车' || this.taskName == '') && this.form.deptUserId != null);
703
+      else if (name == '分管审核')
704
+        isShow = (this.dept > 102 && this.taskName == '用车申请') || (this.taskName == '部门审核' || this.taskName == '分管审核') || ((this.taskName == '安排用车' || this.taskName == '') && this.form.managerUserId != null);
705
+      else if (name == '党工团审核')
706
+        isShow = (this.dept == 0 && this.taskName == '用车申请') || this.taskName == '党工团审核' || ((this.taskName == '安排用车' || this.taskName == '') && this.form.unionUserId != null);
707
+      else if (name == '总经理审核')
708
+        isShow = (this.dept == 102 && this.taskName == '用车申请') || this.taskName == '总经理审核' || ((this.taskName == '安排用车' || this.taskName == '') && this.form.gmUserId != null);
709
+      else if (name == '董事长审核')
710
+        isShow = (this.dept == 101 && this.taskName == '用车申请') || this.taskName == '董事长审核' || ((this.taskName == '安排用车' || this.taskName == '') && this.form.dszUserId != null);
711
+      else if (name == '部门审核签名')
712
+        isShow = (this.taskName == '部门审核' || this.taskName == '分管审核') || ((this.taskName == '安排用车' || this.taskName == '') && this.form.deptUserId != null);
713
+      else if (name == '分管审核签名')
714
+        isShow = this.taskName == '分管审核' || ((this.taskName == '安排用车' || this.taskName == '') && this.form.managerUserId != null);
715
+      else if (name == '党工团审核签名')
716
+        isShow = this.taskName == '党工团审核' || ((this.taskName == '安排用车' || this.taskName == '') && this.form.unionUserId != null);
717
+      else if (name == '总经理审核签名')
718
+        isShow = this.taskName == '总经理审核' || ((this.taskName == '安排用车' || this.taskName == '') && this.form.gmUserId != null);
719
+      else if (name == '董事长审核签名')
720
+        isShow = this.taskName == '董事长审核' || ((this.taskName == '安排用车' || this.taskName == '') && this.form.dszUserId != null);
721
+      return isShow;
722
+    },
723
+    handleKilometersInput(val) {
724
+      // 只允许输入数字
725
+      this.form.kilometers = val.replace(/[^\d]/g, '');
726
+    },
126 727
   },
127 728
 }
128
-
129 729
 </script>
130 730
 
131
-<style lang="scss" scoped></style>
731
+<style lang="scss" scoped>
732
+.form-container {
733
+  padding: 20rpx;
734
+}
735
+
736
+.form-title {
737
+  text-align: center;
738
+  margin-bottom: 30rpx;
739
+
740
+  .title-text {
741
+    font-size: 36rpx;
742
+    font-weight: bold;
743
+  }
744
+
745
+  .title-line {
746
+    height: 2rpx;
747
+    background-color: #eee;
748
+    margin-top: 20rpx;
749
+  }
750
+}
751
+
752
+.custom-form {
753
+  .form-item {
754
+    // margin-bottom: 30rpx;
755
+  }
756
+}
757
+
758
+.date-range {
759
+  align-items: center;
760
+  gap: 20rpx;
761
+}
762
+
763
+.date-text {
764
+  margin: 0 10px;
765
+  font-weight: bold;
766
+}
767
+
768
+.car-list {
769
+  padding: 20rpx 0;
770
+
771
+  .car-row {
772
+    display: flex;
773
+    justify-content: space-between;
774
+    margin-bottom: 20rpx;
775
+  }
776
+
777
+  .car-item {
778
+    width: calc(50vw - 50rpx);
779
+    background-color: #f8f8f8;
780
+    border-radius: 12rpx;
781
+    padding: 20rpx;
782
+    transition: all 0.3s;
783
+    margin: 0 3px;
784
+    border: 2rpx solid transparent;
785
+
786
+    &:active {
787
+      background-color: #f0f0f0;
788
+    }
789
+
790
+    // 选中状态的样式
791
+    &.is-checked {
792
+      background-color: #e6f7ff;
793
+      border-color: #1890ff;
794
+      box-shadow: 0 2rpx 8rpx rgba(24, 144, 255, 0.15);
795
+    }
796
+
797
+    .car-info {
798
+      display: flex;
799
+      flex-direction: column;
800
+      gap: 8rpx;
801
+
802
+      .car-license {
803
+        font-size: 32rpx;
804
+        font-weight: bold;
805
+        color: #333;
806
+      }
807
+
808
+      .car-model {
809
+        font-size: 24rpx;
810
+        color: #666;
811
+      }
812
+    }
813
+  }
814
+}
815
+
816
+.driver-list {
817
+  display: flex;
818
+  flex-wrap: wrap;
819
+  gap: 20rpx;
820
+  margin-top: 20rpx;
821
+}
822
+
823
+.auditor-info {
824
+  margin-top: 20rpx;
825
+  display: flex;
826
+  justify-content: space-between;
827
+  color: #666;
828
+  font-size: 24rpx;
829
+}
830
+
831
+.popup-content {
832
+  width: 90vw;
833
+  max-height: 80vh;
834
+  background-color: #fff;
835
+  border-radius: 20rpx;
836
+  overflow: hidden;
837
+
838
+  .popup-header {
839
+    display: flex;
840
+    justify-content: space-between;
841
+    align-items: center;
842
+    padding: 30rpx;
843
+    border-bottom: 2rpx solid #f5f5f5;
844
+
845
+    .title {
846
+      font-size: 32rpx;
847
+      font-weight: bold;
848
+      color: #333;
849
+    }
850
+  }
851
+}
852
+</style>

+ 0
- 5
oa-ui-app/pages/form/declare/declare.vue Vedi File

@@ -222,7 +222,6 @@ export default {
222 222
 	},
223 223
 	methods: {
224 224
 		handleConfirm(project) {
225
-			console.log('选中的项目:', project);
226 225
 			this.selectedProject = project;
227 226
 			this.projectObj = project;
228 227
 		},
@@ -235,8 +234,6 @@ export default {
235 234
 				if (res.data) {
236 235
 					this.hasForm = true;
237 236
 					this.formData = res.data;
238
-					console.log(this.formData);
239
-
240 237
 					if (!this.applierUserName) {
241 238
 						this.applierUserName = this.formData.user.nickName;
242 239
 					}
@@ -296,9 +293,7 @@ export default {
296 293
 			})
297 294
 		},
298 295
 		countMoney() {
299
-			console.log(this.formData);
300 296
 			let result = Number(this.formData.price) * Number(this.formData.workLoad) * Number(this.formData.coefficient)
301
-			console.log(result);
302 297
 			this.money = result.toFixed(2)
303 298
 		},
304 299
 		async save() {

+ 561
- 28
oa-ui-app/pages/index.vue Vedi File

@@ -1,43 +1,576 @@
1 1
 <template>
2
-  <view class="content">
3
-    <image class="logo" src="@/static/logo.png"></image>
4
-    <view class="text-area">
5
-      <text class="title">Hello RuoYi</text>
2
+  <view class="container">
3
+    <!-- 搜索栏 -->
4
+    <view class="search-box">
5
+      <view class="search-header" @tap="toggleSearch">
6
+        <text class="search-title">搜索条件</text>
7
+        <text class="search-icon">{{ isSearchExpanded ? '收起' : '展开' }}</text>
8
+      </view>
9
+      
10
+      <view class="search-content" v-if="isSearchExpanded">
11
+        <view class="search-item">
12
+          <text>项目编号</text>
13
+          <input v-model="queryParams.projectNumber" placeholder="请输入项目编号" @confirm="handleQuery" />
14
+        </view>
15
+        <view class="search-item">
16
+          <text>项目名称</text>
17
+          <input v-model="queryParams.projectName" placeholder="请输入项目名称" @confirm="handleQuery" />
18
+        </view>
19
+        <view class="search-item">
20
+          <text>项目负责人</text>
21
+          <picker mode="selector" :range="userList" range-key="text" @change="handleLeaderChange">
22
+            <view class="picker">
23
+              <text>{{ selectedLeader || '请选择项目负责人' }}</text>
24
+            </view>
25
+          </picker>
26
+        </view>
27
+        <view class="search-item">
28
+          <text>承担部门</text>
29
+          <picker mode="selector" :range="deptList" range-key="text" @change="handleDeptChange">
30
+            <view class="picker">
31
+              <text>{{ selectedDept || '请选择承担部门' }}</text>
32
+            </view>
33
+          </picker>
34
+        </view>
35
+        <view class="search-item">
36
+          <text>其他关键字</text>
37
+          <input v-model="queryParams.queryString" placeholder="请输入关键字" @confirm="handleQuery" />
38
+        </view>
39
+        <view class="search-buttons">
40
+          <button type="primary" size="mini" @tap="handleQuery">搜索</button>
41
+          <button type="default" size="mini" @tap="resetQuery">重置</button>
42
+        </view>
43
+      </view>
44
+    </view>
45
+
46
+    <!-- 项目列表 -->
47
+    <view class="list-box">
48
+      <view class="list-header">
49
+        <text class="title">测绘项目列表</text>
50
+        <view class="header-btns">
51
+          <button type="primary" size="mini" @tap="handleRegister">登记项目</button>
52
+          <button type="default" size="mini" @tap="handleExport">导出项目</button>
53
+        </view>
54
+      </view>
55
+
56
+      <scroll-view 
57
+        scroll-y 
58
+        class="list-scroll"
59
+        @scrolltolower="loadMore"
60
+        :refresher-enabled="true"
61
+        :refresher-triggered="isRefreshing"
62
+        @refresherrefresh="onRefresh"
63
+      >
64
+        <view class="list-content">
65
+          <view class="project-card" v-for="(item, index) in projectList" :key="index">
66
+            <!-- 卡片头部 -->
67
+            <view class="card-header">
68
+              <view class="header-left">
69
+                <text class="project-name">{{ item.projectName }}</text>
70
+                <text class="status-tag" :class="item.isFinished === '0' ? 'success' : 'warning'">
71
+                  {{ item.isFinished === '0' ? '进行中' : '已结束' }}
72
+                </text>
73
+              </view>
74
+              <text class="level-tag" :class="item.projectLevel === '0' ? 'info' : 'error'">
75
+                {{ item.projectLevel === '0' ? '一般项目' : '重大项目' }}
76
+              </text>
77
+            </view>
78
+
79
+            <!-- 卡片内容 -->
80
+            <view class="card-content">
81
+              <view class="info-row">
82
+                <text class="label">项目编号:</text>
83
+                <text class="value">{{ item.projectNumber }}</text>
84
+              </view>
85
+              <view class="info-row">
86
+                <text class="label">合同编码:</text>
87
+                <text class="value">{{ item.contract ? item.contract.contractCode : '未关联' }}</text>
88
+              </view>
89
+              <view class="info-row">
90
+                <text class="label">承担部门:</text>
91
+                <text class="value">{{ item.undertakingDeptName }}</text>
92
+              </view>
93
+              <view class="info-row">
94
+                <text class="label">项目负责人:</text>
95
+                <text class="value">{{ item.projectLeaderUser ? item.projectLeaderUser.nickName : '' }}</text>
96
+              </view>
97
+              <view class="info-row">
98
+                <text class="label">项目类型:</text>
99
+                <text class="value">{{ item.projectType }}</text>
100
+              </view>
101
+              <view class="info-row">
102
+                <text class="label">项目进度:</text>
103
+                <view class="progress-box">
104
+                  <progress :percent="item.percentage" :activeColor="getProgressColor(item.percentage)" />
105
+                  <text class="progress-text">{{ item.percentage }}%</text>
106
+                </view>
107
+              </view>
108
+            </view>
109
+
110
+            <!-- 卡片底部 -->
111
+            <view class="card-footer">
112
+              <button type="primary" size="mini" @tap="handleView(item)">查看</button>
113
+              <button type="warn" size="mini" @tap="handleDelete(item)">删除</button>
114
+              <button type="default" size="mini" @tap="handleRelate(item)">关联合同</button>
115
+            </view>
116
+          </view>
117
+        </view>
118
+      </scroll-view>
119
+
120
+      <!-- 加载更多 -->
121
+      <view class="load-more" v-if="projectList.length > 0">
122
+        <text v-if="hasMore">上拉加载更多</text>
123
+        <text v-else>没有更多数据了</text>
124
+      </view>
6 125
     </view>
7 126
   </view>
8 127
 </template>
9 128
 
10 129
 <script>
11
-  export default {
12
-    onLoad: function() {
130
+import { listProject, listProjectFuzzy, delProject } from "@/api/oa/project/project";
131
+import { getProjectProgress } from "@/api/oa/project/projectProgress";
132
+import { listDept } from '@/api/system/dept';
133
+import { listUser } from '@/api/system/user';
134
+
135
+export default {
136
+  data() {
137
+    return {
138
+      isSearchExpanded: false,
139
+      queryParams: {
140
+        pageNum: 1,
141
+        pageSize: 10,
142
+        projectNumber: '',
143
+        projectLeader: '',
144
+        projectName: '',
145
+        queryString: '',
146
+        undertakingDept: ''
147
+      },
148
+      projectList: [],
149
+      total: 0,
150
+      loading: false,
151
+      userList: [],
152
+      deptList: [],
153
+      selectedLeader: '',
154
+      selectedDept: '',
155
+      hasMore: true,
156
+      isRefreshing: false
157
+    }
158
+  },
159
+  onLoad() {
160
+    this.getList();
161
+    this.getDeptList();
162
+    this.getUserList();
163
+  },
164
+  methods: {
165
+    toggleSearch() {
166
+      this.isSearchExpanded = !this.isSearchExpanded;
167
+    },
168
+    
169
+    resetQuery() {
170
+      this.queryParams = {
171
+        pageNum: 1,
172
+        pageSize: 10,
173
+        projectNumber: '',
174
+        projectLeader: '',
175
+        projectName: '',
176
+        queryString: '',
177
+        undertakingDept: ''
178
+      };
179
+      this.selectedLeader = '';
180
+      this.selectedDept = '';
181
+      this.handleQuery();
182
+    },
183
+    
184
+    async getList() {
185
+      this.loading = true;
186
+      try {
187
+        const response = this.queryParams.queryString ? 
188
+          await listProjectFuzzy(this.queryParams) : 
189
+          await listProject(this.queryParams);
190
+        
191
+        if (this.queryParams.pageNum === 1) {
192
+          this.projectList = response.rows;
193
+        } else {
194
+          this.projectList = [...this.projectList, ...response.rows];
195
+        }
196
+        
197
+        this.total = response.total;
198
+        this.hasMore = this.projectList.length < this.total;
199
+        
200
+        for (let project of this.projectList) {
201
+          const res = await getProjectProgress(project.projectId);
202
+          if (res.data && res.data.length > 0) {
203
+            project.percentage = Number(res.data[res.data.length - 1].percentage);
204
+          } else {
205
+            project.percentage = 0;
206
+          }
207
+          if (project.isFinished === '1') {
208
+            project.percentage = 100;
209
+          }
210
+        }
211
+      } catch (error) {
212
+        uni.showToast({
213
+          title: '获取数据失败',
214
+          icon: 'none'
215
+        });
216
+      } finally {
217
+        this.loading = false;
218
+        this.isRefreshing = false;
219
+      }
220
+    },
221
+    
222
+    async getDeptList() {
223
+      try {
224
+        const res = await listDept({});
225
+        this.deptList = res.data.map(item => ({
226
+          value: item.deptId,
227
+          text: item.deptName
228
+        }));
229
+      } catch (error) {
230
+        uni.showToast({
231
+          title: '获取部门列表失败',
232
+          icon: 'none'
233
+        });
234
+      }
235
+    },
236
+    
237
+    async getUserList() {
238
+      try {
239
+        const res = await listUser({ pageNum: 1, pageSize: 9999 });
240
+        this.userList = res.rows.map(item => ({
241
+          value: item.userId,
242
+          text: item.nickName
243
+        }));
244
+      } catch (error) {
245
+        uni.showToast({
246
+          title: '获取用户列表失败',
247
+          icon: 'none'
248
+        });
249
+      }
250
+    },
251
+    
252
+    handleQuery() {
253
+      this.queryParams.pageNum = 1;
254
+      this.getList();
255
+    },
256
+    
257
+    handleLeaderChange(e) {
258
+      const index = e.detail.value;
259
+      this.queryParams.projectLeader = this.userList[index].value;
260
+      this.selectedLeader = this.userList[index].text;
261
+      this.handleQuery();
262
+    },
263
+    
264
+    handleDeptChange(e) {
265
+      const index = e.detail.value;
266
+      this.queryParams.undertakingDept = this.deptList[index].value;
267
+      this.selectedDept = this.deptList[index].text;
268
+      this.handleQuery();
269
+    },
270
+    
271
+    handleView(row) {
272
+      uni.navigateTo({
273
+        url: `/pages/project/info?id=${row.projectId}&projectName=${encodeURIComponent(row.projectName)}`
274
+      });
275
+    },
276
+    
277
+    handleDelete(row) {
278
+      uni.showModal({
279
+        title: '提示',
280
+        content: `是否确认删除项目编号为"${row.projectNumber}"的数据项?`,
281
+        success: async (res) => {
282
+          if (res.confirm) {
283
+            try {
284
+              await delProject(row.projectId);
285
+              this.getList();
286
+              uni.showToast({
287
+                title: '删除成功',
288
+                icon: 'success'
289
+              });
290
+            } catch (error) {
291
+              uni.showToast({
292
+                title: '删除失败',
293
+                icon: 'none'
294
+              });
295
+            }
296
+          }
297
+        }
298
+      });
299
+    },
300
+    
301
+    handleRegister() {
302
+      uni.navigateTo({
303
+        url: '/pages/project/register'
304
+      });
305
+    },
306
+    
307
+    handleExport() {
308
+      uni.showToast({
309
+        title: '导出功能开发中',
310
+        icon: 'none'
311
+      });
312
+    },
313
+    
314
+    handleRelate(row) {
315
+      uni.showToast({
316
+        title: '关联合同功能开发中',
317
+        icon: 'none'
318
+      });
319
+    },
320
+    
321
+    loadMore() {
322
+      if (this.hasMore) {
323
+        this.queryParams.pageNum++;
324
+        this.getList();
325
+      }
326
+    },
327
+    
328
+    onRefresh() {
329
+      this.isRefreshing = true;
330
+      this.queryParams.pageNum = 1;
331
+      this.getList();
332
+    },
333
+    
334
+    getProgressColor(percentage) {
335
+      if (!percentage) return '#ff4d4f';
336
+      if (percentage <= 20) return '#ff4d4f';
337
+      if (percentage <= 50) return '#faad14';
338
+      if (percentage <= 80) return '#1890ff';
339
+      return '#52c41a';
13 340
     }
14 341
   }
342
+}
15 343
 </script>
16 344
 
17 345
 <style>
18
-  .content {
19
-    display: flex;
20
-    flex-direction: column;
21
-    align-items: center;
22
-    justify-content: center;
23
-  }
346
+.container {
347
+  padding: 20rpx;
348
+  background-color: #f5f5f5;
349
+  height: 100vh;
350
+  display: flex;
351
+  flex-direction: column;
352
+}
24 353
 
25
-  .logo {
26
-    height: 200rpx;
27
-    width: 200rpx;
28
-    margin-top: 200rpx;
29
-    margin-left: auto;
30
-    margin-right: auto;
31
-    margin-bottom: 50rpx;
32
-  }
354
+.search-box {
355
+  background-color: #fff;
356
+  border-radius: 8rpx;
357
+  margin-bottom: 20rpx;
358
+  overflow: hidden;
359
+}
33 360
 
34
-  .text-area {
35
-    display: flex;
36
-    justify-content: center;
37
-  }
361
+.search-header {
362
+  padding: 20rpx;
363
+  display: flex;
364
+  justify-content: space-between;
365
+  align-items: center;
366
+  border-bottom: 1px solid #ebeef5;
367
+}
38 368
 
39
-  .title {
40
-    font-size: 36rpx;
41
-    color: #8f8f94;
42
-  }
369
+.search-title {
370
+  font-size: 32rpx;
371
+  font-weight: bold;
372
+  color: #333;
373
+}
374
+
375
+.search-icon {
376
+  font-size: 28rpx;
377
+  color: #909399;
378
+}
379
+
380
+.search-content {
381
+  padding: 20rpx;
382
+}
383
+
384
+.search-item {
385
+  margin-bottom: 20rpx;
386
+}
387
+
388
+.search-item text {
389
+  display: block;
390
+  margin-bottom: 10rpx;
391
+  font-size: 28rpx;
392
+  color: #333;
393
+}
394
+
395
+.search-item input,
396
+.search-item .picker {
397
+  width: 100%;
398
+  height: 80rpx;
399
+  padding: 0 20rpx;
400
+  border: 1px solid #dcdfe6;
401
+  border-radius: 4rpx;
402
+  box-sizing: border-box;
403
+}
404
+
405
+.search-item .picker {
406
+  line-height: 80rpx;
407
+  color: #606266;
408
+}
409
+
410
+.search-buttons {
411
+  display: flex;
412
+  gap: 20rpx;
413
+  margin-top: 20rpx;
414
+}
415
+
416
+.search-buttons button {
417
+  flex: 1;
418
+}
419
+
420
+.list-box {
421
+  flex: 1;
422
+  background-color: #fff;
423
+  border-radius: 8rpx;
424
+  display: flex;
425
+  flex-direction: column;
426
+  overflow: hidden;
427
+}
428
+
429
+.list-header {
430
+  padding: 20rpx;
431
+  display: flex;
432
+  justify-content: space-between;
433
+  align-items: center;
434
+  border-bottom: 1px solid #ebeef5;
435
+}
436
+
437
+.title {
438
+  font-size: 32rpx;
439
+  font-weight: bold;
440
+}
441
+
442
+.header-btns {
443
+  display: flex;
444
+  gap: 20rpx;
445
+}
446
+
447
+.list-scroll {
448
+  flex: 1;
449
+  height: 0;
450
+}
451
+
452
+.list-content {
453
+  padding: 20rpx;
454
+}
455
+
456
+.project-card {
457
+  background-color: #fff;
458
+  border-radius: 8rpx;
459
+  margin-bottom: 20rpx;
460
+  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
461
+  overflow: hidden;
462
+}
463
+
464
+.card-header {
465
+  padding: 20rpx;
466
+  display: flex;
467
+  justify-content: space-between;
468
+  align-items: center;
469
+  border-bottom: 1px solid #ebeef5;
470
+  background-color: #fafafa;
471
+}
472
+
473
+.header-left {
474
+  display: flex;
475
+  align-items: center;
476
+  gap: 20rpx;
477
+}
478
+
479
+.project-name {
480
+  font-size: 32rpx;
481
+  font-weight: bold;
482
+  color: #333;
483
+}
484
+
485
+.status-tag,
486
+.level-tag {
487
+  padding: 4rpx 12rpx;
488
+  border-radius: 4rpx;
489
+  font-size: 24rpx;
490
+}
491
+
492
+.success {
493
+  background-color: #f0f9eb;
494
+  color: #67c23a;
495
+}
496
+
497
+.warning {
498
+  background-color: #fdf6ec;
499
+  color: #e6a23c;
500
+}
501
+
502
+.info {
503
+  background-color: #f4f4f5;
504
+  color: #909399;
505
+}
506
+
507
+.error {
508
+  background-color: #fef0f0;
509
+  color: #f56c6c;
510
+}
511
+
512
+.card-content {
513
+  padding: 20rpx;
514
+}
515
+
516
+.info-row {
517
+  display: flex;
518
+  align-items: center;
519
+  margin-bottom: 16rpx;
520
+}
521
+
522
+.info-row:last-child {
523
+  margin-bottom: 0;
524
+}
525
+
526
+.info-row .label {
527
+  width: 160rpx;
528
+  color: #909399;
529
+  font-size: 28rpx;
530
+}
531
+
532
+.info-row .value {
533
+  flex: 1;
534
+  color: #333;
535
+  font-size: 28rpx;
536
+}
537
+
538
+.progress-box {
539
+  flex: 1;
540
+  display: flex;
541
+  align-items: center;
542
+  gap: 20rpx;
543
+}
544
+
545
+.progress-box progress {
546
+  flex: 1;
547
+}
548
+
549
+.progress-text {
550
+  width: 80rpx;
551
+  text-align: right;
552
+  color: #333;
553
+  font-size: 28rpx;
554
+}
555
+
556
+.card-footer {
557
+  display: flex;
558
+  justify-content: flex-end;
559
+  gap: 20rpx;
560
+  padding: 20rpx;
561
+  border-top: 1px solid #ebeef5;
562
+  background-color: #fafafa;
563
+}
564
+
565
+.card-footer button {
566
+  margin: 0;
567
+  padding: 0 30rpx;
568
+}
569
+
570
+.load-more {
571
+  text-align: center;
572
+  padding: 20rpx;
573
+  color: #909399;
574
+  font-size: 28rpx;
575
+}
43 576
 </style>

+ 326
- 5
oa-ui-app/pages/message/completed/index.vue Vedi File

@@ -1,21 +1,342 @@
1 1
 <!--
2 2
  * @Author: ysh
3 3
  * @Date: 2025-02-19 15:30:13
4
- * @LastEditors: 
5
- * @LastEditTime: 2025-02-19 15:30:40
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-05-23 16:00:38
6 6
 -->
7 7
 <template>
8
-  <view>
8
+  <view class="page-container">
9
+    <!-- 搜索框 -->
10
+    <view class="search-box">
11
+      <uni-search-bar radius="34" placeholder="请输入流程名称" @input="inputChange" v-model="queryParams.name" />
12
+    </view>
13
+    <!-- 流程列表 -->
14
+    <mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
15
+      :up="upOption" :fixed="false" :top="10" style="padding: 30rpx;">
16
+      <view>
17
+        <view v-for="(item, index) in finishedList" :key="index">
18
+          <view class="list-item">
19
+            <!-- 状态与标题行 -->
20
+            <view class="header-line">
21
+              <text class="proc-name" style="font-weight: bold; font-size: 30rpx; color: #FF5733;">{{ item.procDefName }}</text>
22
+            </view>
9 23
 
24
+            <!-- 主要内容 -->
25
+            <view class="content-box">
26
+              <!-- 标题 -->
27
+              <view class="title-box">
28
+                <uni-icons type="info" size="16" color="#666" class="title-icon"></uni-icons>
29
+                <text class="title-text">{{ item.title }}</text>
30
+              </view>
31
+
32
+              <!-- 信息分割线 -->
33
+              <uv-divider :dashed="true" margin="10rpx 0"></uv-divider>
34
+
35
+              <!-- 详细信息 -->
36
+              <view class="detail-grid">
37
+                <view class="detail-item">
38
+                  <uni-icons type="person" size="14" color="#999"></uni-icons>
39
+                  <text class="detail-text">{{ item.startUserName }}</text>
40
+                  <uni-tag style="margin-left: 10rpx;" :text="item.startDeptName" type="info" size="mini" />
41
+                </view>
42
+
43
+                <view class="detail-item">
44
+                  <uni-icons type="calendar" size="14" color="#999"></uni-icons>
45
+                  <text class="detail-text">{{ item.createTime }}</text>
46
+                </view>
47
+
48
+                <view class="detail-item">
49
+                  <uni-icons type="clock" size="14" color="#999"></uni-icons>
50
+                  <text class="detail-text">{{ item.duration }}</text>
51
+                </view>
52
+
53
+                <view class="detail-item">
54
+                  <uni-icons type="location" size="14" color="#999"></uni-icons>
55
+                  <text class="detail-text">{{ item.taskName }}</text>
56
+                </view>
57
+              </view>
58
+            </view>
59
+
60
+            <!-- 操作按钮 -->
61
+            <view class="action-box">
62
+              <text class="view-detail" @click="handleFlowRecord(item)">办理进度</text>
63
+              <text class="view-detail" @click="handleFlowNote(item)">表单信息</text>
64
+              <text class="view-detail" @click="handleRevoke(item)" :class="{ disabled: beDisabled(item) }">撤回</text>
65
+            </view>
66
+          </view>
67
+        </view>
68
+      </view>
69
+    </mescroll-uni>
70
+
71
+    <!-- 菜单抽屉 -->
72
+    <uni-drawer ref="drawer" mode="right" :width="300">
73
+      <flow-record :rows="selectedItem" v-if="active == 'record'"></flow-record>
74
+    </uni-drawer>
10 75
   </view>
11 76
 </template>
12 77
 
13 78
 <script>
14
-  export default {
15
-    
79
+import { debounce } from 'lodash-es';
80
+import MescrollMixin from '@/uni_modules/mescroll/components/mescroll-uni/mescroll-mixins.js'
81
+import { finishedList, revokeProcess } from "@/api/flowable/finished";
82
+import { getProcessVariables } from "@/api/flowable/definition";
83
+import FlowRecord from '@/pages/components/flowable/FlowRecord.vue';
84
+
85
+export default {
86
+  components: { FlowRecord },
87
+  mixins: [MescrollMixin],
88
+  data() {
89
+    return {
90
+      mescroll: null, // mescroll实例
91
+      // 已办任务列表数据
92
+      finishedList: [],
93
+      total: 0,
94
+      queryParams: {
95
+        name: '',
96
+        pageNum: 1,
97
+        pageSize: 10
98
+      },
99
+      selectedItem: null, // 当前选中项
100
+      active: 'record',
101
+      downOption: {
102
+        auto: true,
103
+        textOutOffset: '下拉刷新',
104
+        textLoading: '加载中...'
105
+      },
106
+      upOption: {
107
+        auto: false,
108
+        page: { num: 1, size: 10 },
109
+        noMoreSize: 5,
110
+        empty: { tip: '暂无已办任务' },
111
+        textNoMore: '~ 没有更多任务了 ~'
112
+      }
113
+    }
114
+  },
115
+  onLoad: function (options) {
116
+    uni.startPullDownRefresh();
117
+  },
118
+  methods: {
119
+    // 初始化mescroll
120
+    mescrollInit(mescroll) {
121
+      this.mescroll = mescroll;
122
+    },
123
+    // mescroll下拉刷新回调
124
+    async downCallback() {
125
+      this.finishedList = []; // 清空列表
126
+      this.queryParams.pageNum = 1
127
+      const res = await this.loadData()
128
+      this.finishedList = res.data.list;
129
+      this.mescroll.endSuccess(res.data.length)
130
+      this.mescroll.endErr()
131
+      uni.stopPullDownRefresh();
132
+    },
133
+    // mescroll上拉加载回调
134
+    async upCallback(page) {
135
+      this.queryParams.pageNum = page.num
136
+      const res = await this.loadData()
137
+      this.finishedList = this.finishedList.concat(res.data.list)
138
+      this.mescroll.endSuccess(res.data.list.length, res.data.hasNextPage)
139
+    },
140
+    // 加载数据
141
+    async loadData() {
142
+      try {
143
+        const res = await finishedList(this.queryParams)
144
+        return {
145
+          data: {
146
+            list: res.data.records,
147
+          }
148
+        }
149
+      } catch (e) {
150
+        this.mescroll.endErr()
151
+        return { data: { list: [] } }
152
+      }
153
+    },
154
+    // 输入变化处理
155
+    inputChange(res) {
156
+      this.queryParams.name = res;
157
+      this.search();
158
+    },
159
+    // 执行搜索
160
+    search() {
161
+      this.loadData()
162
+      this.downCallback()
163
+    },
164
+    // 办理进度
165
+    handleFlowRecord(row) {
166
+      this.active = 'record';
167
+      this.selectedItem = row;
168
+      this.$refs.drawer.open();
169
+    },
170
+    // 表单信息
171
+    handleFlowNote(row) {
172
+      getProcessVariables(row.taskId).then(res => {
173
+        if (res.data) {
174
+          const query = {
175
+            procInsId: row.procInsId,
176
+            executionId: row.executionId,
177
+            deployId: row.deployId,
178
+            taskId: row.taskId,
179
+            taskName: '',
180
+            startUserName: row.startUserName,
181
+            procDefName: row.procDefName,
182
+            formId: res.data.formId
183
+          }
184
+          const encodedParams = encodeURIComponent(JSON.stringify(query));
185
+          uni.navigateTo({
186
+            url: `/pages/message/apply/detail?params=${encodedParams}`
187
+          })
188
+        }
189
+      })
190
+    },
191
+    // 撤回任务
192
+    handleRevoke(row) {
193
+      if (this.beDisabled(row)) return;
194
+
195
+      uni.showModal({
196
+        title: '提示',
197
+        content: '确定撤回任务吗?',
198
+        success: (res) => {
199
+          if (res.confirm) {
200
+            const params = {
201
+              instanceId: row.procInsId,
202
+              taskId: row.taskId
203
+            }
204
+            revokeProcess(params).then(res => {
205
+              this.$modal.msgSuccess(res.msg);
206
+              this.upCallback();
207
+            });
208
+          }
209
+        }
210
+      });
211
+    },
212
+    // 判断是否禁用撤回
213
+    beDisabled(row) {
214
+      if (row.procDefName == '项目流转' && row.taskName == '项目安排') {
215
+        return true
216
+      }
217
+      return false
218
+    }
16 219
   }
220
+}
17 221
 </script>
18 222
 
19 223
 <style lang="scss" scoped>
224
+.list-item {
225
+  padding: 24rpx;
226
+  background: #fff;
227
+  border-radius: 16rpx;
228
+  margin-bottom: 20rpx;
229
+  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
230
+  width: 100%;
231
+}
232
+
233
+/* 头部状态行 */
234
+.header-line {
235
+  display: flex;
236
+  align-items: center;
237
+  margin-bottom: 20rpx;
238
+}
239
+
240
+.proc-name {
241
+  font-size: 30rpx;
242
+  color: #333;
243
+  font-weight: 500;
244
+  margin-left: 16rpx;
245
+}
246
+
247
+.status-tag {
248
+  transform: translateY(-2rpx);
249
+}
250
+
251
+/* 内容区域 */
252
+.content-box {
253
+  padding: 12rpx 0;
254
+}
255
+
256
+.title-box {
257
+  display: flex;
258
+  align-items: center;
259
+  margin-bottom: 16rpx;
260
+}
261
+
262
+.title-icon {
263
+  margin-right: 8rpx;
264
+}
265
+
266
+.title-text {
267
+  font-size: 28rpx;
268
+  color: #333;
269
+  font-weight: 600;
270
+}
271
+
272
+/* 详细信息网格 */
273
+.detail-grid {
274
+  display: grid;
275
+  grid-template-columns: repeat(2, 1fr);
276
+  gap: 16rpx 24rpx;
277
+  margin-top: 20rpx;
278
+}
279
+
280
+.detail-item {
281
+  display: flex;
282
+  align-items: center;
283
+}
284
+
285
+.detail-text {
286
+  font-size: 24rpx;
287
+  color: #666;
288
+  margin-left: 8rpx;
289
+  overflow: hidden;
290
+  text-overflow: ellipsis;
291
+}
292
+
293
+/* 操作区域 */
294
+.action-box {
295
+  display: flex;
296
+  justify-content: space-between;
297
+  align-items: center;
298
+  margin-top: 24rpx;
299
+  padding-top: 16rpx;
300
+  border-top: 1rpx solid #eee;
301
+}
302
+
303
+.view-detail {
304
+  color: #2979FF;
305
+  font-size: 26rpx;
306
+  padding: 8rpx 16rpx;
307
+  border-radius: 8rpx;
308
+  background: #f5f7fa;
309
+}
310
+
311
+.view-detail.disabled {
312
+  color: #c0c4cc;
313
+  pointer-events: none;
314
+}
315
+
316
+/* 添加点击态效果 */
317
+.action-box:active {
318
+  background: #f8f8f8;
319
+  transform: scale(0.98);
320
+  transition: all 0.2s;
321
+}
322
+
323
+/* 状态标签增强 */
324
+.status-tag {
325
+  padding: 4rpx 12rpx !important;
326
+  border-radius: 8rpx !important;
327
+}
328
+
329
+/* 颜色增强 */
330
+.proc-name {
331
+  color: #1a1a1a;
332
+}
333
+
334
+.title-text {
335
+  color: #2d2d2d;
336
+}
20 337
 
338
+/* 图标颜色统一 */
339
+.detail-item uni-icons {
340
+  color: #909399 !important;
341
+}
21 342
 </style>

+ 16
- 16
oa-ui-app/pages/message/index.vue Vedi File

@@ -2,14 +2,10 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-01-21 10:01:39
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-03-19 16:21:31
5
+ * @LastEditTime: 2025-05-23 16:08:41
6 6
 -->
7 7
 <template>
8 8
 	<view class="u-page container">
9
-		<view class="send" @click="openSendFlow">
10
-			<!-- <uv-icon name="bell-fill" color="#2979ff" size="50"></uv-icon> -->
11
-			<uni-icons type="paperplane-filled" size="50" color="#2979ff"></uni-icons>
12
-		</view>
13 9
 		<scroll-view class="scroll-list" scroll-y="true">
14 10
 			<view class="uni-list">
15 11
 				<view class="uni-list-cell">
@@ -30,6 +26,20 @@
30 26
 						</view>
31 27
 					</view>
32 28
 				</view>
29
+				<view class="uni-list-cell">
30
+					<view class="uni-media-list" @click="openSendFlow">
31
+						<view style="margin-right: 20rpx;">
32
+							<u-image :fade="false" src="@/static/images/message/sendFlow.png" width="40px" height="40px"></u-image>
33
+						</view>
34
+						<view class="uni-media-list-body">
35
+							<view class="uni-media-list-text-top"><span>发起流程</span>
36
+							</view>
37
+							<view class="uni-media-list-text-bottom">
38
+								<uni-text><span>发起新流程</span></uni-text>
39
+							</view>
40
+						</view>
41
+					</view>
42
+				</view>
33 43
 				<view class="uni-list-cell">
34 44
 					<view class="uni-media-list" @click="$tab.navigateTo('/pages/message/completed/index')">
35 45
 						<view style="margin-right: 20rpx;">
@@ -143,7 +153,7 @@ export default {
143 153
 		sendFlow(row) {
144 154
 			uni.showModal({
145 155
 				title: '提示',
146
-				content: '是否发起《'+row.name+'》流程?',
156
+				content: '是否发起《' + row.name + '》流程?',
147 157
 				success: (res) => {
148 158
 					if (!res.confirm) {
149 159
 						return;
@@ -215,16 +225,6 @@ export default {
215 225
 	// position: relative;
216 226
 }
217 227
 
218
-.send {
219
-	position: absolute;
220
-	right: 0px;
221
-	bottom: 10px;
222
-	width: 50px;
223
-	height: 50px;
224
-	background-color: #fff;
225
-	border-radius: 4px;
226
-}
227
-
228 228
 .bottom-popup {
229 229
 	max-height: 50vh;
230 230
 	margin-bottom: 65px;

+ 59
- 71
oa-ui-app/pages/message/myProcess/index.vue Vedi File

@@ -2,79 +2,71 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2025-02-19 15:36:34
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2025-04-11 15:33:30
5
+ * @LastEditTime: 2025-05-23 15:42:14
6 6
 -->
7 7
 <template>
8 8
   <view class="page-container">
9 9
     <!-- 搜索框 -->
10 10
     <view class="search-box">
11
-      <uni-search-bar radius="34" placeholder="请输入流程名称" @confirm="search" @input="inputChange"
12
-        v-model="queryParams.procDefName" />
13
-    </view>
14
-    <view>
15
-      <!-- <uv-sticky bgColor="#fff">
16
-        <uv-tabs :list="[{ name: '进行中' }, { name: '已完成' }]" :current="activeTabs" @click="switchTabs"></uv-tabs>
17
-      </uv-sticky> -->
11
+      <uni-search-bar radius="34" placeholder="请输入流程名称" @input="inputChange" v-model="queryParams.name" />
18 12
     </view>
19 13
     <!-- 流程列表 -->
20 14
     <mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
21
-      :up="upOption" :fixed="false" :top="10">
22
-      <uni-list>
23
-        <uni-list-item v-for="(item, index) in myProcessList" :key="index">
24
-          <template v-slot:body>
25
-            <view class="list-item">
26
-              <!-- 状态与标题行 -->
27
-              <view class="header-line">
28
-                <uni-tag :text="item.finishTime ? '已完成' : '进行中'" :type="item.finishTime ? 'success' : 'warning'"
29
-                  size="small" class="status-tag" />
30
-                <text class="proc-name">{{ item.procDefName }}</text>
15
+      :up="upOption" :fixed="false" :top="10" style="padding: 30rpx;">
16
+      <view>
17
+        <view v-for="(item, index) in myProcessList" :key="index">
18
+          <view class="list-item">
19
+            <!-- 状态与标题行 -->
20
+            <view class="header-line">
21
+              <uni-tag :text="item.finishTime ? '已完成' : '进行中'" :type="item.finishTime ? 'success' : 'warning'"
22
+                size="small" class="status-tag" />
23
+              <text class="proc-name">{{ item.procDefName }}</text>
24
+            </view>
25
+
26
+            <!-- 主要内容 -->
27
+            <view class="content-box">
28
+              <!-- 标题 -->
29
+              <view class="title-box">
30
+                <uni-icons type="info" size="16" color="#666" class="title-icon"></uni-icons>
31
+                <text class="title-text">{{ item.title }}</text>
31 32
               </view>
32 33
 
33
-              <!-- 主要内容 -->
34
-              <view class="content-box">
35
-                <!-- 标题 -->
36
-                <view class="title-box">
37
-                  <uni-icons type="info" size="16" color="#666" class="title-icon"></uni-icons>
38
-                  <text class="title-text">{{ item.title }}</text>
34
+              <!-- 信息分割线 -->
35
+              <uv-divider :dashed="true" margin="10rpx 0"></uv-divider>
36
+
37
+              <!-- 详细信息 -->
38
+              <view class="detail-grid">
39
+                <view class="detail-item">
40
+                  <uni-icons type="calendar" size="14" color="#999"></uni-icons>
41
+                  <text class="detail-text">{{ item.createTime }}</text>
39 42
                 </view>
40 43
 
41
-                <!-- 信息分割线 -->
42
-                <uv-divider :dashed="true" margin="10rpx 0"></uv-divider>
43
-
44
-                <!-- 详细信息 -->
45
-                <view class="detail-grid">
46
-                  <view class="detail-item">
47
-                    <uni-icons type="calendar" size="14" color="#999"></uni-icons>
48
-                    <text class="detail-text">{{ item.createTime }}</text>
49
-                  </view>
50
-
51
-                  <view class="detail-item">
52
-                    <uni-icons type="clock" size="14" color="#999"></uni-icons>
53
-                    <text class="detail-text">{{ item.duration }}</text>
54
-                  </view>
55
-
56
-                  <view class="detail-item">
57
-                    <uni-icons type="location" size="14" color="#999"></uni-icons>
58
-                    <text class="detail-text">{{ item.taskName }}</text>
59
-                  </view>
60
-
61
-                  <view class="detail-item">
62
-                    <uni-icons type="person" size="14" color="#999"></uni-icons>
63
-                    <text class="detail-text">{{ item.assigneeName || '暂无待办人' }}</text>
64
-                  </view>
44
+                <view class="detail-item">
45
+                  <uni-icons type="clock" size="14" color="#999"></uni-icons>
46
+                  <text class="detail-text">{{ item.duration }}</text>
47
+                </view>
48
+
49
+                <view class="detail-item">
50
+                  <uni-icons type="location" size="14" color="#999"></uni-icons>
51
+                  <text class="detail-text">{{ item.taskName }}</text>
65 52
                 </view>
66
-              </view>
67 53
 
68
-              <!-- 操作按钮 -->
69
-              <view class="action-box">
70
-                <text class="view-detail">查看详情</text>
71
-                <uni-icons type="more-filled" size="20" color="#666" @click="showAction(item)"
72
-                  class="action-icon"></uni-icons>
54
+                <view class="detail-item">
55
+                  <uni-icons type="person" size="14" color="#999"></uni-icons>
56
+                  <text class="detail-text">{{ item.assigneeName || '暂无待办人' }}</text>
57
+                </view>
73 58
               </view>
74 59
             </view>
75
-          </template>
76
-        </uni-list-item>
77
-      </uni-list>
60
+
61
+            <!-- 操作按钮 -->
62
+            <view class="action-box">
63
+              <text class="view-detail">查看详情</text>
64
+              <uni-icons type="more-filled" size="20" color="#666" @click="showAction(item)"
65
+                class="action-icon"></uni-icons>
66
+            </view>
67
+          </view>
68
+        </view>
69
+      </view>
78 70
     </mescroll-uni>
79 71
 
80 72
     <!-- 操作菜单 -->
@@ -119,7 +111,7 @@ export default {
119 111
       myProcessList: [],
120 112
       total: 0,
121 113
       queryParams: {
122
-        keyword: '',
114
+        name: '',
123 115
         pageNum: 1,
124 116
         pageSize: 10
125 117
       },
@@ -128,12 +120,12 @@ export default {
128 120
       clickRow: {},
129 121
       active: 'record',
130 122
       downOption: {
131
-        auto: false, // 手动初始化后需要关闭自动加载
123
+        auto: true, // 手动初始化后需要关闭自动加载
132 124
         textOutOffset: '下拉刷新',
133 125
         textLoading: '加载中...'
134 126
       },
135 127
       upOption: {
136
-        auto: true, // 自动加载初始页
128
+        auto: false, // 自动加载初始页
137 129
         page: { num: 1, size: 10 }, // 初始页码和每页数量
138 130
         noMoreSize: 5, // 数据不足时显示"没有更多"
139 131
         empty: { tip: '暂无待审任务' },
@@ -142,11 +134,7 @@ export default {
142 134
     }
143 135
   },
144 136
   created() {
145
-    // 在生命周期中创建防抖方法
146
-    // this.debounceSearch = debounce(() => {
147
-    //   this.search()
148
-    // }, 500)
149
-    // this.getMyprogressList();
137
+
150 138
   },
151 139
   onLoad: function (options) {
152 140
     uni.startPullDownRefresh();
@@ -185,12 +173,11 @@ export default {
185 173
         return {
186 174
           data: {
187 175
             list: res.data.records,
188
-            hasNextPage: res.hasNextPage
189 176
           }
190 177
         }
191 178
       } catch (e) {
192 179
         this.mescroll.endErr()
193
-        return { data: { list: [], hasNextPage: false } }
180
+        return { data: { list: [] } }
194 181
       }
195 182
     },
196 183
     getMyprogressList() {
@@ -209,14 +196,15 @@ export default {
209 196
       this.$refs.actionPopup.open()
210 197
     },
211 198
     // 输入变化处理
212
-    inputChange() {
213
-      this.debounceSearch()
199
+    inputChange(res) {
200
+      this.queryParams.name = res;
201
+      this.search();
214 202
     },
215 203
 
216 204
     // 执行搜索
217 205
     search() {
218 206
       this.loadData()
219
-      this.mescroll.resetUpScroll()
207
+      this.downCallback()
220 208
     },
221 209
 
222 210
     handleFlowRecord(row) {
@@ -281,7 +269,7 @@ export default {
281 269
   padding: 24rpx;
282 270
   background: #fff;
283 271
   border-radius: 16rpx;
284
-  margin: 16rpx 24rpx;
272
+  margin-bottom: 20rpx;
285 273
   box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
286 274
   width: 100%;
287 275
 }

+ 11
- 5
oa-ui-app/pages/mine/about/index.vue Vedi File

@@ -1,9 +1,15 @@
1
+<!--
2
+ * @Author: ysh
3
+ * @Date: 2025-01-16 11:17:08
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2025-05-23 16:11:40
6
+-->
1 7
 <template>
2 8
   <view class="about-container">
3 9
     <view class="header-section text-center">
4 10
       <image style="width: 150rpx;height: 150rpx;" src="/static/logo.png" mode="widthFix">
5 11
       </image>
6
-      <uni-title type="h2" title="CMC综合办公移动端"></uni-title>
12
+      <uni-title type="h2" title="CMC智联云枢办公系统移动端"></uni-title>
7 13
     </view>
8 14
 
9 15
     <view class="content-section">
@@ -14,19 +20,19 @@
14 20
             <view class="text-right">v{{version}}</view>
15 21
           </view>
16 22
         </view>
17
-        <view class="list-cell list-cell-arrow">
23
+        <!-- <view class="list-cell list-cell-arrow">
18 24
           <view class="menu-item-box">
19 25
             <view>公司网站</view>
20 26
             <view class="text-right">
21 27
               <uni-link :href="url" :text="url" showUnderLine="false"></uni-link>
22 28
             </view>
23 29
           </view>
24
-        </view>
30
+        </view> -->
25 31
       </view>
26 32
     </view>
27 33
 
28 34
     <view class="copyright">
29
-      <view>Copyright &copy; 2025 cmc All Rights Reserved.</view>
35
+      <view>Copyright &copy; 2025 CMC All Rights Reserved.</view>
30 36
     </view>
31 37
   </view>
32 38
 </template>
@@ -35,7 +41,7 @@
35 41
   export default {
36 42
     data() {
37 43
       return {
38
-        url: getApp().globalData.config.appInfo.site_url,
44
+        // url: getApp().globalData.config.appInfo.site_url,
39 45
         version: getApp().globalData.config.appInfo.version
40 46
       }
41 47
     }

+ 6
- 6
oa-ui-app/pages/mine/index.vue Vedi File

@@ -26,7 +26,7 @@
26 26
     </view>
27 27
 
28 28
     <view class="content-section">
29
-      <view class="mine-actions grid col-4 text-center">
29
+      <!-- <view class="mine-actions grid col-4 text-center">
30 30
         <view class="action-item" @click="handleJiaoLiuQun">
31 31
           <view class="iconfont icon-friendfill text-pink icon"></view>
32 32
           <text class="text">交流群</text>
@@ -43,21 +43,21 @@
43 43
           <view class="iconfont icon-dianzan text-green icon"></view>
44 44
           <text class="text">点赞我们</text>
45 45
         </view>
46
-      </view>
46
+      </view> -->
47 47
 
48 48
       <view class="menu-list">
49
-        <view class="list-cell list-cell-arrow" @click="handleToEditInfo">
49
+        <!-- <view class="list-cell list-cell-arrow" @click="handleToEditInfo">
50 50
           <view class="menu-item-box">
51 51
             <view class="iconfont icon-user menu-icon"></view>
52 52
             <view>编辑资料</view>
53 53
           </view>
54
-        </view>
55
-        <view class="list-cell list-cell-arrow" @click="handleHelp">
54
+        </view> -->
55
+        <!-- <view class="list-cell list-cell-arrow" @click="handleHelp">
56 56
           <view class="menu-item-box">
57 57
             <view class="iconfont icon-help menu-icon"></view>
58 58
             <view>常见问题</view>
59 59
           </view>
60
-        </view>
60
+        </view> -->
61 61
         <view class="list-cell list-cell-arrow" @click="handleAbout">
62 62
           <view class="menu-item-box">
63 63
             <view class="iconfont icon-aixin menu-icon"></view>

BIN
oa-ui-app/static/images/message/sendFlow.png Vedi File


Loading…
Annulla
Salva