Commit d26a220b85570bf763652095086a88be9ab14242

Authored by 史婷婷
1 parent 2171bcc2

feat: 客户开发列表-批量审批

... ... @@ -93,6 +93,7 @@ export function batchApproveApi(params) {
93 93 url: `/flow/task/approve/batch`,
94 94 method: 'post',
95 95 data: params,
  96 + contentType: ContentTypeEnum.JSON
96 97 })
97 98 }
98 99
... ...
1   -/**
2   - * @description: Request result set
3   - */
4   -export enum ResultEnum {
5   - SUCCESS = 200,
6   - ERROR = -1,
7   - TIMEOUT = 401,
8   - TYPE = 'success',
9   -}
10   -
11   -export enum ResponseEnum {
12   - BLOB = 'BLOB',
13   -}
14   -/**
15   - * @description: request method
16   - */
17   -export enum RequestEnum {
18   - GET = 'GET',
19   - POST = 'POST',
20   - PUT = 'PUT',
21   - DELETE = 'DELETE',
22   -}
23   -
24   -/**
25   - * @description: contentType
26   - */
27   -export enum ContentTypeEnum {
28   - // json
29   - JSON = 'application/json;charset=UTF-8',
30   - // form-data qs
31   - FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
32   - // form-data upload
33   - BLOB = 'multipart/form-data;charset=UTF-8',
34   -}
... ... @@ -135,6 +135,29 @@
135 135 title="科办"
136 136 @confirm="onOfficeConfirm"
137 137 />
  138 + <!-- 批量:统一弹框(通过/驳回) -->
  139 + <uni-popup ref="approvePopup" type="center" :mask-click="false">
  140 + <view class="action-modal">
  141 + <view class="header">{{ approveType === 'PASS' ? '通过' : '驳回' }}</view>
  142 + <view class="body">
  143 + <text class="tip">
  144 + {{ approveType === 'PASS' ? '您将通过该信息的审核' : '您将驳回该信息的审核' }}
  145 + </text>
  146 + <uni-easyinput
  147 + v-model="approveComment"
  148 + :placeholder="approveType === 'PASS' ? '请输入通过原因' : '请输入驳回原因'"
  149 + />
  150 + </view>
  151 + <view class="footer">
  152 + <button class="btn cancel" @click="cancelApprove">取消</button>
  153 + <button
  154 + class="btn confirm"
  155 + type="primary"
  156 + @click="confirmApprove"
  157 + >{{ approveType === 'PASS' ? '通过' : '驳回' }}</button>
  158 + </view>
  159 + </view>
  160 + </uni-popup>
138 161 </view>
139 162 </template>
140 163
... ... @@ -142,7 +165,7 @@
142 165 import CardList from '@/components/card/index.vue'
143 166 import FilterModal from '@/components/filter/index.vue'
144 167 import SingleSelectSheet from '@/components/single-select/index.vue'
145   -import { queryApi, officeQueryApi, statusOptions, getTodoTypeStatisticsApi } from '@/api/devManage.js'
  168 +import { queryApi, officeQueryApi, statusOptions, getTodoTypeStatisticsApi, batchApproveApi } from '@/api/devManage.js'
146 169 import {getDicByCodes, getDicName} from '@/utils/dic';
147 170
148 171 export default {
... ... @@ -150,6 +173,7 @@ export default {
150 173 data() {
151 174 return {
152 175 searchKeyword: '',
  176 + searchKeywordDebounced: '',
153 177 officeList: [], // 科办列表下拉选
154 178 tabs: [],
155 179 todoType: '',
... ... @@ -180,7 +204,9 @@ export default {
180 204 // 批量选择
181 205 batchMode: false,
182 206 selectedKeys: [],
183   - rowKey: 'customerId',
  207 + rowKey: 'id',
  208 + selectedRows: [],
  209 + currentItems: [],
184 210
185 211 // 筛选弹框
186 212 filterVisible: false,
... ... @@ -188,12 +214,15 @@ export default {
188 214 officeSelectVisible: false,
189 215 // 审核状态枚举(来自 api/devManage.js)
190 216 statusOptions,
  217 + // 批量操作弹框数据
  218 + approveType: 'PASS', // PASS:通过;REFUSE:驳回
  219 + approveComment: ''
191 220 }
192 221 },
193 222 computed: {
194 223 extraCombined() {
195 224 return {
196   - keyword: this.searchKeyword,
  225 + customerName: this.searchKeywordDebounced || undefined,
197 226 todoType: this.todoType || undefined,
198 227 workshopType: this.workshopType || undefined
199 228 }
... ... @@ -206,6 +235,13 @@ export default {
206 235 this.extraParams = v
207 236 },
208 237 immediate: true
  238 + },
  239 + // 勾选变化时同步 selectedRows
  240 + selectedKeys: {
  241 + deep: true,
  242 + handler() {
  243 + this.updateSelectedRows()
  244 + }
209 245 }
210 246 },
211 247 created() {
... ... @@ -229,42 +265,36 @@ export default {
229 265 methods: {
230 266 onCardLoaded({ items }) {
231 267 this.currentItems = items
  268 + this.updateSelectedRows()
232 269 },
233 270 onCardError() {
234 271 uni.showToast({ title: '列表加载失败', icon: 'none' })
235 272 },
236   - onSearch() {
237   - this.extraParams = { ...this.extraParams, keyword: this.searchKeyword }
238   - },
239   - // 输入实时搜索:300ms 防抖更新关键字并触发 CardList 刷新
  273 + // 输入实时搜索:1200ms 防抖,仅在停止输入超过阈值后刷新
240 274 onSearchInput(val) {
241   - this.searchKeyword = typeof val === 'string' ? val : (val && val.value) ? val.value : this.searchKeyword
242 275 if (this.searchDebounceTimer) clearTimeout(this.searchDebounceTimer)
243 276 this.searchDebounceTimer = setTimeout(() => {
244   - this.extraParams = { ...this.extraParams, keyword: this.searchKeyword }
  277 + this.searchKeywordDebounced = this.searchKeyword
245 278 this.searchDebounceTimer = null
246   - }, 300)
  279 + }, 1200)
247 280 },
248 281 // uni-search-bar 确认搜索:更新关键字并触发 CardList 刷新
249 282 search(e) {
250 283 const val = e && e.value != null ? e.value : this.searchKeyword
251 284 this.searchKeyword = val
252   - this.extraParams = { ...this.extraParams, keyword: val }
  285 + this.searchKeywordDebounced = val
253 286 },
254 287 switchTab(item) {
255 288 this.todoType = item.value;
256   - this.extraParams = { ...this.extraParams, todoType: item.value }
257 289 },
258 290 switchWorkshopType(item, i) {
259 291 this.workshopType = item.value;
260 292 this.currentWorkshopTypeIndex = i;
261   - this.extraParams = { ...this.extraParams, workshopType: item.value }
262 293 },
263 294 openFilter() {
264 295 this.filterVisible = true
265 296 },
266 297 onFilterReset(payload) {
267   - console.log('onFilterReset',payload)
268 298 // 保持弹框不关闭,仅同步表单
269 299 this.filterForm = payload
270 300 },
... ... @@ -297,13 +327,45 @@ export default {
297 327 this.batchMode = !this.batchMode
298 328 if (!this.batchMode) this.selectedKeys = []
299 329 },
  330 + // 根据 selectedKeys 与当前页 items 计算选中行对象
  331 + updateSelectedRows() {
  332 + const key = this.rowKey || 'id'
  333 + const keys = Array.isArray(this.selectedKeys) ? this.selectedKeys : []
  334 + const items = Array.isArray(this.currentItems) ? this.currentItems : []
  335 + this.selectedRows = items.filter(it => keys.includes(it && it[key]))
  336 + },
300 337 batchReject() {
301 338 if (this.selectedKeys.length === 0) return uni.showToast({ title: '请选择记录', icon: 'none' })
302   - uni.showToast({ title: '已发起驳回', icon: 'none' })
  339 + this.approveType = 'REFUSE'
  340 + this.approveComment = ''
  341 + this.$refs.approvePopup && this.$refs.approvePopup.open()
303 342 },
304 343 batchPass() {
305 344 if (this.selectedKeys.length === 0) return uni.showToast({ title: '请选择记录', icon: 'none' })
306   - uni.showToast({ title: '已发起通过', icon: 'none' })
  345 + this.approveType = 'PASS'
  346 + this.approveComment = ''
  347 + this.$refs.approvePopup && this.$refs.approvePopup.open()
  348 + },
  349 + cancelApprove() {
  350 + this.$refs.approvePopup && this.$refs.approvePopup.close()
  351 + this.approveComment = ''
  352 + },
  353 + async confirmApprove() {
  354 + this.flowTaskIds = this.selectedRows.map((item) => item.flowTaskId || '' + "");
  355 + batchApproveApi({ taskIds: this.flowTaskIds, approveType: this.approveType, message: this.approveComment }).then((res) => {
  356 + console.log('batchApproveApi_res', res)
  357 + uni.showToast({ title: this.approveType === 'PASS' ? '已通过' : '已驳回', icon: 'none' })
  358 + this.$refs.approvePopup && this.$refs.approvePopup.close()
  359 + this.approveComment = '';
  360 + this.selectedKeys = [];
  361 + this.selectedRows = [];
  362 + this.batchMode = false;
  363 + this.$refs.cardRef && this.$refs.cardRef.reload && this.$refs.cardRef.reload();
  364 + this.getTodoTypeStatisticsFun()
  365 + }).catch(() => {
  366 + console.error('confirmApprove error', e)
  367 + uni.showToast({ title: '操作失败', icon: 'none' })
  368 + })
307 369 },
308 370 onAdd() {
309 371 uni.showToast({ title: '点击新增', icon: 'none' })
... ... @@ -317,9 +379,9 @@ export default {
317 379 params.createEndTime = params.dateRange[1] + ' 23:59:59'
318 380 delete params.dateRange
319 381 }
320   - // 关键字
321   - if (this.searchKeyword) {
322   - params.keyword = this.searchKeyword
  382 + // 关键字(使用去抖后的值避免频繁触发)
  383 + if (this.searchKeywordDebounced) {
  384 + params.customerName = this.searchKeywordDebounced
323 385 }
324 386 return queryApi(params)
325 387 .then(res => {
... ... @@ -633,4 +695,55 @@ export default {
633 695
634 696 }
635 697
  698 +.action-modal {
  699 + width: 560rpx;
  700 + padding: 32rpx 28rpx 20rpx;
  701 + background: #fff;
  702 + border-radius: 20rpx;
  703 + .header {
  704 + text-align: center;
  705 + font-size: 34rpx;
  706 + font-weight: 600;
  707 + margin-bottom: 12rpx;
  708 + color: rgba(0,0,0,0.9);
  709 + }
  710 + .body {
  711 + padding: 12rpx 4rpx 24rpx;
  712 + .tip {
  713 + display: block;
  714 + text-align: center;
  715 + font-size: 32rpx;
  716 + color: rgba(0,0,0,0.6);
  717 + margin-bottom: 32rpx;
  718 + }
  719 + ::v-deep .uni-easyinput {
  720 + width: 100%;
  721 + }
  722 + }
  723 + .footer {
  724 + display: flex;
  725 + justify-content: space-between;
  726 + gap: 16rpx;
  727 + .btn {
  728 + flex: 1;
  729 + height: 80rpx;
  730 + line-height: 80rpx;
  731 + border-radius: 12rpx;
  732 + font-size: 30rpx;
  733 + font-size: 32rpx;
  734 + &::after {
  735 + border: none;
  736 + }
  737 + }
  738 + .cancel {
  739 + background: #fff;
  740 + color: rgba(0,0,0,0.9);
  741 + }
  742 + .confirm {
  743 + background: #fff !important;
  744 + color: $theme-primary !important;
  745 + }
  746 + }
  747 +}
  748 +
636 749 </style>
\ No newline at end of file
... ...
... ... @@ -44,7 +44,10 @@ config.responseType = 'blob';
44 44 header: config.header,
45 45 dataType: 'json'
46 46 }).then(response => {
47   - let [error, res] = response
  47 + let [error, res] = response;
  48 + console.log('request__response', response)
  49 + console.log('request__error', error)
  50 + console.log('request__res', res)
48 51 if (error) {
49 52 toast('后端接口连接异常')
50 53 reject('后端接口连接异常')
... ...