Commit 5e3ecbbc27ae76203ded1349211ef6178ac0ee63

Authored by 史婷婷
1 parent be0745ff

feat: 流程中心-全部&待办&已办-列表+筛选+tabs

1 1 import request from '@/utils/request'
2 2
3 3
4   -// 查询列表
5   -export function queryApi(params) {
  4 +// 我发起的 查询列表
  5 +export function myFlowQueryApi(params) {
6 6 return request({
7 7 url: `/flow/task/list/my`,
8 8 method: 'get',
9 9 params
10 10 })
  11 +}
  12 +
  13 +// 任务数量统计
  14 +export function statisticsCountApi() {
  15 + return request({
  16 + url: `/flow/task/statistics/count`,
  17 + method: 'get'
  18 + })
  19 +}
  20 +// 任务列表(全部/已办/待办)
  21 +export function taskListApi(params) {
  22 + return request({
  23 + url: `/flow/task/list/task`,
  24 + method: 'get',
  25 + params
  26 + })
11 27 }
\ No newline at end of file
... ...
... ... @@ -92,6 +92,14 @@
92 92 }
93 93 },
94 94 {
  95 + "path": "pages/flow/approval",
  96 + "style": {
  97 + "navigationBarTitleText": "流程中心",
  98 + "navigationBarBackgroundColor": "#ffffff",
  99 + "navigationBarTextStyle": "black"
  100 + }
  101 + },
  102 + {
95 103 "path": "pages/contract_framework/index",
96 104 "style": {
97 105 "navigationBarTitleText": "框架合同",
... ...
... ... @@ -211,7 +211,6 @@
211 211 },
212 212 created() {
213 213 this.getOffice();
214   - // this.loadAllDicData();
215 214 // 获取待办类型数量统计
216 215 this.getTodoTypeStatisticsFun()
217 216 },
... ... @@ -429,12 +428,12 @@
429 428 value: ''
430 429 },
431 430 {
432   - label: `已办(${res.data.completedCount > 999 ? '999+' : res.data.completedCount || 0})`,
433   - value: 'COMPLETED'
434   - },
435   - {
436 431 label: `待办(${res.data.todoCount > 999 ? '999+' : res.data.todoCount || 0})`,
437 432 value: 'WAIT'
  433 + },
  434 + {
  435 + label: `已办(${res.data.completedCount > 999 ? '999+' : res.data.completedCount || 0})`,
  436 + value: 'COMPLETED'
438 437 }
439 438 ]
440 439 });
... ... @@ -494,7 +493,7 @@
494 493 display: flex;
495 494 align-items: center;
496 495 justify-content: space-between;
497   - padding: 26rpx 0;
  496 + padding: 26rpx 34rpx;
498 497 border-bottom: solid 1px #E7E7E7;
499 498
500 499 .tab {
... ... @@ -637,7 +636,6 @@
637 636 color: rgba(0, 0, 0, 0.6);
638 637 font-size: 28rpx;
639 638 margin-bottom: 24rpx;
640   - height: 32rpx;
641 639
642 640 &:last-child {
643 641 margin-bottom: 0;
... ... @@ -645,6 +643,7 @@
645 643
646 644 text {
647 645 width: 60%;
  646 + line-height: 32rpx;
648 647
649 648 &:last-child {
650 649 color: rgba(0, 0, 0, 0.9);
... ...
  1 +<template>
  2 + <view class="page">
  3 + <view class="fixed">
  4 + <view class="search-row">
  5 + <uni-search-bar v-model="searchKeyword" radius="6" placeholder="请输入流程编号/名称/标题" clearButton="auto"
  6 + cancelButton="none" bgColor="#F3F3F3" textColor="rgba(0,0,0,0.4)" @confirm="onSearchConfirm"
  7 + @input="onSearchInput" />
  8 + <image class="tool-icon" src="/static/images/dev_manage/filter_icon.png" @click="openFilter" />
  9 + </view>
  10 + <!-- 页内 tabs -->
  11 + <view class="tabs">
  12 + <view v-for="(t, i) in tabs" :key="i" :class="['tab', { active: tabValue === t.value }]"
  13 + @click="switchTab(t)">
  14 + {{ t.label }}
  15 + </view>
  16 + </view>
  17 + </view>
  18 +
  19 +
  20 +
  21 + <view class="list-box">
  22 + <CardList ref="cardRef" :fetchFn="fetchList" :query="query" :extra="extraParams" :enable-refresh="true"
  23 + :enable-load-more="true" row-key="id" @loaded="onLoaded" @error="onError">
  24 + <template v-slot="{ item }">
  25 + <view class="card">
  26 + <view class="card-header">
  27 + <text class="title omit2">{{ item.title }}</text>
  28 + </view>
  29 + <view class="info-row">
  30 + <text>流程模块</text><text>{{ getDicName('PROCESS_MODE', item.mode, cardModeOptions) }}</text>
  31 + </view>
  32 + <view class="info-row">
  33 + <text>发起人</text><text>{{ item.startBy }}</text>
  34 + </view>
  35 + <view class="info-row">
  36 + <text>发起时间</text><text>{{ item.startTime }}</text>
  37 + </view>
  38 + <view class="footer">
  39 + <button class="btn" type="primary" plain @click.stop="onDetail(item)">审核详情</button>
  40 + <button class="btn" type="primary" plain @click.stop="onDetail(item)">去审核</button>
  41 + </view>
  42 + </view>
  43 + </template>
  44 + </CardList>
  45 + </view>
  46 +
  47 + <filter-modal :visible.sync="filterVisible" :value.sync="filterForm" title="筛选" @reset="onFilterReset"
  48 + @confirm="onFilterConfirm">
  49 + <template v-slot="{ model }">
  50 + <view class="filter-form">
  51 + <view class="form-item">
  52 + <view class="label">流程模块</view>
  53 + <view class="fake-select" @click="openModeSelect">
  54 + <text v-if="!model.mode" class="placeholder">请选择</text>
  55 + <text v-else class="value">{{ model.modeName }}</text>
  56 + </view>
  57 + </view>
  58 + </view>
  59 + </template>
  60 + </filter-modal>
  61 +
  62 + <SingleSelectSheet :visible.sync="modeSelectVisible" :options="modeOptions" v-model="filterForm.mode"
  63 + title="流程模块" @confirm="onModeConfirm" />
  64 + </view>
  65 +</template>
  66 +
  67 +<script>
  68 + import CardList from '@/components/card/index.vue'
  69 + import FilterModal from '@/components/filter/index.vue'
  70 + import SingleSelectSheet from '@/components/single-select/index.vue'
  71 + import {
  72 + taskListApi,
  73 + statisticsCountApi
  74 + } from '@/api/flow.js'
  75 + import {
  76 + getDicByCodes
  77 + } from '@/utils/dic'
  78 + import {
  79 + getDicName
  80 + } from '@/utils/dic.js'
  81 +
  82 + export default {
  83 + name: 'MyFlow',
  84 + components: {
  85 + CardList,
  86 + FilterModal,
  87 + SingleSelectSheet
  88 + },
  89 + data() {
  90 + return {
  91 + searchKeyword: '',
  92 + searchKeywordDebounced: '',
  93 + searchDebounceTimer: null,
  94 + tabValue: '1',
  95 + isCompleted: null,
  96 + tabs: [],
  97 + filterVisible: false,
  98 + filterForm: {
  99 + mode: '',
  100 + modeName: ''
  101 + },
  102 + modeSelectVisible: false,
  103 + modeOptions: [],
  104 + cardModeOptions: [],
  105 + query: {},
  106 + extraParams: {},
  107 + currentItems: []
  108 + }
  109 + },
  110 + created() {
  111 + this.loadModeOptions()
  112 + this.getStatisticsFun()
  113 + },
  114 + onReachBottom() {
  115 + if (this.$refs && this.$refs.cardRef && this.$refs.cardRef.onLoadMore) {
  116 + this.$refs.cardRef.onLoadMore()
  117 + }
  118 + },
  119 + watch: {},
  120 + methods: {
  121 + onSearchInput(val) {
  122 + if (this.searchDebounceTimer) clearTimeout(this.searchDebounceTimer)
  123 + this.searchDebounceTimer = setTimeout(() => {
  124 + this.searchKeywordDebounced = this.searchKeyword
  125 + this.searchDebounceTimer = null
  126 + this.extraParams = {
  127 + searchKey: this.searchKeywordDebounced || '',
  128 + mode: this.filterForm.mode || '',
  129 + isCompleted: this.isCompleted
  130 + }
  131 + }, 800)
  132 + },
  133 + onSearchConfirm(e) {
  134 + const val = e && e.value != null ? e.value : this.searchKeyword
  135 + this.searchKeyword = val
  136 + this.searchKeywordDebounced = val
  137 + this.extraParams = {
  138 + searchKey: this.searchKeywordDebounced || '',
  139 + mode: this.filterForm.mode || '',
  140 + isCompleted: this.isCompleted
  141 + }
  142 + },
  143 + openFilter() {
  144 + this.filterVisible = true
  145 + },
  146 + onFilterReset(payload) {
  147 + this.filterForm = payload
  148 + },
  149 + onFilterConfirm(payload) {
  150 + // 兜底:部分端可能未同步 v-model
  151 + if ((payload.mode === '' || payload.mode == null) && this.filterForm.mode !== '') {
  152 + payload.mode = this.filterForm.mode
  153 + payload.modeName = this.filterForm.modeName
  154 + }
  155 + this.filterForm = payload
  156 + // 仅在确认时更新给 CardList 的条件
  157 + this.extraParams = {
  158 + searchKey: this.searchKeywordDebounced || '',
  159 + mode: payload.mode || '',
  160 + isCompleted: this.isCompleted
  161 + }
  162 + this.filterVisible = false
  163 + },
  164 + openModeSelect() {
  165 + this.modeSelectVisible = true
  166 + },
  167 + onModeConfirm({
  168 + value,
  169 + label
  170 + }) {
  171 + this.filterForm.mode = value || '';
  172 + this.filterForm.modeName = label || '';
  173 + },
  174 + onLoaded({
  175 + items
  176 + }) {
  177 + this.currentItems = items || []
  178 + },
  179 + onError() {
  180 + uni.showToast({
  181 + title: '列表加载失败',
  182 + icon: 'none'
  183 + })
  184 + },
  185 + async loadModeOptions() {
  186 + try {
  187 + const dicCodes = ['PROCESS_MODE']
  188 + const results = await getDicByCodes(dicCodes)
  189 + const option = results.PROCESS_MODE.data || []
  190 + this.cardModeOptions = Array.isArray(option) ? option : [];
  191 + this.modeOptions = option.map(it => ({
  192 + label: it.name || '',
  193 + value: it.code || ''
  194 + }))
  195 + } catch (e) {
  196 + this.modeOptions = [];
  197 + this.cardModeOptions = [];
  198 + }
  199 + },
  200 + getStatisticsFun() {
  201 + statisticsCountApi().then((res) => {
  202 + this.tabs = [{
  203 + label: `全部(${res.data.allCount > 999 ? '999+' : res.data.allCount || 0})`,
  204 + value: '1'
  205 + },
  206 + {
  207 + label: `待办(${res.data.todoCount > 999 ? '999+' : res.data.todoCount || 0})`,
  208 + value: '2'
  209 + },
  210 + {
  211 + label: `已办(${res.data.completedCount > 999 ? '999+' : res.data.completedCount || 0})`,
  212 + value: '3'
  213 + }
  214 + ]
  215 + });
  216 + },
  217 + fetchList({
  218 + pageIndex,
  219 + pageSize,
  220 + query,
  221 + extra
  222 + }) {
  223 + const params = {
  224 + pageIndex,
  225 + pageSize,
  226 + ...query,
  227 + ...extra
  228 + }
  229 + return taskListApi(params).then(res => {
  230 + const _data = res.data || {}
  231 + const records = _data.datas || _data.list || _data.records || []
  232 + const totalCount = _data.totalCount || _data.count || 0
  233 + const hasNext = _data.hasNext || false
  234 + return {
  235 + records,
  236 + totalCount,
  237 + hasNext
  238 + }
  239 + }).catch(() => {
  240 + this.onError()
  241 + return {
  242 + records: [],
  243 + totalCount: 0,
  244 + hasNext: false
  245 + }
  246 + })
  247 + },
  248 + onDetail(item) {
  249 + uni.showToast({
  250 + title: '审核详情',
  251 + icon: 'none'
  252 + })
  253 + },
  254 + getDicName: getDicName,
  255 + switchTab(item) {
  256 + this.tabValue = item.value;
  257 + this.isCompleted = item.value === '3' ? true : item.value === '2' ? false : null;
  258 + this.extraParams = {
  259 + searchKey: this.searchKeywordDebounced || '',
  260 + mode: this.filterForm.mode || '',
  261 + isCompleted: this.isCompleted
  262 + }
  263 + },
  264 + }
  265 + }
  266 +</script>
  267 +
  268 +<style lang="scss" scoped>
  269 + .page {
  270 + display: flex;
  271 + flex-direction: column;
  272 + height: 100vh;
  273 + }
  274 +
  275 + .fixed {
  276 + position: fixed;
  277 + top: 96rpx;
  278 + left: 0;
  279 + right: 0;
  280 + z-index: 2;
  281 + background: #fff;
  282 + }
  283 +
  284 + .search-row {
  285 + display: flex;
  286 + align-items: center;
  287 + padding: 16rpx 32rpx;
  288 +
  289 + .tool-icon {
  290 + width: 48rpx;
  291 + height: 48rpx;
  292 + margin-left: 16rpx;
  293 + }
  294 +
  295 + .uni-searchbar {
  296 + padding: 0;
  297 + flex: 1;
  298 + }
  299 + }
  300 +
  301 + /* 仅当前页覆盖 uni-search-bar 盒子高度 */
  302 + ::v-deep .uni-searchbar__box {
  303 + height: 80rpx !important;
  304 + justify-content: start;
  305 +
  306 + .uni-searchbar__box-search-input {
  307 + font-size: 32rpx !important;
  308 + }
  309 + }
  310 +
  311 + .tabs {
  312 + display: flex;
  313 + align-items: center;
  314 + justify-content: space-between;
  315 + padding: 26rpx 34rpx;
  316 +
  317 + .tab {
  318 + width: 180rpx;
  319 + text-align: center;
  320 + color: #666;
  321 + color: rgba(0, 0, 0, 0.9);
  322 + line-height: 44rpx;
  323 + position: relative;
  324 + }
  325 +
  326 + .tab.active {
  327 + color: $theme-primary;
  328 + font-weight: 600;
  329 + }
  330 +
  331 + .tab.active::after {
  332 + content: '';
  333 + position: absolute;
  334 + left: 50%;
  335 + transform: translateX(-50%);
  336 + bottom: -13px;
  337 + width: 32rpx;
  338 + height: 6rpx;
  339 + border-radius: 4rpx;
  340 + background: $theme-primary;
  341 + }
  342 + }
  343 +
  344 + .list-box {
  345 + flex: 1;
  346 + padding: 236rpx 0 0
  347 + }
  348 +
  349 + .card {
  350 + .footer {
  351 + margin-top: 28rpx;
  352 + box-shadow: inset 0px 1px 0px 0px #E7E7E7;
  353 + padding-top: 22rpx;
  354 + display: flex;
  355 + justify-content: flex-end;
  356 + .btn {
  357 + height: 64rpx;
  358 + line-height: 60rpx;
  359 + font-size: 28rpx;
  360 + width: 160rpx;
  361 + margin-left: 22rpx;
  362 + margin-right: 0;
  363 + padding: 0;
  364 + }
  365 + }
  366 +
  367 + .card-header {
  368 + margin-bottom: 28rpx;
  369 +
  370 + .title {
  371 + font-size: 36rpx;
  372 + font-weight: 600;
  373 + line-height: 50rpx;
  374 + color: #323241;
  375 + }
  376 + }
  377 +
  378 + .info-row {
  379 + display: flex;
  380 + align-items: center;
  381 + color: rgba(0, 0, 0, 0.6);
  382 + font-size: 28rpx;
  383 + margin-bottom: 24rpx;
  384 +
  385 + &:last-child {
  386 + margin-bottom: 0;
  387 + }
  388 +
  389 + text {
  390 + width: 60%;
  391 + line-height: 32rpx;
  392 +
  393 + &:last-child {
  394 + color: rgba(0, 0, 0, 0.9);
  395 + width: 40%;
  396 + }
  397 + }
  398 + }
  399 + }
  400 +
  401 + .filter-form {
  402 + .form-item {
  403 + margin-bottom: 24rpx;
  404 + }
  405 +
  406 + .label {
  407 + margin-bottom: 20rpx;
  408 + color: rgba(0, 0, 0, 0.9);
  409 + height: 44rpx;
  410 + line-height: 44rpx;
  411 + font-size: 30rpx;
  412 + }
  413 +
  414 + .fake-select {
  415 + height: 80rpx;
  416 + line-height: 80rpx;
  417 + padding: 0 20rpx;
  418 + background: #f3f3f3;
  419 + border-radius: 12rpx;
  420 +
  421 + .placeholder {
  422 + color: #999;
  423 + }
  424 +
  425 + .value {
  426 + color: #333;
  427 + }
  428 + }
  429 + }
  430 +</style>
\ No newline at end of file
... ...
... ... @@ -10,7 +10,7 @@
10 10 </view>
11 11
12 12 <view class="list-box">
13   - <CardList ref="cardRef" :fetchFn="fetchList" :query="query" :extra="extra" :enable-refresh="true"
  13 + <CardList ref="cardRef" :fetchFn="fetchList" :query="query" :extra="extraParams" :enable-refresh="true"
14 14 :enable-load-more="true" row-key="id" @loaded="onLoaded" @error="onError">
15 15 <template v-slot="{ item }">
16 16 <view class="card">
... ... @@ -59,7 +59,7 @@
59 59 import FilterModal from '@/components/filter/index.vue'
60 60 import SingleSelectSheet from '@/components/single-select/index.vue'
61 61 import {
62   - queryApi
  62 + myFlowQueryApi
63 63 } from '@/api/flow.js'
64 64 import {
65 65 getDicByCodes
... ... @@ -89,7 +89,7 @@
89 89 modeOptions: [],
90 90 cardModeOptions: [],
91 91 query: {},
92   - extra: {},
  92 + extraParams: {},
93 93 currentItems: []
94 94 }
95 95 },
... ... @@ -108,7 +108,7 @@
108 108 this.searchDebounceTimer = setTimeout(() => {
109 109 this.searchKeywordDebounced = this.searchKeyword
110 110 this.searchDebounceTimer = null
111   - this.extra = {
  111 + this.extraParams = {
112 112 searchKey: this.searchKeywordDebounced || '',
113 113 mode: this.filterForm.mode || ''
114 114 }
... ... @@ -118,7 +118,7 @@
118 118 const val = e && e.value != null ? e.value : this.searchKeyword
119 119 this.searchKeyword = val
120 120 this.searchKeywordDebounced = val
121   - this.extra = {
  121 + this.extraParams = {
122 122 searchKey: this.searchKeywordDebounced || '',
123 123 mode: this.filterForm.mode || ''
124 124 }
... ... @@ -137,7 +137,7 @@
137 137 }
138 138 this.filterForm = payload
139 139 // 仅在确认时更新给 CardList 的条件
140   - this.extra = {
  140 + this.extraParams = {
141 141 searchKey: this.searchKeywordDebounced || '',
142 142 mode: payload.mode || ''
143 143 }
... ... @@ -191,7 +191,7 @@
191 191 ...query,
192 192 ...extra
193 193 }
194   - return queryApi(params).then(res => {
  194 + return myFlowQueryApi(params).then(res => {
195 195 const _data = res.data || {}
196 196 const records = _data.datas || _data.list || _data.records || []
197 197 const totalCount = _data.totalCount || _data.count || 0
... ... @@ -283,6 +283,7 @@
283 283 width: 160rpx;
284 284 margin-left: auto;
285 285 margin-right: 0;
  286 + padding: 0;
286 287 }
287 288 }
288 289
... ... @@ -303,7 +304,6 @@
303 304 color: rgba(0, 0, 0, 0.6);
304 305 font-size: 28rpx;
305 306 margin-bottom: 24rpx;
306   - height: 32rpx;
307 307
308 308 &:last-child {
309 309 margin-bottom: 0;
... ... @@ -311,6 +311,7 @@
311 311
312 312 text {
313 313 width: 60%;
  314 + line-height: 32rpx;
314 315
315 316 &:last-child {
316 317 color: rgba(0, 0, 0, 0.9);
... ...
... ... @@ -14,7 +14,7 @@
14 14
15 15 <!-- 顶部统计卡片 -->
16 16 <view class="stats-row">
17   - <view class="stat-card" @click="navigateTo('/pages/approval/index')">
  17 + <view class="stat-card" @click="navigateTo('/pages/flow/approval')">
18 18 <image class="card-bg" src="/static/images/index/card_wait.png" mode="aspectFill" />
19 19 <view class="card-content">
20 20 <text class="card-title">待审批的</text>
... ...