Commit 0b09d8ab56ded1fcb0b0cc030d2808a9bef65434

Authored by 史婷婷
1 parent dca65249

feat: 审核、审核详情、流程图、开发管理审核--暂存1

... ... @@ -24,4 +24,13 @@ export function taskListApi(params) {
24 24 method: 'get',
25 25 params
26 26 })
  27 +}
  28 +
  29 +// 根据流程实例ID获取审批链路(流程图 已审批和当前审批节点,未审批节点不返回)
  30 +export function getFlowLinkByInstanceIdApi(params) {
  31 + return request({
  32 + url: `/flow/task/getFlowLinkByInstanceId`,
  33 + method: 'get',
  34 + params
  35 + })
27 36 }
\ No newline at end of file
... ...
  1 +<template>
  2 + <view class="timeline">
  3 + <view v-for="(s, i) in renderNodeList" :key="i" class="node">
  4 + <view :class="['dot', s.active ? 'active' : '', !s.isFinish && isEnd && s.nodeName == '结束' ? 'red' : '']" />
  5 + <view class="line" />
  6 + <view class="content">
  7 + <view class="row">
  8 + <text class="name">{{ s.nodeCode === 'start' ? s.createBy : (s.nodeName + (s.createBy ? '-' + s.createBy :
  9 + '')) }}</text>
  10 + <text class="time">{{ s.createTime }}</text>
  11 + </view>
  12 + <view class="desc">{{ s.nodeCode === 'start' ? '发起' : (s.approvalDesc + (s.message ? ':' : '') + s.message) }}
  13 + </view>
  14 + </view>
  15 + </view>
  16 +
  17 + </view>
  18 +</template>
  19 +<script>
  20 +export default {
  21 + name: 'FlowTimeline',
  22 + props: {
  23 + nodeList: { type: Array, default: () => [] },
  24 + isEnd: { type: Boolean, default: false }
  25 + },
  26 + data() {
  27 + return { renderNodeList: [] }
  28 + },
  29 + watch: {
  30 + isEnd: {
  31 + immediate: true,
  32 + handler() {
  33 + this.updateRenderNodeList()
  34 + }
  35 + }
  36 + },
  37 + methods: {
  38 + updateRenderNodeList() {
  39 + this.renderNodeList = [];
  40 + let base = [...this.nodeList];
  41 + console.log('updateRenderNodeList__base', base)
  42 + console.log('updateRenderNodeList__this.isEnd', this.isEnd)
  43 + if (this.isEnd) {
  44 + const lastNode = base[base.length - 1] || {};
  45 + console.log('updateRenderNodeList__lastNode', lastNode)
  46 + const pass = lastNode.approval === 1;
  47 + // 审批结果(1:通过;2:退回;3:撤回;4:反对;5:终止)
  48 + base.push({ nodeName: pass ? '完成' : '结束', active: false, isFinish: pass });
  49 + } else {
  50 + if (base.length) {
  51 + base[base.length - 1].active = true;
  52 + }
  53 + }
  54 + this.renderNodeList = base.map(item => {
  55 + return {
  56 + approval: item.approval || 0,
  57 + approvalDesc: item.approvalDesc || '',
  58 + createBy: item.createBy || '',
  59 + createTime: item.createTime || '',
  60 + message: item.message || '',
  61 + nodeCode: item.nodeCode || '',
  62 + nodeName: item.nodeName || '',
  63 + active: item.active || false,
  64 + isFinish: item.approval === 1
  65 + }
  66 + }) || []
  67 + }
  68 + }
  69 +}
  70 +</script>
  71 +<style lang="scss" scoped>
  72 +.timeline {
  73 + background: #fff;
  74 + padding: 26rpx 32rpx;
  75 +
  76 + .node {
  77 + position: relative;
  78 + padding-left: 28rpx;
  79 + margin-bottom: 8rpx;
  80 +
  81 + .dot {
  82 + position: absolute;
  83 + top: 14rpx;
  84 + left: 0;
  85 + width: 16rpx;
  86 + height: 16rpx;
  87 + border: 1px solid $theme-primary;
  88 + border-radius: 50%;
  89 + background: $theme-primary;
  90 +
  91 + &.active {
  92 + background: #fff;
  93 + }
  94 +
  95 + &.red {
  96 + background: #D54941;
  97 + border-color: #D54941;
  98 + }
  99 + }
  100 +
  101 + .line {
  102 + position: absolute;
  103 + top: 52rpx;
  104 + left: 8rpx;
  105 + bottom: 0;
  106 + width: 2rpx;
  107 + background: #E7E7E7;
  108 + background: $theme-primary;
  109 + }
  110 +
  111 + &:last-child {
  112 + .line {
  113 + background: transparent;
  114 + }
  115 + }
  116 +
  117 + .content {
  118 + padding-left: 24rpx;
  119 + padding-bottom: 32rpx;
  120 +
  121 + .row {
  122 + display: flex;
  123 + justify-content: space-between;
  124 + margin-bottom: 8rpx;
  125 +
  126 + .name {
  127 + color: rgba(0, 0, 0, 0.9);
  128 + font-size: 28rpx;
  129 + line-height: 44rpx;
  130 + }
  131 +
  132 + .time {
  133 + color: rgba(0, 0, 0, 0.4);
  134 + font-size: 24rpx;
  135 + flex-shrink: 0;
  136 + line-height: 44rpx;
  137 + }
  138 + }
  139 +
  140 + .desc {
  141 + color: rgba(0, 0, 0, 0.6);
  142 + font-size: 24rpx;
  143 + line-height: 40rpx;
  144 + }
  145 +
  146 + }
  147 + }
  148 +}
  149 +</style>
\ No newline at end of file
... ...
... ... @@ -107,6 +107,22 @@
107 107 }
108 108 },
109 109 {
  110 + "path": "pages/flow/audit_detail",
  111 + "style": {
  112 + "navigationBarTitleText": "审核详情",
  113 + "navigationBarBackgroundColor": "#ffffff",
  114 + "navigationBarTextStyle": "black"
  115 + }
  116 + },
  117 + {
  118 + "path": "pages/flow/audit",
  119 + "style": {
  120 + "navigationBarTitleText": "审核",
  121 + "navigationBarBackgroundColor": "#ffffff",
  122 + "navigationBarTextStyle": "black"
  123 + }
  124 + },
  125 + {
110 126 "path": "pages/contract_framework/index",
111 127 "style": {
112 128 "navigationBarTitleText": "框架合同",
... ...
... ... @@ -610,9 +610,9 @@
610 610 padding: 0 14rpx;
611 611 border-radius: 6rpx;
612 612
613   - &.status_1 {
614   - background: #3D48A3;
615   - }
  613 + &.status_1 {
  614 + background: $theme-primary;
  615 + }
616 616
617 617 &.status_2 {
618 618 background: #2BA471;
... ...
  1 +<template>
  2 + <view class="viewer">
  3 + <view class="title">{{ inner.customerName }}</view>
  4 + <view class="grid">
  5 + <view class="row"><text class="label">企业类型</text><uni-easyinput v-model="inner.enterpriseType" :inputBorder="false" /></view>
  6 + <view class="row"><text class="label">区域</text><uni-easyinput v-model="inner.region" :inputBorder="false" /></view>
  7 + <view class="row"><text class="label">客户分类</text><uni-easyinput v-model="inner.companySuggestedCategory" :inputBorder="false" /></view>
  8 + <view class="row"><text class="label">登记日期</text><uni-datetime-picker type="date" v-model="inner.registerDate" /></view>
  9 + </view>
  10 + </view>
  11 +</template>
  12 +<script>
  13 +import { getDetailApi } from '@/api/devManage.js'
  14 +export default {
  15 + name: 'CustomerDevelopViewer',
  16 + props: { id: { type: [String, Number], default: '' } },
  17 + data() {
  18 + return { inner: { customerName: '', enterpriseType: '', region: '', companySuggestedCategory: '', registerDate: '' } }
  19 + },
  20 + watch: {
  21 + id: { immediate: true, handler(v) { if (v) { this.fetch(v) } } }
  22 + },
  23 + methods: {
  24 + fetch(id) {
  25 + getDetailApi(id).then(res => {
  26 + const d = res && res.data ? res.data : {}
  27 + this.inner = {
  28 + customerName: d.customerName || d.name || '',
  29 + enterpriseType: d.enterpriseType || d.type || '',
  30 + region: d.region || d.area || '',
  31 + companySuggestedCategory: d.companySuggestedCategory || d.category || '',
  32 + registerDate: d.registerDate || d.createTime || ''
  33 + }
  34 + }).catch(() => {})
  35 + },
  36 + getFormValues() { return { id: this.id, ...this.inner } }
  37 + }
  38 +}
  39 +</script>
  40 +<style lang="scss" scoped>
  41 +.viewer { background: #fff; padding: 24rpx }
  42 +.title { font-size: 36rpx; font-weight: 600; color: #323241; margin-bottom: 24rpx }
  43 +.grid { display: flex; flex-direction: column; gap: 20rpx }
  44 +.row { display: flex; align-items: center; justify-content: space-between }
  45 +.label { width: 220rpx; color: rgba(0,0,0,0.6); font-size: 28rpx }
  46 +</style>
\ No newline at end of file
... ...
... ... @@ -16,11 +16,11 @@
16 16 </view>
17 17 </view>
18 18
19   -
  19 +
20 20
21 21 <view class="list-box">
22 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">
  23 + :enable-load-more="true" row-key="instanceId" @loaded="onLoaded" @error="onError">
24 24 <template v-slot="{ item }">
25 25 <view class="card">
26 26 <view class="card-header">
... ... @@ -38,8 +38,9 @@
38 38 <view class="footer">
39 39 <!-- completed true 已办 显示 审核详情按钮 -->
40 40 <!-- completed false 待办 显示 去审核按钮 -->
41   - <button v-if="item.completed" class="btn" type="primary" plain @click.stop="onDetail(item)">审核详情</button>
42   - <button class="btn" type="primary" plain @click.stop="onDetail(item)">去审核</button>
  41 + <button v-if="item.completed" class="btn" type="primary" plain
  42 + @click.stop="onGoDetail(item)">审核详情</button>
  43 + <button v-else class="btn" type="primary" plain @click.stop="onGoAudit(item)">去审核</button>
43 44 </view>
44 45 </view>
45 46 </template>
... ... @@ -67,366 +68,390 @@
67 68 </template>
68 69
69 70 <script>
70   - import CardList from '@/components/card/index.vue'
71   - import FilterModal from '@/components/filter/index.vue'
72   - import SingleSelectSheet from '@/components/single-select/index.vue'
73   - import {
74   - taskListApi,
75   - statisticsCountApi
76   - } from '@/api/flow.js'
77   - import {
78   - getDicByCodes
79   - } from '@/utils/dic'
80   - import {
81   - getDicName
82   - } from '@/utils/dic.js'
83   -
84   - export default {
85   - name: 'MyFlow',
86   - components: {
87   - CardList,
88   - FilterModal,
89   - SingleSelectSheet
90   - },
91   - data() {
92   - return {
93   - searchKeyword: '',
94   - searchKeywordDebounced: '',
95   - searchDebounceTimer: null,
96   - tabValue: '1',
97   - isCompleted: null,
98   - tabs: [],
99   - filterVisible: false,
100   - filterForm: {
101   - mode: '',
102   - modeName: ''
103   - },
104   - modeSelectVisible: false,
105   - modeOptions: [],
106   - cardModeOptions: [],
107   - query: {},
108   - extraParams: {},
109   - currentItems: []
110   - }
111   - },
112   - created() {
113   - this.loadModeOptions()
114   - this.getStatisticsFun()
115   - },
116   - onReachBottom() {
117   - if (this.$refs && this.$refs.cardRef && this.$refs.cardRef.onLoadMore) {
118   - this.$refs.cardRef.onLoadMore()
119   - }
120   - },
121   - watch: {},
122   - methods: {
123   - onSearchInput(val) {
124   - if (this.searchDebounceTimer) clearTimeout(this.searchDebounceTimer)
125   - this.searchDebounceTimer = setTimeout(() => {
126   - this.searchKeywordDebounced = this.searchKeyword
127   - this.searchDebounceTimer = null
128   - this.extraParams = {
129   - searchKey: this.searchKeywordDebounced || '',
130   - mode: this.filterForm.mode || '',
131   - isCompleted: this.isCompleted
132   - }
133   - }, 800)
  71 +import CardList from '@/components/card/index.vue'
  72 +import FilterModal from '@/components/filter/index.vue'
  73 +import SingleSelectSheet from '@/components/single-select/index.vue'
  74 +import {
  75 + taskListApi,
  76 + statisticsCountApi
  77 +} from '@/api/flow.js'
  78 +import {
  79 + getDicByCodes
  80 +} from '@/utils/dic'
  81 +import {
  82 + getDicName
  83 +} from '@/utils/dic.js'
  84 +
  85 +export default {
  86 + name: 'MyFlow',
  87 + components: {
  88 + CardList,
  89 + FilterModal,
  90 + SingleSelectSheet
  91 + },
  92 + data() {
  93 + return {
  94 + searchKeyword: '',
  95 + searchKeywordDebounced: '',
  96 + searchDebounceTimer: null,
  97 + tabValue: '1',
  98 + isCompleted: null,
  99 + tabs: [],
  100 + filterVisible: false,
  101 + filterForm: {
  102 + mode: '',
  103 + modeName: ''
134 104 },
135   - onSearchConfirm(e) {
136   - const val = e && e.value != null ? e.value : this.searchKeyword
137   - this.searchKeyword = val
138   - this.searchKeywordDebounced = val
  105 + modeSelectVisible: false,
  106 + modeOptions: [],
  107 + cardModeOptions: [],
  108 + query: {},
  109 + extraParams: {},
  110 + currentItems: []
  111 + }
  112 + },
  113 + created() {
  114 + this.loadModeOptions()
  115 + this.getStatisticsFun()
  116 + },
  117 + onReachBottom() {
  118 + if (this.$refs && this.$refs.cardRef && this.$refs.cardRef.onLoadMore) {
  119 + this.$refs.cardRef.onLoadMore()
  120 + }
  121 + },
  122 + watch: {},
  123 + methods: {
  124 + onSearchInput(val) {
  125 + if (this.searchDebounceTimer) clearTimeout(this.searchDebounceTimer)
  126 + this.searchDebounceTimer = setTimeout(() => {
  127 + this.searchKeywordDebounced = this.searchKeyword
  128 + this.searchDebounceTimer = null
139 129 this.extraParams = {
140 130 searchKey: this.searchKeywordDebounced || '',
141 131 mode: this.filterForm.mode || '',
142 132 isCompleted: this.isCompleted
143 133 }
144   - },
145   - openFilter() {
146   - this.filterVisible = true
147   - },
148   - onFilterReset(payload) {
149   - this.filterForm = payload
150   - },
151   - onFilterConfirm(payload) {
152   - // 兜底:部分端可能未同步 v-model
153   - if ((payload.mode === '' || payload.mode == null) && this.filterForm.mode !== '') {
154   - payload.mode = this.filterForm.mode
155   - payload.modeName = this.filterForm.modeName
156   - }
157   - this.filterForm = payload
158   - // 仅在确认时更新给 CardList 的条件
159   - this.extraParams = {
160   - searchKey: this.searchKeywordDebounced || '',
161   - mode: payload.mode || '',
162   - isCompleted: this.isCompleted
163   - }
164   - this.filterVisible = false
165   - },
166   - openModeSelect() {
167   - this.modeSelectVisible = true
168   - },
169   - onModeConfirm({
170   - value,
171   - label
172   - }) {
173   - this.filterForm.mode = value || '';
174   - this.filterForm.modeName = label || '';
175   - },
176   - onLoaded({
177   - items
178   - }) {
179   - this.currentItems = items || []
180   - },
181   - onError() {
182   - uni.showToast({
183   - title: '列表加载失败',
184   - icon: 'none'
185   - })
186   - },
187   - async loadModeOptions() {
188   - try {
189   - const dicCodes = ['PROCESS_MODE']
190   - const results = await getDicByCodes(dicCodes)
191   - const option = results.PROCESS_MODE.data || []
192   - this.cardModeOptions = Array.isArray(option) ? option : [];
193   - this.modeOptions = option.map(it => ({
194   - label: it.name || '',
195   - value: it.code || ''
196   - }))
197   - } catch (e) {
198   - this.modeOptions = [];
199   - this.cardModeOptions = [];
  134 + }, 800)
  135 + },
  136 + onSearchConfirm(e) {
  137 + const val = e && e.value != null ? e.value : this.searchKeyword
  138 + this.searchKeyword = val
  139 + this.searchKeywordDebounced = val
  140 + this.extraParams = {
  141 + searchKey: this.searchKeywordDebounced || '',
  142 + mode: this.filterForm.mode || '',
  143 + isCompleted: this.isCompleted
  144 + }
  145 + },
  146 + openFilter() {
  147 + this.filterVisible = true
  148 + },
  149 + onFilterReset(payload) {
  150 + this.filterForm = payload
  151 + },
  152 + onFilterConfirm(payload) {
  153 + // 兜底:部分端可能未同步 v-model
  154 + if ((payload.mode === '' || payload.mode == null) && this.filterForm.mode !== '') {
  155 + payload.mode = this.filterForm.mode
  156 + payload.modeName = this.filterForm.modeName
  157 + }
  158 + this.filterForm = payload
  159 + // 仅在确认时更新给 CardList 的条件
  160 + this.extraParams = {
  161 + searchKey: this.searchKeywordDebounced || '',
  162 + mode: payload.mode || '',
  163 + isCompleted: this.isCompleted
  164 + }
  165 + this.filterVisible = false
  166 + },
  167 + openModeSelect() {
  168 + this.modeSelectVisible = true
  169 + },
  170 + onModeConfirm({
  171 + value,
  172 + label
  173 + }) {
  174 + this.filterForm.mode = value || '';
  175 + this.filterForm.modeName = label || '';
  176 + },
  177 + onLoaded({
  178 + items
  179 + }) {
  180 + this.currentItems = items || []
  181 + },
  182 + onError() {
  183 + uni.showToast({
  184 + title: '列表加载失败',
  185 + icon: 'none'
  186 + })
  187 + },
  188 + async loadModeOptions() {
  189 + try {
  190 + const dicCodes = ['PROCESS_MODE']
  191 + const results = await getDicByCodes(dicCodes)
  192 + const option = results.PROCESS_MODE.data || []
  193 + this.cardModeOptions = Array.isArray(option) ? option : [];
  194 + this.modeOptions = option.map(it => ({
  195 + label: it.name || '',
  196 + value: it.code || ''
  197 + }))
  198 + } catch (e) {
  199 + this.modeOptions = [];
  200 + this.cardModeOptions = [];
  201 + }
  202 + },
  203 + getStatisticsFun() {
  204 + statisticsCountApi().then((res) => {
  205 + this.tabs = [{
  206 + label: `全部(${res.data.allCount > 999 ? '999+' : res.data.allCount || 0})`,
  207 + value: '1'
  208 + },
  209 + {
  210 + label: `待办(${res.data.todoCount > 999 ? '999+' : res.data.todoCount || 0})`,
  211 + value: '2'
  212 + },
  213 + {
  214 + label: `已办(${res.data.completedCount > 999 ? '999+' : res.data.completedCount || 0})`,
  215 + value: '3'
200 216 }
201   - },
202   - getStatisticsFun() {
203   - statisticsCountApi().then((res) => {
204   - this.tabs = [{
205   - label: `全部(${res.data.allCount > 999 ? '999+' : res.data.allCount || 0})`,
206   - value: '1'
207   - },
208   - {
209   - label: `待办(${res.data.todoCount > 999 ? '999+' : res.data.todoCount || 0})`,
210   - value: '2'
211   - },
212   - {
213   - label: `已办(${res.data.completedCount > 999 ? '999+' : res.data.completedCount || 0})`,
214   - value: '3'
215   - }
216   - ]
217   - });
218   - },
219   - fetchList({
  217 + ]
  218 + });
  219 + },
  220 + fetchList({
  221 + pageIndex,
  222 + pageSize,
  223 + query,
  224 + extra
  225 + }) {
  226 + const params = {
220 227 pageIndex,
221 228 pageSize,
222   - query,
223   - extra
224   - }) {
225   - const params = {
226   - pageIndex,
227   - pageSize,
228   - ...query,
229   - ...extra
  229 + ...query,
  230 + ...extra
  231 + }
  232 + return taskListApi(params).then(res => {
  233 + const _data = res.data || {}
  234 + const records = _data.datas || _data.list || _data.records || []
  235 + const totalCount = _data.totalCount || _data.count || 0
  236 + const hasNext = _data.hasNext || false
  237 + return {
  238 + records,
  239 + totalCount,
  240 + hasNext
230 241 }
231   - return taskListApi(params).then(res => {
232   - const _data = res.data || {}
233   - const records = _data.datas || _data.list || _data.records || []
234   - const totalCount = _data.totalCount || _data.count || 0
235   - const hasNext = _data.hasNext || false
236   - return {
237   - records,
238   - totalCount,
239   - hasNext
240   - }
241   - }).catch(() => {
242   - this.onError()
243   - return {
244   - records: [],
245   - totalCount: 0,
246   - hasNext: false
247   - }
248   - })
249   - },
250   - onDetail(item) {
251   - uni.showToast({
252   - title: '审核详情',
253   - icon: 'none'
254   - })
255   - },
256   - getDicName: getDicName,
257   - switchTab(item) {
258   - this.tabValue = item.value;
259   - this.isCompleted = item.value === '3' ? true : item.value === '2' ? false : null;
260   - this.extraParams = {
261   - searchKey: this.searchKeywordDebounced || '',
262   - mode: this.filterForm.mode || '',
263   - isCompleted: this.isCompleted
  242 + }).catch(() => {
  243 + this.onError()
  244 + return {
  245 + records: [],
  246 + totalCount: 0,
  247 + hasNext: false
264 248 }
265   - },
266   - }
  249 + })
  250 + },
  251 + onGoDetail(item) {
  252 + const CACHE_KEY = 'FLOW_AUDIT_CACHE';
  253 + console.log('onGoDetail___item', item)
  254 + const _ext = JSON.parse(item.ext || '{}');
  255 + const payload = {
  256 + taskId: item.taskId || '',
  257 + instanceId: item.instanceId || '',
  258 + businessId: item.businessId || '',
  259 + bizFlag: _ext.bizFlag || ''
  260 + }
  261 + uni.setStorageSync(CACHE_KEY, payload)
  262 + uni.navigateTo({ url: '/pages/flow/audit_detail' })
  263 + },
  264 + onGoAudit(item) {
  265 + const CACHE_KEY = 'FLOW_AUDIT_CACHE';
  266 + console.log('onGoAudit___item', item)
  267 + const _ext = JSON.parse(item.ext || '{}');
  268 + // todo 审批 在bizFlag值后面拼 _EDIT,这个配置一个新的审核组件
  269 + // 1.在映射的地方,如果审核和审批详情一样 就映射成同一个组件
  270 + // 2.在映射的地方,如果审核和审批详情不一样 就映射成两一个组件
  271 + const payload = {
  272 + taskId: item.taskId || '',
  273 + instanceId: item.instanceId || '',
  274 + businessId: item.businessId || '',
  275 + bizFlag: _ext.bizFlag + '_EDIT'
  276 + }
  277 + uni.setStorageSync(CACHE_KEY, payload)
  278 + uni.navigateTo({ url: '/pages/flow/audit' })
  279 + },
  280 + getDicName: getDicName,
  281 + switchTab(item) {
  282 + this.tabValue = item.value;
  283 + this.isCompleted = item.value === '3' ? true : item.value === '2' ? false : null;
  284 + this.extraParams = {
  285 + searchKey: this.searchKeywordDebounced || '',
  286 + mode: this.filterForm.mode || '',
  287 + isCompleted: this.isCompleted
  288 + }
  289 + },
267 290 }
  291 +}
268 292 </script>
269 293
270 294 <style lang="scss" scoped>
271   - .page {
272   - display: flex;
273   - flex-direction: column;
274   - height: 100vh;
275   - }
  295 +.page {
  296 + display: flex;
  297 + flex-direction: column;
  298 + height: 100vh;
  299 +}
  300 +
  301 +.fixed {
  302 + position: fixed;
  303 + top: 96rpx;
  304 + left: 0;
  305 + right: 0;
  306 + z-index: 2;
  307 + background: #fff;
  308 +}
  309 +
  310 +.search-row {
  311 + display: flex;
  312 + align-items: center;
  313 + padding: 16rpx 32rpx;
276 314
277   - .fixed {
278   - position: fixed;
279   - top: 96rpx;
280   - left: 0;
281   - right: 0;
282   - z-index: 2;
283   - background: #fff;
  315 + .tool-icon {
  316 + width: 48rpx;
  317 + height: 48rpx;
  318 + margin-left: 16rpx;
284 319 }
285 320
286   - .search-row {
287   - display: flex;
288   - align-items: center;
289   - padding: 16rpx 32rpx;
  321 + .uni-searchbar {
  322 + padding: 0;
  323 + flex: 1;
  324 + }
  325 +}
290 326
291   - .tool-icon {
292   - width: 48rpx;
293   - height: 48rpx;
294   - margin-left: 16rpx;
295   - }
  327 +/* 仅当前页覆盖 uni-search-bar 盒子高度 */
  328 +::v-deep .uni-searchbar__box {
  329 + height: 80rpx !important;
  330 + justify-content: start;
296 331
297   - .uni-searchbar {
298   - padding: 0;
299   - flex: 1;
300   - }
  332 + .uni-searchbar__box-search-input {
  333 + font-size: 32rpx !important;
301 334 }
  335 +}
302 336
303   - /* 仅当前页覆盖 uni-search-bar 盒子高度 */
304   - ::v-deep .uni-searchbar__box {
305   - height: 80rpx !important;
306   - justify-content: start;
  337 +.tabs {
  338 + display: flex;
  339 + align-items: center;
  340 + justify-content: space-between;
  341 + padding: 26rpx 34rpx;
307 342
308   - .uni-searchbar__box-search-input {
309   - font-size: 32rpx !important;
310   - }
  343 + .tab {
  344 + width: 180rpx;
  345 + text-align: center;
  346 + color: #666;
  347 + color: rgba(0, 0, 0, 0.9);
  348 + line-height: 44rpx;
  349 + position: relative;
311 350 }
312 351
313   - .tabs {
314   - display: flex;
315   - align-items: center;
316   - justify-content: space-between;
317   - padding: 26rpx 34rpx;
  352 + .tab.active {
  353 + color: $theme-primary;
  354 + font-weight: 600;
  355 + }
318 356
319   - .tab {
320   - width: 180rpx;
321   - text-align: center;
322   - color: #666;
323   - color: rgba(0, 0, 0, 0.9);
324   - line-height: 44rpx;
325   - position: relative;
326   - }
  357 + .tab.active::after {
  358 + content: '';
  359 + position: absolute;
  360 + left: 50%;
  361 + transform: translateX(-50%);
  362 + bottom: -13px;
  363 + width: 32rpx;
  364 + height: 6rpx;
  365 + border-radius: 4rpx;
  366 + background: $theme-primary;
  367 + }
  368 +}
327 369
328   - .tab.active {
329   - color: $theme-primary;
330   - font-weight: 600;
331   - }
  370 +.list-box {
  371 + flex: 1;
  372 + padding: 236rpx 0 0
  373 +}
332 374
333   - .tab.active::after {
334   - content: '';
335   - position: absolute;
336   - left: 50%;
337   - transform: translateX(-50%);
338   - bottom: -13px;
339   - width: 32rpx;
340   - height: 6rpx;
341   - border-radius: 4rpx;
342   - background: $theme-primary;
  375 +.card {
  376 + .footer {
  377 + margin-top: 28rpx;
  378 + box-shadow: inset 0px 1px 0px 0px #E7E7E7;
  379 + padding-top: 22rpx;
  380 + display: flex;
  381 + justify-content: flex-end;
  382 +
  383 + .btn {
  384 + height: 64rpx;
  385 + line-height: 60rpx;
  386 + font-size: 28rpx;
  387 + width: 160rpx;
  388 + margin-left: 22rpx;
  389 + margin-right: 0;
  390 + padding: 0;
343 391 }
344 392 }
345 393
346   - .list-box {
347   - flex: 1;
348   - padding: 236rpx 0 0
349   - }
  394 + .card-header {
  395 + margin-bottom: 28rpx;
350 396
351   - .card {
352   - .footer {
353   - margin-top: 28rpx;
354   - box-shadow: inset 0px 1px 0px 0px #E7E7E7;
355   - padding-top: 22rpx;
356   - display: flex;
357   - justify-content: flex-end;
358   - .btn {
359   - height: 64rpx;
360   - line-height: 60rpx;
361   - font-size: 28rpx;
362   - width: 160rpx;
363   - margin-left: 22rpx;
364   - margin-right: 0;
365   - padding: 0;
366   - }
  397 + .title {
  398 + font-size: 36rpx;
  399 + font-weight: 600;
  400 + line-height: 50rpx;
  401 + color: rgba(0, 0, 0, 0.9);
367 402 }
  403 + }
368 404
369   - .card-header {
370   - margin-bottom: 28rpx;
  405 + .info-row {
  406 + display: flex;
  407 + align-items: center;
  408 + color: rgba(0, 0, 0, 0.6);
  409 + font-size: 28rpx;
  410 + margin-bottom: 24rpx;
371 411
372   - .title {
373   - font-size: 36rpx;
374   - font-weight: 600;
375   - line-height: 50rpx;
376   - color: rgba(0,0,0,0.9);
377   - }
  412 + &:last-child {
  413 + margin-bottom: 0;
378 414 }
379 415
380   - .info-row {
381   - display: flex;
382   - align-items: center;
383   - color: rgba(0, 0, 0, 0.6);
384   - font-size: 28rpx;
385   - margin-bottom: 24rpx;
  416 + text {
  417 + width: 60%;
  418 + line-height: 32rpx;
386 419
387 420 &:last-child {
388   - margin-bottom: 0;
389   - }
390   -
391   - text {
392   - width: 60%;
393   - line-height: 32rpx;
394   -
395   - &:last-child {
396   - color: rgba(0, 0, 0, 0.9);
397   - width: 40%;
398   - }
  421 + color: rgba(0, 0, 0, 0.9);
  422 + width: 40%;
399 423 }
400 424 }
401 425 }
  426 +}
402 427
403   - .filter-form {
404   - .form-item {
405   - margin-bottom: 24rpx;
406   - }
  428 +.filter-form {
  429 + .form-item {
  430 + margin-bottom: 24rpx;
  431 + }
407 432
408   - .label {
409   - margin-bottom: 20rpx;
410   - color: rgba(0, 0, 0, 0.9);
411   - height: 44rpx;
412   - line-height: 44rpx;
413   - font-size: 30rpx;
414   - }
  433 + .label {
  434 + margin-bottom: 20rpx;
  435 + color: rgba(0, 0, 0, 0.9);
  436 + height: 44rpx;
  437 + line-height: 44rpx;
  438 + font-size: 30rpx;
  439 + }
415 440
416   - .fake-select {
417   - height: 80rpx;
418   - line-height: 80rpx;
419   - padding: 0 20rpx;
420   - background: #f3f3f3;
421   - border-radius: 12rpx;
  441 + .fake-select {
  442 + height: 80rpx;
  443 + line-height: 80rpx;
  444 + padding: 0 20rpx;
  445 + background: #f3f3f3;
  446 + border-radius: 12rpx;
422 447
423   - .placeholder {
424   - color: #999;
425   - }
  448 + .placeholder {
  449 + color: #999;
  450 + }
426 451
427   - .value {
428   - color: #333;
429   - }
  452 + .value {
  453 + color: #333;
430 454 }
431 455 }
  456 +}
432 457 </style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <view class="page">
  3 + <view class="fixed">
  4 + <view class="tabs" :class="[`${tab === 'base' ? 'bg1' : 'bg2'}`]">
  5 + <view :class="['tab', { active: tab === 'base' }]" @click="tab = 'base'">基础信息</view>
  6 + <view :class="['tab', { active: tab === 'flow' }]" @click="tab = 'flow'">流程</view>
  7 + </view>
  8 + </view>
  9 + <view class="body">
  10 + <view v-if="tab === 'base'">
  11 + <component :is="componentName" :id="auditCtx.businessId" ref="basicRef" />
  12 + </view>
  13 + <view v-else>
  14 + <FlowTimeline :nodeList="nodeList" :isEnd="isEnd" />
  15 + </view>
  16 + </view>
  17 + <view class="footer">
  18 + <button v-if="extraBtnText" class="btn extra" @click="onExtra">{{ extraBtnText }}</button>
  19 + <button class="btn reject" @click="onReject">驳回</button>
  20 + <button class="btn pass" type="primary" @click="onPass">通过</button>
  21 + </view>
  22 +
  23 + <uni-popup ref="approvePopup" type="center" :mask-click="false">
  24 + <view class="action-modal">
  25 + <view class="action-modal_header">{{ approveType === 'PASS' ? '通过' : '驳回' }}</view>
  26 + <view class="action-modal_body">
  27 + <text class="tip">{{ approveType === 'PASS' ? '您将通过该信息的审核' : '您将驳回该信息的审核' }}</text>
  28 + <uni-easyinput v-model="approveComment"
  29 + :placeholder="approveType === 'PASS' ? '请输入通过原因' : '请输入驳回原因'" />
  30 + </view>
  31 + <view class="action-modal_footer">
  32 + <button class="btn cancel" @click="cancelApprove">取消</button>
  33 + <button class="btn confirm" type="primary" @click="confirmApprove">{{ approveType === 'PASS' ? '通过'
  34 + : '驳回' }}</button>
  35 + </view>
  36 + </view>
  37 + </uni-popup>
  38 + </view>
  39 +</template>
  40 +<script>
  41 +import FlowTimeline from '@/components/flow-timeline/index.vue'
  42 +import { getSysFlowComponentPath } from '@/utils/flow-components.js'
  43 +import { getFlowLinkByInstanceIdApi } from '@/api/flow.js'
  44 +export default {
  45 + components: {
  46 + FlowTimeline
  47 + },
  48 + data() {
  49 + return {
  50 + tab: 'base',
  51 + auditCtx: {
  52 + taskId: '',
  53 + instanceId: '',
  54 + businessId: '',
  55 + bizFlag: ''
  56 + },
  57 + isEnd: false, // 流程是否结束
  58 + nodeList: [],
  59 + extraBtnText: '客户信息',
  60 + approveType: 'PASS',
  61 + approveComment: ''
  62 + }
  63 + },
  64 + computed: {
  65 + componentName() {
  66 + const name = getSysFlowComponentPath(this.auditCtx.bizFlag || '')
  67 + return name || ''
  68 + }
  69 + },
  70 + onLoad() {
  71 + const CACHE_KEY = 'FLOW_AUDIT_CACHE'
  72 + const cache = uni.getStorageSync(CACHE_KEY) || {};
  73 + console.log('审核__cache', cache)
  74 + this.auditCtx = cache;
  75 + if (this.auditCtx && this.auditCtx.instanceId) {
  76 + this.loadFlowLinkByInstanceId(this.auditCtx.instanceId)
  77 + }
  78 + },
  79 + methods: {
  80 + loadFlowLinkByInstanceId(instanceId) {
  81 + getFlowLinkByInstanceIdApi({ instanceId }).then(res => {
  82 + console.log('审核__loadFlowLinkByInstanceId', res)
  83 + const _data = res && res.data ? res.data : {};
  84 + this.nodeList = _data.nodeList || [];
  85 + this.isEnd = _data.end || false;
  86 + }).catch(() => { })
  87 + },
  88 + getPayload() {
  89 + const ref = this.$refs.basicRef
  90 + const vals = ref && typeof ref.getFormValues === 'function' ? ref.getFormValues() : {}
  91 + return {
  92 + bizFlag: this.auditCtx.bizFlag,
  93 + ...vals
  94 + }
  95 + },
  96 + onReject() {
  97 + this.approveType = 'REFUSE'
  98 + this.approveComment = ''
  99 + this.$refs.approvePopup.open()
  100 + },
  101 + onPass() {
  102 + this.approveType = 'PASS'
  103 + this.approveComment = ''
  104 + this.$refs.approvePopup.open()
  105 + },
  106 + onExtra() {
  107 + const payload = this.getPayload()
  108 + uni.showToast({
  109 + title: this.extraBtnText,
  110 + icon: 'none'
  111 + })
  112 + },
  113 + cancelApprove() {
  114 + this.$refs.approvePopup.close()
  115 + },
  116 + confirmApprove() {
  117 + const payload = {
  118 + ...this.getPayload(),
  119 + approveType: this.approveType,
  120 + approveComment: this.approveComment
  121 + }
  122 + this.$refs.approvePopup.close()
  123 + uni.showToast({
  124 + title: this.approveType === 'PASS' ? '已通过' : '已驳回',
  125 + icon: 'none'
  126 + })
  127 + }
  128 + }
  129 +}
  130 +</script>
  131 +<style lang="scss" scoped>
  132 +.page {
  133 + display: flex;
  134 + flex-direction: column;
  135 + height: 100vh
  136 +}
  137 +
  138 +.fixed {
  139 + position: fixed;
  140 + top: 96rpx;
  141 + left: 0;
  142 + right: 0;
  143 + z-index: 2;
  144 +
  145 + .tabs {
  146 + display: flex;
  147 + align-items: center;
  148 + height: 96rpx;
  149 +
  150 + &.bg1 {
  151 + background-image: url('/static/images/flow/tab_1_icon.png');
  152 + background-repeat: no-repeat;
  153 + background-position: right center;
  154 + background-size: cover;
  155 + }
  156 +
  157 + &.bg2 {
  158 + background-image: url('/static/images/flow/tab_2_icon.png');
  159 + background-repeat: no-repeat;
  160 + background-position: right center;
  161 + background-size: cover;
  162 + }
  163 +
  164 + .tab {
  165 + width: 50%;
  166 + height: 96rpx;
  167 + line-height: 96rpx;
  168 + text-align: center;
  169 + font-weight: 600;
  170 + color: rgba(0, 0, 0, 0.9);
  171 +
  172 + &.active {
  173 + color: $theme-primary;
  174 + }
  175 + }
  176 + }
  177 +}
  178 +
  179 +.body {
  180 + flex: 1;
  181 + padding-top: 104rpx;
  182 + padding-bottom: 150rpx
  183 +}
  184 +
  185 +.footer {
  186 + position: fixed;
  187 + left: 0;
  188 + right: 0;
  189 + bottom: 0;
  190 + padding: 32rpx;
  191 + padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
  192 + background: #fff;
  193 + box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06);
  194 + display: flex;
  195 + gap: 32rpx;
  196 +
  197 + .btn {
  198 + height: 80rpx;
  199 + line-height: 80rpx;
  200 + border-radius: 12rpx;
  201 + font-size: 32rpx;
  202 + flex: 1;
  203 + }
  204 +
  205 + .reject {
  206 + background: #fff;
  207 + color: #D54941;
  208 + border: 1rpx solid #D54941;
  209 + }
  210 +
  211 + .pass {
  212 + background: $theme-primary;
  213 + color: #fff;
  214 + }
  215 +
  216 + .extra {
  217 + background: #fff;
  218 + color: $theme-primary;
  219 + border: none;
  220 +
  221 + &::after {
  222 + border: none;
  223 + }
  224 + }
  225 +}
  226 +
  227 +.action-modal {
  228 + width: 560rpx;
  229 + padding: 32rpx 28rpx 20rpx;
  230 + background: #fff;
  231 + border-radius: 20rpx;
  232 +
  233 + &_header {
  234 + text-align: center;
  235 + font-size: 34rpx;
  236 + font-weight: 600;
  237 + margin-bottom: 12rpx;
  238 + color: rgba(0, 0, 0, 0.9);
  239 + }
  240 +
  241 + &_body {
  242 + padding: 12rpx 4rpx 24rpx;
  243 +
  244 + .tip {
  245 + display: block;
  246 + text-align: center;
  247 + font-size: 32rpx;
  248 + color: rgba(0, 0, 0, 0.6);
  249 + margin-bottom: 32rpx;
  250 + }
  251 +
  252 + ::v-deep .uni-easyinput {
  253 + width: 100%;
  254 + }
  255 + }
  256 +
  257 + &_footer {
  258 + display: flex;
  259 + justify-content: space-between;
  260 + gap: 16rpx;
  261 +
  262 + .btn {
  263 + flex: 1;
  264 + height: 80rpx;
  265 + line-height: 80rpx;
  266 + border-radius: 12rpx;
  267 + font-size: 30rpx;
  268 + font-size: 32rpx;
  269 +
  270 + &::after {
  271 + border: none;
  272 + }
  273 + }
  274 +
  275 + .cancel {
  276 + background: #fff;
  277 + color: rgba(0, 0, 0, 0.9);
  278 + }
  279 +
  280 + .confirm {
  281 + background: #fff !important;
  282 + color: $theme-primary !important;
  283 + }
  284 + }
  285 +}
  286 +</style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <view class="page">
  3 + <view class="fixed">
  4 + <view class="tabs" :class="[`${tab === 'base' ? 'bg1' : 'bg2'}`]">
  5 + <view :class="['tab', { active: tab === 'base' }]" @click="tab = 'base'">基础信息</view>
  6 + <view :class="['tab', { active: tab === 'flow' }]" @click="tab = 'flow'">流程</view>
  7 + </view>
  8 + </view>
  9 + <view class="body">
  10 + <view v-if="tab === 'base'">
  11 + <component :is="componentName" :id="auditCtx.businessId" ref="basicRef" />
  12 + </view>
  13 + <view v-else>
  14 + <FlowTimeline :nodeList="nodeList" :isEnd="isEnd" />
  15 + </view>
  16 + </view>
  17 + </view>
  18 +</template>
  19 +<script>
  20 +import FlowTimeline from '@/components/flow-timeline/index.vue'
  21 +import { getSysFlowComponentPath } from '@/utils/flow-components.js'
  22 +import { getFlowLinkByInstanceIdApi } from '@/api/flow.js'
  23 +export default {
  24 + components: {
  25 + FlowTimeline
  26 + },
  27 + data() {
  28 + return {
  29 + tab: 'base',
  30 + auditCtx: {
  31 + taskId: '',
  32 + instanceId: '',
  33 + businessId: '',
  34 + bizFlag: ''
  35 + },
  36 + isEnd: false, // 流程是否结束
  37 + nodeList: []
  38 + }
  39 + },
  40 + computed: {
  41 + componentName() {
  42 + const name = getSysFlowComponentPath(this.auditCtx.bizFlag || '')
  43 + return name || ''
  44 + }
  45 + },
  46 + onLoad() {
  47 + const CACHE_KEY = 'FLOW_AUDIT_CACHE'
  48 + const cache = uni.getStorageSync(CACHE_KEY) || {};
  49 + console.log('审核详情__cache', cache)
  50 + this.auditCtx = cache;
  51 + if (this.auditCtx && this.auditCtx.instanceId) {
  52 + this.loadFlowLinkByInstanceId(this.auditCtx.instanceId)
  53 + }
  54 + },
  55 + methods: {
  56 + loadFlowLinkByInstanceId(instanceId) {
  57 + getFlowLinkByInstanceIdApi({ instanceId }).then(res => {
  58 + console.log('审核__loadFlowLinkByInstanceId', res)
  59 + const _data = res && res.data ? res.data : {};
  60 + this.nodeList = _data.nodeList || [];
  61 + this.isEnd = _data.end || false;
  62 + }).catch(() => { })
  63 + },
  64 + }
  65 +}
  66 +</script>
  67 +<style lang="scss" scoped>
  68 +.page {
  69 + display: flex;
  70 + flex-direction: column;
  71 + height: 100vh
  72 +}
  73 +
  74 +.fixed {
  75 + position: fixed;
  76 + top: 96rpx;
  77 + left: 0;
  78 + right: 0;
  79 + z-index: 2;
  80 +
  81 + .tabs {
  82 + display: flex;
  83 + align-items: center;
  84 + height: 96rpx;
  85 +
  86 + &.bg1 {
  87 + background-image: url('/static/images/flow/tab_1_icon.png');
  88 + background-repeat: no-repeat;
  89 + background-position: right center;
  90 + background-size: cover;
  91 + }
  92 +
  93 + &.bg2 {
  94 + background-image: url('/static/images/flow/tab_2_icon.png');
  95 + background-repeat: no-repeat;
  96 + background-position: right center;
  97 + background-size: cover;
  98 + }
  99 +
  100 + .tab {
  101 + width: 50%;
  102 + height: 96rpx;
  103 + line-height: 96rpx;
  104 + text-align: center;
  105 + font-weight: 600;
  106 + color: rgba(0, 0, 0, 0.9);
  107 +
  108 + &.active {
  109 + color: $theme-primary;
  110 + }
  111 + }
  112 + }
  113 +}
  114 +
  115 +.body {
  116 + flex: 1;
  117 + padding-top: 104rpx;
  118 +}
  119 +</style>
\ No newline at end of file
... ...
... ... @@ -11,7 +11,7 @@
11 11
12 12 <view class="list-box">
13 13 <CardList ref="cardRef" :fetchFn="fetchList" :query="query" :extra="extraParams" :enable-refresh="true"
14   - :enable-load-more="true" row-key="id" @loaded="onLoaded" @error="onError">
  14 + :enable-load-more="true" row-key="instanceId" @loaded="onLoaded" @error="onError">
15 15 <template v-slot="{ item }">
16 16 <view class="card">
17 17 <view class="card-header">
... ... @@ -55,299 +55,306 @@
55 55 </template>
56 56
57 57 <script>
58   - import CardList from '@/components/card/index.vue'
59   - import FilterModal from '@/components/filter/index.vue'
60   - import SingleSelectSheet from '@/components/single-select/index.vue'
61   - import {
62   - myFlowQueryApi
63   - } from '@/api/flow.js'
64   - import {
65   - getDicByCodes
66   - } from '@/utils/dic'
67   - import {
68   - getDicName
69   - } from '@/utils/dic.js'
  58 +import CardList from '@/components/card/index.vue'
  59 +import FilterModal from '@/components/filter/index.vue'
  60 +import SingleSelectSheet from '@/components/single-select/index.vue'
  61 +import {
  62 + myFlowQueryApi
  63 +} from '@/api/flow.js'
  64 +import {
  65 + getDicByCodes
  66 +} from '@/utils/dic'
  67 +import {
  68 + getDicName
  69 +} from '@/utils/dic.js'
70 70
71   - export default {
72   - name: 'MyFlow',
73   - components: {
74   - CardList,
75   - FilterModal,
76   - SingleSelectSheet
  71 +export default {
  72 + name: 'MyFlow',
  73 + components: {
  74 + CardList,
  75 + FilterModal,
  76 + SingleSelectSheet
  77 + },
  78 + data() {
  79 + return {
  80 + searchKeyword: '',
  81 + searchKeywordDebounced: '',
  82 + searchDebounceTimer: null,
  83 + filterVisible: false,
  84 + filterForm: {
  85 + mode: '',
  86 + modeName: ''
  87 + },
  88 + modeSelectVisible: false,
  89 + modeOptions: [],
  90 + cardModeOptions: [],
  91 + query: {},
  92 + extraParams: {},
  93 + currentItems: []
  94 + }
  95 + },
  96 + created() {
  97 + this.loadModeOptions()
  98 + },
  99 + onReachBottom() {
  100 + if (this.$refs && this.$refs.cardRef && this.$refs.cardRef.onLoadMore) {
  101 + this.$refs.cardRef.onLoadMore()
  102 + }
  103 + },
  104 + watch: {},
  105 + methods: {
  106 + onSearchInput(val) {
  107 + if (this.searchDebounceTimer) clearTimeout(this.searchDebounceTimer)
  108 + this.searchDebounceTimer = setTimeout(() => {
  109 + this.searchKeywordDebounced = this.searchKeyword
  110 + this.searchDebounceTimer = null
  111 + this.extraParams = {
  112 + searchKey: this.searchKeywordDebounced || '',
  113 + mode: this.filterForm.mode || ''
  114 + }
  115 + }, 800)
77 116 },
78   - data() {
79   - return {
80   - searchKeyword: '',
81   - searchKeywordDebounced: '',
82   - searchDebounceTimer: null,
83   - filterVisible: false,
84   - filterForm: {
85   - mode: '',
86   - modeName: ''
87   - },
88   - modeSelectVisible: false,
89   - modeOptions: [],
90   - cardModeOptions: [],
91   - query: {},
92   - extraParams: {},
93   - currentItems: []
  117 + onSearchConfirm(e) {
  118 + const val = e && e.value != null ? e.value : this.searchKeyword
  119 + this.searchKeyword = val
  120 + this.searchKeywordDebounced = val
  121 + this.extraParams = {
  122 + searchKey: this.searchKeywordDebounced || '',
  123 + mode: this.filterForm.mode || ''
94 124 }
95 125 },
96   - created() {
97   - this.loadModeOptions()
  126 + openFilter() {
  127 + this.filterVisible = true
  128 + },
  129 + onFilterReset(payload) {
  130 + this.filterForm = payload
98 131 },
99   - onReachBottom() {
100   - if (this.$refs && this.$refs.cardRef && this.$refs.cardRef.onLoadMore) {
101   - this.$refs.cardRef.onLoadMore()
  132 + onFilterConfirm(payload) {
  133 + // 兜底:部分端可能未同步 v-model
  134 + if ((payload.mode === '' || payload.mode == null) && this.filterForm.mode !== '') {
  135 + payload.mode = this.filterForm.mode
  136 + payload.modeName = this.filterForm.modeName
102 137 }
  138 + this.filterForm = payload
  139 + // 仅在确认时更新给 CardList 的条件
  140 + this.extraParams = {
  141 + searchKey: this.searchKeywordDebounced || '',
  142 + mode: payload.mode || ''
  143 + }
  144 + this.filterVisible = false
103 145 },
104   - watch: {},
105   - methods: {
106   - onSearchInput(val) {
107   - if (this.searchDebounceTimer) clearTimeout(this.searchDebounceTimer)
108   - this.searchDebounceTimer = setTimeout(() => {
109   - this.searchKeywordDebounced = this.searchKeyword
110   - this.searchDebounceTimer = null
111   - this.extraParams = {
112   - searchKey: this.searchKeywordDebounced || '',
113   - mode: this.filterForm.mode || ''
114   - }
115   - }, 800)
116   - },
117   - onSearchConfirm(e) {
118   - const val = e && e.value != null ? e.value : this.searchKeyword
119   - this.searchKeyword = val
120   - this.searchKeywordDebounced = val
121   - this.extraParams = {
122   - searchKey: this.searchKeywordDebounced || '',
123   - mode: this.filterForm.mode || ''
124   - }
125   - },
126   - openFilter() {
127   - this.filterVisible = true
128   - },
129   - onFilterReset(payload) {
130   - this.filterForm = payload
131   - },
132   - onFilterConfirm(payload) {
133   - // 兜底:部分端可能未同步 v-model
134   - if ((payload.mode === '' || payload.mode == null) && this.filterForm.mode !== '') {
135   - payload.mode = this.filterForm.mode
136   - payload.modeName = this.filterForm.modeName
137   - }
138   - this.filterForm = payload
139   - // 仅在确认时更新给 CardList 的条件
140   - this.extraParams = {
141   - searchKey: this.searchKeywordDebounced || '',
142   - mode: payload.mode || ''
143   - }
144   - this.filterVisible = false
145   - },
146   - openModeSelect() {
147   - this.modeSelectVisible = true
148   - },
149   - onModeConfirm({
150   - value,
151   - label
152   - }) {
153   - this.filterForm.mode = value || '';
154   - this.filterForm.modeName = label || '';
155   - },
156   - onLoaded({
157   - items
158   - }) {
159   - this.currentItems = items || []
160   - },
161   - onError() {
162   - uni.showToast({
163   - title: '列表加载失败',
164   - icon: 'none'
165   - })
166   - },
167   - async loadModeOptions() {
168   - try {
169   - const dicCodes = ['PROCESS_MODE']
170   - const results = await getDicByCodes(dicCodes)
171   - const option = results.PROCESS_MODE.data || []
172   - this.cardModeOptions = Array.isArray(option) ? option : [];
173   - this.modeOptions = option.map(it => ({
174   - label: it.name || '',
175   - value: it.code || ''
176   - }))
177   - } catch (e) {
178   - this.modeOptions = [];
179   - this.cardModeOptions = [];
180   - }
181   - },
182   - fetchList({
  146 + openModeSelect() {
  147 + this.modeSelectVisible = true
  148 + },
  149 + onModeConfirm({
  150 + value,
  151 + label
  152 + }) {
  153 + this.filterForm.mode = value || '';
  154 + this.filterForm.modeName = label || '';
  155 + },
  156 + onLoaded({
  157 + items
  158 + }) {
  159 + this.currentItems = items || []
  160 + },
  161 + onError() {
  162 + uni.showToast({
  163 + title: '列表加载失败',
  164 + icon: 'none'
  165 + })
  166 + },
  167 + async loadModeOptions() {
  168 + try {
  169 + const dicCodes = ['PROCESS_MODE']
  170 + const results = await getDicByCodes(dicCodes)
  171 + const option = results.PROCESS_MODE.data || []
  172 + this.cardModeOptions = Array.isArray(option) ? option : [];
  173 + this.modeOptions = option.map(it => ({
  174 + label: it.name || '',
  175 + value: it.code || ''
  176 + }))
  177 + } catch (e) {
  178 + this.modeOptions = [];
  179 + this.cardModeOptions = [];
  180 + }
  181 + },
  182 + fetchList({
  183 + pageIndex,
  184 + pageSize,
  185 + query,
  186 + extra
  187 + }) {
  188 + const params = {
183 189 pageIndex,
184 190 pageSize,
185   - query,
186   - extra
187   - }) {
188   - const params = {
189   - pageIndex,
190   - pageSize,
191   - ...query,
192   - ...extra
  191 + ...query,
  192 + ...extra
  193 + }
  194 + return myFlowQueryApi(params).then(res => {
  195 + const _data = res.data || {}
  196 + const records = _data.datas || _data.list || _data.records || []
  197 + const totalCount = _data.totalCount || _data.count || 0
  198 + const hasNext = _data.hasNext || false
  199 + return {
  200 + records,
  201 + totalCount,
  202 + hasNext
193 203 }
194   - return myFlowQueryApi(params).then(res => {
195   - const _data = res.data || {}
196   - const records = _data.datas || _data.list || _data.records || []
197   - const totalCount = _data.totalCount || _data.count || 0
198   - const hasNext = _data.hasNext || false
199   - return {
200   - records,
201   - totalCount,
202   - hasNext
203   - }
204   - }).catch(() => {
205   - this.onError()
206   - return {
207   - records: [],
208   - totalCount: 0,
209   - hasNext: false
210   - }
211   - })
212   - },
213   - onDetail(item) {
214   - uni.showToast({
215   - title: '审核详情',
216   - icon: 'none'
217   - })
218   - },
219   - getDicName: getDicName,
220   - }
  204 + }).catch(() => {
  205 + this.onError()
  206 + return {
  207 + records: [],
  208 + totalCount: 0,
  209 + hasNext: false
  210 + }
  211 + })
  212 + },
  213 + onDetail(item) {
  214 + const CACHE_KEY = 'FLOW_AUDIT_CACHE'
  215 + const _ext = JSON.parse(item.ext || '{}');
  216 + const payload = {
  217 + taskId: item.taskId || '',
  218 + instanceId: item.instanceId || '',
  219 + businessId: item.businessId || '',
  220 + bizFlag: _ext.bizFlag || ''
  221 + }
  222 + uni.setStorageSync(CACHE_KEY, payload)
  223 + uni.navigateTo({ url: '/pages/flow/audit_detail' })
  224 + },
  225 + getDicName: getDicName,
221 226 }
  227 +}
222 228 </script>
223 229
224 230 <style lang="scss" scoped>
225   - .page {
226   - display: flex;
227   - flex-direction: column;
228   - height: 100vh;
229   - }
  231 +.page {
  232 + display: flex;
  233 + flex-direction: column;
  234 + height: 100vh;
  235 +}
230 236
231   - .fixed {
232   - position: fixed;
233   - top: 96rpx;
234   - left: 0;
235   - right: 0;
236   - z-index: 2;
237   - background: #fff;
  237 +.fixed {
  238 + position: fixed;
  239 + top: 96rpx;
  240 + left: 0;
  241 + right: 0;
  242 + z-index: 2;
  243 + background: #fff;
  244 +}
  245 +
  246 +.search-row {
  247 + display: flex;
  248 + align-items: center;
  249 + padding: 16rpx 32rpx;
  250 +
  251 + .tool-icon {
  252 + width: 48rpx;
  253 + height: 48rpx;
  254 + margin-left: 16rpx;
238 255 }
239 256
240   - .search-row {
241   - display: flex;
242   - align-items: center;
243   - padding: 16rpx 32rpx;
  257 + .uni-searchbar {
  258 + padding: 0;
  259 + flex: 1;
  260 + }
  261 +}
244 262
245   - .tool-icon {
246   - width: 48rpx;
247   - height: 48rpx;
248   - margin-left: 16rpx;
249   - }
  263 +/* 仅当前页覆盖 uni-search-bar 盒子高度 */
  264 +::v-deep .uni-searchbar__box {
  265 + height: 80rpx !important;
  266 + justify-content: start;
250 267
251   - .uni-searchbar {
252   - padding: 0;
253   - flex: 1;
254   - }
  268 + .uni-searchbar__box-search-input {
  269 + font-size: 32rpx !important;
255 270 }
  271 +}
  272 +
  273 +.list-box {
  274 + flex: 1;
  275 + padding: 112rpx 0 0
  276 +}
256 277
257   - /* 仅当前页覆盖 uni-search-bar 盒子高度 */
258   - ::v-deep .uni-searchbar__box {
259   - height: 80rpx !important;
260   - justify-content: start;
  278 +.card {
  279 + .footer {
  280 + margin-top: 28rpx;
  281 + box-shadow: inset 0px 1px 0px 0px #E7E7E7;
  282 + padding-top: 22rpx;
  283 + display: flex;
  284 + justify-content: flex-end;
261 285
262   - .uni-searchbar__box-search-input {
263   - font-size: 32rpx !important;
  286 + .btn {
  287 + height: 64rpx;
  288 + line-height: 60rpx;
  289 + font-size: 28rpx;
  290 + width: 160rpx;
  291 + margin-left: auto;
  292 + margin-right: 0;
  293 + padding: 0;
264 294 }
265 295 }
266 296
267   - .list-box {
268   - flex: 1;
269   - padding: 112rpx 0 0
270   - }
  297 + .card-header {
  298 + margin-bottom: 28rpx;
271 299
272   - .card {
273   - .footer {
274   - margin-top: 28rpx;
275   - box-shadow: inset 0px 1px 0px 0px #E7E7E7;
276   - padding-top: 22rpx;
277   - display: flex;
278   - justify-content: flex-end;
279   - .btn {
280   - height: 64rpx;
281   - line-height: 60rpx;
282   - font-size: 28rpx;
283   - width: 160rpx;
284   - margin-left: auto;
285   - margin-right: 0;
286   - padding: 0;
287   - }
  300 + .title {
  301 + font-size: 36rpx;
  302 + font-weight: 600;
  303 + line-height: 50rpx;
  304 + color: rgba(0, 0, 0, 0.9);
288 305 }
  306 + }
289 307
290   - .card-header {
291   - margin-bottom: 28rpx;
  308 + .info-row {
  309 + display: flex;
  310 + align-items: center;
  311 + color: rgba(0, 0, 0, 0.6);
  312 + font-size: 28rpx;
  313 + margin-bottom: 24rpx;
292 314
293   - .title {
294   - font-size: 36rpx;
295   - font-weight: 600;
296   - line-height: 50rpx;
297   - color: rgba(0,0,0,0.9);
298   - }
  315 + &:last-child {
  316 + margin-bottom: 0;
299 317 }
300 318
301   - .info-row {
302   - display: flex;
303   - align-items: center;
304   - color: rgba(0, 0, 0, 0.6);
305   - font-size: 28rpx;
306   - margin-bottom: 24rpx;
  319 + text {
  320 + width: 60%;
  321 + line-height: 32rpx;
307 322
308 323 &:last-child {
309   - margin-bottom: 0;
310   - }
311   -
312   - text {
313   - width: 60%;
314   - line-height: 32rpx;
315   -
316   - &:last-child {
317   - color: rgba(0, 0, 0, 0.9);
318   - width: 40%;
319   - }
  324 + color: rgba(0, 0, 0, 0.9);
  325 + width: 40%;
320 326 }
321 327 }
322 328 }
  329 +}
323 330
324   - .filter-form {
325   - .form-item {
326   - margin-bottom: 24rpx;
327   - }
  331 +.filter-form {
  332 + .form-item {
  333 + margin-bottom: 24rpx;
  334 + }
328 335
329   - .label {
330   - margin-bottom: 20rpx;
331   - color: rgba(0, 0, 0, 0.9);
332   - height: 44rpx;
333   - line-height: 44rpx;
334   - font-size: 30rpx;
335   - }
  336 + .label {
  337 + margin-bottom: 20rpx;
  338 + color: rgba(0, 0, 0, 0.9);
  339 + height: 44rpx;
  340 + line-height: 44rpx;
  341 + font-size: 30rpx;
  342 + }
336 343
337   - .fake-select {
338   - height: 80rpx;
339   - line-height: 80rpx;
340   - padding: 0 20rpx;
341   - background: #f3f3f3;
342   - border-radius: 12rpx;
  344 + .fake-select {
  345 + height: 80rpx;
  346 + line-height: 80rpx;
  347 + padding: 0 20rpx;
  348 + background: #f3f3f3;
  349 + border-radius: 12rpx;
343 350
344   - .placeholder {
345   - color: #999;
346   - }
  351 + .placeholder {
  352 + color: #999;
  353 + }
347 354
348   - .value {
349   - color: #333;
350   - }
  355 + .value {
  356 + color: #333;
351 357 }
352 358 }
  359 +}
353 360 </style>
\ No newline at end of file
... ...
  1 +import CustomerDevelopViewer from '@/pages/dev_manage/viewer.vue'
  2 +
  3 +export default function registerComponents(Vue) {
  4 + Vue.component('CustomerDevelopViewer', CustomerDevelopViewer)
  5 +}
\ No newline at end of file
... ...
1 1 import tab from './tab'
2 2 import auth from './auth'
3 3 import modal from './modal'
  4 +import registerComponents from './components'
4 5
5 6 export default {
6 7 install(Vue) {
... ... @@ -10,5 +11,6 @@ export default {
10 11 Vue.prototype.$auth = auth
11 12 // 模态框对象
12 13 Vue.prototype.$modal = modal
  14 + registerComponents(Vue)
13 15 }
14 16 }
... ...
... ... @@ -34,7 +34,6 @@ export function getDicByCodes(codes) {
34 34 * @returns {string}
35 35 */
36 36 export function getDicName(code, value, items = []) {
37   - console.log('getDicName', code, value, items);
38 37 const list = Array.isArray(items) ? items : [];
39 38 const item = list.find(it => it && it.code === value);
40 39 return item ? item.name : value;
... ...
  1 +export const getSysFlowComponentPath = (bizFlag) => {
  2 + console.log('bizFlag', bizFlag)
  3 + let componentPath = '';
  4 + switch (bizFlag) {
  5 + case 'CUSTOMER_DEVELOP': // 客户开发
  6 + componentPath = 'CustomerDevelopViewer'; // 客户开发-审批详情
  7 + break;
  8 + case 'CUSTOMER_DEVELOP_EDIT': // 客户开发
  9 + componentPath = 'CustomerDevelopViewer'; // 客户开发-审批
  10 + break;
  11 + case 'CUSTOMER_CREDIT': // 客户资信
  12 + componentPath = 'CustomerCreditViewer'; // 客户资信-审批详情
  13 + break;
  14 + case 'CUSTOMER_CREDIT_EDIT': // 客户资信
  15 + componentPath = 'CustomerCreditApprove'; // 客户资信-审批
  16 + break;
  17 + case 'STANDARD_CONTRACT': // 标准合同
  18 + componentPath = 'StandardContractViewer'; // 标准合同-审批详情
  19 + break;
  20 + case 'STANDARD_CONTRACT_EDIT': // 标准合同
  21 + componentPath = 'StandardContractApprove'; // 标准合同-审批
  22 + break;
  23 + case 'FORMAL_CONTRACT': // 正式合同
  24 + componentPath = 'FormalContractViewer'; // 正式合同-审批详情
  25 + break;
  26 + case 'FORMAL_CONTRACT_EDIT': // 正式合同
  27 + componentPath = 'FormalContractApprove'; // 正式合同-审批
  28 + break;
  29 + case 'PROCESS_STD_AGMT': // 处理标准合同
  30 + componentPath = 'ProcessStdAgmtViewer'; // 处理标准合同-审批
  31 + break;
  32 + case 'PROCESS_STD_AGMT_EDIT': // 处理标准合同
  33 + componentPath = 'ProcessStdAgmtApprove'; // 处理标准合同-审批
  34 + break;
  35 + case 'SPEC_LOCK_DELAY': // 特殊锁单延迟
  36 + componentPath = 'SpecLockDelayViewer'; // 特殊锁单延迟-审批详情
  37 + break;
  38 + case 'SPEC_LOCK_DELAY_EDIT': // 特殊锁单延迟
  39 + componentPath = 'SpecLockDelayApprove'; // 特殊锁单延迟-审批
  40 + break;
  41 + }
  42 + return componentPath;
  43 +};
\ No newline at end of file
... ...
... ... @@ -45,9 +45,6 @@ config.responseType = 'blob';
45 45 dataType: 'json'
46 46 }).then(response => {
47 47 let [error, res] = response;
48   - console.log('request__response', response)
49   - console.log('request__error', error)
50   - console.log('request__res', res)
51 48 if (error) {
52 49 toast('后端接口连接异常')
53 50 reject('后端接口连接异常')
... ...