撤销吊销

This commit is contained in:
chenxf 2026-04-07 16:46:08 +08:00
parent 842aab55c6
commit dc554e540f
5 changed files with 1993 additions and 41 deletions

File diff suppressed because it is too large Load Diff

View File

@ -2180,6 +2180,36 @@ export const constantRoutes = [{
},
children: [{
path: '/revoke/cancelRevoke/todo',
name: 'cancelRevokeTodo',
component: () =>
import('@/views/revoke/cancelRevoke/list.vue'),
meta: {
title: '撤销吊销',
type: 'todo'
}
},
{
path: '/revoke/cancelRevoke/history',
name: 'cancelRevokeHistory',
component: () =>
import('@/views/revoke/cancelRevoke/list.vue'),
meta: {
title: '撤销吊销',
type: 'history'
},
hidden: true
},
{
path: '/revoke/cancelRevoke/handle',
name: 'cancelRevokeHandle',
component: () =>
import('@/views/revoke/cancelRevoke/handle.vue'),
meta: {
title: '撤销吊销办理'
},
hidden: true
}, {
path: '/revoke/revokeList',
name: 'revokeList',
component: () =>

View File

@ -0,0 +1,243 @@
<template>
<el-dialog
title="个案办理"
:visible.sync="visible"
width="90vw"
top="153px"
custom-class="dialog-abnormal-list"
>
<edit-table
v-if="visible"
v-model="tableData"
:table-prop="tableProp"
:fields="fields"
:show-search-form="true"
row-key="revokeListId"
:search-form-prop="searchFormProp"
:search-loader="searchLoader"
:selected.sync="selected"
/>
<div class="cancel-reason-row">
<span class="cancel-reason-label">撤销吊销原因</span>
<el-input
v-model="cancelRevokeReason"
type="textarea"
:rows="3"
size="mini"
placeholder="请输入撤销吊销原因"
maxlength="200"
show-word-limit
/>
</div>
<div slot="footer">
<el-button type="primary" plain @click="visible = false">取消</el-button>
<el-button type="primary" @click="confirm">确认</el-button>
</div>
</el-dialog>
</template>
<script>
import { apiCancelRevoke, apiCancelRevokeRevokedList } from '@/api/auto-baseapi'
export default {
data() {
return {
visible: false,
tableData: [],
selected: [],
searchFormProp: {
formConfig: {
labelWidth: '100px',
labelPosition: 'right',
gutter: 32
}
},
cancelRevokeReason: '',
tableProp: {
emitLoadOnCreate: true,
tableConfig: {
rowKey: 'revokeListId',
selection: true,
tableSelectionProps: {
reserveSelection: true,
selectable: (row) => {
return this.selected.length === 0 || this.selected[0] === row
}
},
disableSelectAll: false,
showIndex: true,
indexWidth: '100px',
showPagination: true,
tableProps: {
headerCellStyle: { background: '#F5F5F5!important', color: '#333333!important', padding: '0px' },
headerRowStyle: { height: '48px' },
rowStyle: { height: '48px' },
cellStyle: { padding: '16px 0', color: '#333' }
}
}
}
}
},
computed: {
fields() {
return [
{
type: 'input',
label: '统一社会信用代码/注册号',
prop: 'searchUscc',
span: 8,
tableProps: {
showOverflowTooltip: true,
formatter: (row) => {
return `${row.uniscid || row.regno || ''}`
}
}
},
{
type: 'input',
label: ({ meta }) => {
return meta.isSearchForm ? '主体名称(精确)' : '主体名称'
},
prop: 'entname',
span: 8,
tableProps: {
showOverflowTooltip: true
}
},
{
type: 'buttons',
showInTable: false,
hiddenLabel: true,
label: '搜索操作',
prop: '搜索操作',
span: 8,
formItemProp: {
labelWidth: '0px'
},
buttons: [
{
label: '查询',
type: 'primary',
handler: (scope) => {
const { meta } = scope
const { _editTable } = meta
const { editTableInstance } = _editTable
editTableInstance.search()
}
},
{
label: '重置',
type: 'primary',
buttonProps: {
plain: true
},
handler: (scope) => {
const { meta } = scope
const { _editTable } = meta
const { editTableInstance } = _editTable
editTableInstance.search({ reset: true })
}
}
]
},
{
type: 'input',
label: '吊销原因',
prop: 'revokeReason',
span: 8,
showInForm: false,
tableProps: {
showOverflowTooltip: true
}
},
{
type: 'date-picker',
label: '吊销日期',
prop: 'revokeDate',
showInForm: false
}
]
}
},
methods: {
open() {
this.selected = []
this.cancelRevokeReason = ''
this.visible = true
},
searchLoader(pageParam, parseSearchFormData) {
return apiCancelRevokeRevokedList({
pageNum: pageParam.current,
pageSize: pageParam.size,
...parseSearchFormData
}).then((res) => {
if (res.code !== 0) {
this.$message.error(res.msg || '查询失败')
return {
tableData: [],
total: 0
}
}
const data = res.data || {}
return {
tableData: data.list || [],
total: data.total || 0
}
})
},
confirm() {
if (this.selected.length !== 1) {
this.$message.warning('只能选择一家吊销主体发起业务')
return
}
if (!this.cancelRevokeReason || !this.cancelRevokeReason.trim()) {
this.$message.warning('请先填写撤销吊销原因')
return
}
const row = this.selected[0]
const payload = {
approvalDept: '',
approvalDeptId: '',
approvalDocNo: '',
cancelRevokeReason: this.cancelRevokeReason.trim(),
entname: row.entname,
pripid: row.pripid,
regno: row.regno,
remarks: '',
revokeListId: row.revokeListId,
revokeReason: row.revokeReason,
uniscid: row.uniscid
}
apiCancelRevoke(payload).then((res) => {
if (res.code !== 0) {
this.$message.error(res.msg || '撤销吊销业务发起失败')
return
}
this.$message.success('撤销吊销业务发起成功')
this.$emit('started', res.data || {})
this.visible = false
})
}
}
}
</script>
<style lang="scss" scoped>
.cancel-reason-row {
display: flex;
align-items: center;
margin-top: 8px;
margin-bottom: 12px;
.cancel-reason-label {
width: 110px;
text-align: right;
margin-right: 10px;
color: #606266;
font-size: 14px;
}
.el-input {
flex: 1;
}
}
</style>

View File

@ -0,0 +1,492 @@
<template>
<div class="page cancel-revoke-handle-page">
<easy-form
ref="EasyForm"
v-model="data"
:fields="fields"
:form-config="formConfig"
:buttons="buttons"
>
<template v-slot:divider="scope">
<div class="divider">
<span class="text">{{ scope.getPropValue(scope.field.label) }}</span>
</div>
</template>
<template v-slot:opinion="scope">
<div class="opinion-selector">
<span class="opinion-selector-text">常用语</span>
<el-select
v-model="scope.field.opinion"
size="medium"
placeholder="请选择常用语"
@change="(val) => {
scope.formData[scope.field.prop] = val
scope.easyFormItemBindData.componentScope.formInstance.clearValidate(scope.field.prop)
}"
>
<el-option
v-for="item in scope.field.options"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</div>
</template>
<template v-slot:upperDept="scope">
<div class="opinion-selector">
<el-radio-group
v-model="localSelection"
@change="(val) => {
getUsersWithPermission(val === '上级部门处理人' ? 1 : 0)
scope.easyFormItemBindData.componentScope.formInstance.clearValidate(scope.field.prop)
}"
>
<el-radio label="当前部门处理人">当前部门处理人</el-radio>
<el-radio label="上级部门处理人">上级部门处理人</el-radio>
</el-radio-group>
</div>
</template>
<template v-slot:steps>
<div class="steps">
<el-steps v-if="approveList.length > 0" :active="0" finish-status="success">
<el-step v-for="(step, idx) in approveList" :key="step.approveId || idx">
<template v-slot:description>
<div class="step-description">
<div>
<span class="step-description-label">节点</span>
<span class="step-description-value">{{ toNodeCn(step.node) }}</span>
</div>
<div>
<span class="step-description-label">办理人</span>
<span class="step-description-value">{{ step.approverName || '-' }}</span>
</div>
<div>
<span class="step-description-label">意见</span>
<span class="step-description-value">{{ step.approveOpinion || '-' }}</span>
</div>
<div>
<span class="step-description-label">日期</span>
<span class="step-description-value">{{ step.approveTime || '-' }}</span>
</div>
</div>
</template>
</el-step>
</el-steps>
<div v-else class="empty-tips">暂无历史信息</div>
</div>
</template>
</easy-form>
</div>
</template>
<script>
import {
apiCancelRevokeBizId,
apiCancelRevokeDetailBizSeq,
apiCancelRevokeNext
} from '@/api/auto-baseapi'
import { getUsersWithPermission } from '@/api/user'
export default {
data() {
return {
approveList: [],
flowModel: [],
nextPerformerMap: [],
localSelection: '当前部门处理人',
data: {
approveOpinion: '',
approveStatus: '1',
bizId: '',
bizSeq: '',
cancelRevokeDate: '',
currentNode: '',
currentNodeName: '',
entname: '',
isAgree: '',
nextNodeID: '',
nextOrgID: '',
nextPerformerIds: '',
nextPerformerNames: '',
nextPerformerids: '',
opinionContent: '',
regno: '',
revokeListId: '',
revokeReason: '',
tasklistid: '',
transferReason: '',
uniscid: '',
status: '',
steps: []
},
formConfig: {
labelPosition: 'right',
buttonPosition: 'right',
gutter: 0,
isView: () => {
return this.readonly
}
},
buttons: [
{
label: '返回',
type: 'primary',
show: true,
buttonProps: { plain: true },
handler: () => {
this.$router.back()
}
},
{
label: '提交',
type: 'primary',
show: () => !this.readonly,
loading: false,
handler: ({ formInstance, getOptionValue }) => {
return formInstance.submitForm().then((formData) => {
if (!this.data.bizId || !this.data.bizSeq) {
this.$message.error('业务参数缺失,无法提交')
return
}
this.$confirm('是否继续提交', '提示', {
type: 'info',
customClass: 'type-1',
confirmHandlerPromise: () => {
const nextPerformer = getOptionValue('nextPerformerIds')
const payload = {
approveOpinion: formData.opinionContent,
approveStatus: this.resolveApproveStatus(formData),
bizId: this.data.bizId,
bizSeq: this.data.bizSeq,
cancelRevokeDate: this.data.cancelRevokeDate,
currentNode: this.data.currentNode,
nextNodeID: formData.isAgree,
nextPerformerNames: nextPerformer ? nextPerformer.label : '',
nextPerformerids: nextPerformer ? nextPerformer.value : '',
revokeListId: this.data.revokeListId,
tasklistid: this.data.tasklistid
}
if (formData.isAgree === 'transfer') {
payload.nextOrgID = formData.nextOrgID
payload.transferReason = formData.transferReason
}
return apiCancelRevokeNext(payload).then((res) => {
if (res.code !== 0) {
this.$message.error(res.msg || '提交失败')
return
}
this.$message.success('提交成功')
this.$router.push('/revoke/cancelRevoke/todo')
}).finally(() => {
})
}
})
})
}
}
]
}
},
computed: {
readonly() {
return this.$route.query.type === 'history'
},
fields() {
return [
{ type: 'divider', label: '撤销吊销业务信息' },
{ type: 'input', label: '统一社会信用代码', prop: 'uniscid', span: 12, isView: true },
{ type: 'input', label: '注册号', prop: 'regno', span: 12, isView: true },
{ type: 'input', label: '主体名称', prop: 'entname', span: 24, isView: true },
{ type: 'input', label: '吊销原因', prop: 'revokeReason', span: 24, isView: true },
{ type: 'input', label: '撤销吊销原因', prop: 'cancelRevokeReason', span: 24, isView: true },
{ type: 'divider', label: '历史处理信息' },
{
type: 'slot',
slotName: 'steps',
label: '历史处理信息',
hiddenLabel: true,
prop: 'steps',
span: 24,
defaultValue: () => []
},
{
type: 'divider',
label: ({ formData }) => {
return ({
'0': '经办意见',
'1': '审核意见',
'2': '审批意见',
'7': '执法负责意见'
})[formData.status] || '审批意见'
},
showInForm: () => !this.readonly
},
{
type: 'radio',
label: '选择操作',
prop: 'isAgree',
required: true,
span: 24,
options: () => this.flowModel,
showInForm: () => !this.readonly,
onChange: (scope, value, isFromUser) => {
if (!isFromUser) return
scope.formData.opinionContent = ''
scope.formData.nextPerformerIds = ''
scope.formData.nextPerformerids = ''
scope.formData.nextPerformerNames = ''
this.loadNextPerformers(value)
}
},
{
type: 'select',
beforeSlotName: 'upperDept',
label: '下一步处理人',
prop: 'nextPerformerIds',
required: true,
span: 12,
options: () => this.nextPerformerMap,
showInForm: ({ formData }) => !this.readonly && this.$util.isNotEmpty(formData.isAgree) && formData.isAgree !== 'transfer'
},
{
type: 'textarea',
afterSlotName: 'opinion',
label: '审批意见',
prop: 'opinionContent',
required: true,
span: 24,
opinion: '',
options: [
'同意',
'不同意'
],
showInForm: () => !this.readonly
},
{
type: 'input',
label: '转办机关',
prop: 'nextOrgID',
required: true,
span: 24,
showInForm: ({ formData }) => formData.isAgree === 'transfer' && !this.readonly
},
{
type: 'input',
label: '转办原因',
prop: 'transferReason',
required: true,
span: 24,
showInForm: ({ formData }) => formData.isAgree === 'transfer' && !this.readonly
}
]
}
},
created() {
this.initPage()
},
methods: {
initPage() {
const bizId = this.$route.query.bizId
const bizSeq = this.$route.query.bizSeq
const tasklistid = this.$route.query.tasklistid
if (!bizId && !bizSeq) {
this.$message.error('缺少业务参数')
return
}
Promise.all([
this.loadDetail(bizId, bizSeq, tasklistid)
]).catch(() => {
this.$message.error('页面数据加载失败')
})
},
loadDetail(bizId, bizSeq, tasklistid) {
if (bizSeq) {
return apiCancelRevokeDetailBizSeq({ bizSeq }, { tasklistid }).then((res) => {
if (res.code !== 0) {
this.$message.error(res.msg || '业务详情查询失败')
return
}
this.fillDetail(res.data || {})
})
}
return apiCancelRevokeBizId({ bizId }, { tasklistid }).then((res) => {
if (res.code !== 0) {
this.$message.error(res.msg || '业务详情查询失败')
return
}
this.fillDetail(res.data || {})
})
},
fillDetail(detail) {
const flowModel = this.buildFlowModel(detail.nextSequenceFlowList || [])
const detailApproveList = (detail.approveList || []).map(item => ({
...item,
node: this.toNodeCn(item.node)
}))
this.flowModel = flowModel
this.approveList = detailApproveList
this.data = {
...this.data,
...detail,
bizId: detail.bizId || this.$route.query.bizId || '',
bizSeq: detail.bizSeq || this.$route.query.bizSeq || '',
tasklistid: detail.tasklistid || this.$route.query.tasklistid || '',
currentNode: detail.currentNode || '',
currentNodeName: detail.currentNodeName || detail.currentNode || '',
revokeListId: detail.revokeListId || '',
cancelRevokeDate: detail.cancelRevokeDate || '',
status: detail.status || detail.taskStatus || '',
steps: detailApproveList
}
if (!this.data.isAgree && this.flowModel.length > 0) {
this.data.isAgree = this.flowModel[0].value
}
if (this.$util.isNotEmpty(this.data.isAgree) && !this.readonly) {
this.loadNextPerformers(this.data.isAgree)
}
},
buildFlowModel(list) {
return (list || []).map((item) => {
const node = item.nextNode || item
const value = node.id || item.nextNodeID || item.nextNodeId || item.nodeId || item.id || item.value || ''
const label = node.name || item.nextNodeName || item.nextNodeDesc || item.nodeName || item.name || item.label || value
return { label, value }
}).filter(item => item.value)
},
buildUserOptions(users) {
const list = Array.isArray(users) ? users : ((users && users.data) || [])
return list.map((item) => {
const value = item.value || item.primaryKey || item.userId || item.id || item.key || ''
const label = item.label || item.name || item.userName || item.username || value
return { label, value }
}).filter(item => item.value)
},
loadNextPerformers(nextNodeID, isUpper) {
if (!nextNodeID) {
this.nextPerformerMap = []
return Promise.resolve()
}
return getUsersWithPermission(nextNodeID, this.$store.getters.orgId, isUpper).then((res) => {
this.nextPerformerMap = this.buildUserOptions(res)
}).catch(() => {
this.nextPerformerMap = []
})
},
getUsersWithPermission(isUpper) {
return this.loadNextPerformers(this.data.isAgree, isUpper)
},
toNodeCn(node) {
const key = (node || '').toString()
return ({
0: '待受理',
1: '经办',
2: '审核',
3: '审批',
4: '办结',
start: '发起',
handle: '经办',
examine: '审核',
approve: '审批',
law: '执法负责',
transfer: '转办',
end: '办结',
revoke: '撤销'
})[key] || node || '-'
},
resolveApproveStatus(formData) {
const text = ((this.flowModel.find(item => item.value === formData.isAgree) || {}).label || '').toLowerCase()
if (text.includes('不同意') || text.includes('拒绝') || text.includes('驳回') || text.includes('退回')) {
return '2'
}
return '1'
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/styles/abnormal-handle";
.cancel-revoke-handle-page {
.divider {
display: flex;
align-items: center;
margin-bottom: 8px;
.text {
font-size: 16px;
font-weight: 600;
color: #333;
padding-left: 10px;
border-left: 4px solid #2A8CE3;
line-height: 20px;
}
}
.opinion-selector {
margin-top: 18px;
display: flex;
.opinion-selector-text {
margin-right: 16px;
}
}
.steps {
padding: 20px 24px;
overflow: auto;
.el-steps {
position: relative;
width: max-content;
min-width: 100%;
&::before {
content: "";
position: absolute;
border: 2px solid #F5F5F5;
width: 100%;
top: 18px;
}
.el-step {
flex-basis: unset !important;
margin-right: 56px !important;
flex-shrink: 0;
/deep/ .el-step__head {
margin-bottom: 16px;
.el-step__line {
display: none;
}
.el-step__icon {
width: 36px;
height: 36px;
border-color: #2A8CE3;
color: #2A8CE3;
}
}
/deep/ .el-step__description {
padding-right: 0;
}
}
}
.step-description {
font-size: 14px;
> div {
margin-bottom: 8px;
}
.step-description-label {
color: #666666;
}
.step-description-value {
color: #333333;
}
}
.empty-tips {
color: #999;
font-size: 14px;
}
}
}
</style>

View File

@ -0,0 +1,211 @@
<template>
<div class="main-content m20">
<el-tabs v-model="type" type="card">
<el-tab-pane label="撤销吊销待办" name="todo" />
<el-tab-pane label="撤销吊销记录" name="history" />
</el-tabs>
<div class="content-header">
<div class="body search-body">
<el-row class="mb10">
<el-col :span="12">
<label class="label-name">统一社会信用代码/注册号</label>
<div class="search-input-box">
<el-input v-model="searchForm.uniscid" clearable size="mini" placeholder="请输入统一社会信用代码/注册号" @keyup.enter.native="loadPage" />
</div>
</el-col>
<el-col :span="12">
<label class="label-name">主体名称</label>
<div class="search-input-box">
<el-input v-model="searchForm.entname" clearable size="mini" placeholder="请输入主体名称" @keyup.enter.native="loadPage" />
</div>
</el-col>
</el-row>
<el-row>
<el-col :span="12" :offset="1">
<div class="btn-box">
<el-button v-if="type === 'todo'" type="primary" size="mini" @click="openSubjectDialog">个案办理</el-button>
<el-button type="primary" size="mini" @click="loadPage">查询</el-button>
<el-button size="mini" @click="reset">重置</el-button>
</div>
</el-col>
</el-row>
</div>
</div>
<div class="content-body">
<div class="body">
<el-table
v-loading="loading"
size="mini"
:data="tableData"
:fit="true"
:header-cell-style="{background:'#8cc3fb',color:'#fff'}"
>
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column prop="bizSeq" label="业务编号" min-width="180" show-overflow-tooltip />
<el-table-column prop="uniscid" label="统一社会信用代码" min-width="180" show-overflow-tooltip />
<el-table-column prop="regno" label="注册号" min-width="140" show-overflow-tooltip />
<el-table-column prop="entname" label="主体名称" min-width="220" show-overflow-tooltip />
<el-table-column prop="revokeReason" label="吊销原因" min-width="220" show-overflow-tooltip />
<el-table-column prop="currentNodeName" label="当前节点" min-width="120" show-overflow-tooltip />
<el-table-column prop="createTime" label="创建时间" min-width="170" show-overflow-tooltip />
<el-table-column label="操作" width="100" fixed="right" align="center">
<template slot-scope="scope">
<el-button type="text" size="mini" @click="handleRow(scope.row)">{{ type === 'todo' ? '办理' : '查看' }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="page-box">
<el-pagination
:disabled="loading"
:current-page="pageParam.current"
:page-size="pageParam.size"
:total="pageParam.total"
:page-sizes="[10, 20, 30, 40, 50, 100]"
layout="slot,total, sizes, prev, pager, next, jumper"
@size-change="pageSizeChange"
@current-change="pageIndexChange"
>
<span>
{{ pageParam.current }} /
{{ pageParam.total !== 0 ? parseInt((pageParam.total + pageParam.size - 1) / pageParam.size) : 1 }}
</span>
</el-pagination>
</div>
</div>
</div>
<SubjectSelectDialog ref="subjectDialog" @started="onStarted" />
</div>
</template>
<script>
import SubjectSelectDialog from './components/SubjectSelectDialog.vue'
import { apiCancelRevokeRecords, apiCancelRevokeTodoList } from '@/api/auto-baseapi'
export default {
components: { SubjectSelectDialog },
data() {
return {
type: this.$route.meta.type,
loading: false,
searchForm: {
uniscid: '',
entname: ''
},
tableData: [],
pageParam: {
current: 1,
size: 10,
total: 0
}
}
},
watch: {
type(val) {
this.$router.replace({ path: `/revoke/cancelRevoke/${val}` })
},
'$route.meta.type'(val) {
this.type = val
this.pageParam.current = 1
this.loadPage()
}
},
mounted() {
this.loadPage()
},
methods: {
openSubjectDialog() {
this.$refs.subjectDialog.open()
},
onStarted(data) {
this.$router.push({
path: '/revoke/cancelRevoke/handle',
query: {
bizId: data.bizId,
bizSeq: data.bizSeq,
tasklistid: data.tasklistid,
workflowid: data.workflowid,
type: 'todo'
}
})
},
pageIndexChange(current) {
this.pageParam.current = current
this.loadPage()
},
pageSizeChange(size) {
this.pageParam.size = size
this.pageParam.current = 1
this.loadPage()
},
reset() {
this.searchForm.uniscid = ''
this.searchForm.entname = ''
this.pageParam.current = 1
this.loadPage()
},
loadPage() {
if (this.loading) return
this.loading = true
const params = {
pageNum: this.pageParam.current,
pageSize: this.pageParam.size,
uniscid: this.searchForm.uniscid,
entname: this.searchForm.entname
}
const request = this.type === 'todo' ? apiCancelRevokeTodoList : apiCancelRevokeRecords
request(params).then((res) => {
if (res.code !== 0) {
this.$message.error(res.msg || '查询失败')
this.tableData = []
this.pageParam.total = 0
return
}
const data = res.data || {}
this.tableData = data.list || []
this.pageParam.total = data.total || 0
}).finally(() => {
this.loading = false
})
},
handleRow(row) {
this.$router.push({
path: '/revoke/cancelRevoke/handle',
query: {
bizId: row.bizId,
bizSeq: row.bizSeq,
tasklistid: row.tasklistid,
workflowid: row.workflowid,
type: this.type
}
})
}
}
}
</script>
<style lang="scss" scoped>
.main-content {
background: #fff;
padding: 20px;
}
.label-name {
text-align: right;
display: inline-block;
width: 220px;
font-size: $table-content-font-size;
}
.search-input-box {
display: inline-block;
width: calc(100% - 220px);
}
.page-box {
margin-top: 12px;
}
.mb10 {
margin-bottom: 10px;
}
</style>