审批完成的合同,在合同签署界面进行在线签署。合同发起界面不能在线签署。
This commit is contained in:
@@ -228,7 +228,7 @@
|
||||
<a v-if="canMockReject(row)" @click="handleMockReject(row)">{{
|
||||
isSingleAgreementRecord(row) ? "审批驳回" : "测试驳回"
|
||||
}}</a>
|
||||
<a v-if="canOnlineSign(row)" @click="handleOnlineSign(row)">{{
|
||||
<a v-if="SHOW_SIGN_ACTIONS_IN_LAUNCH && canOnlineSign(row)" @click="handleOnlineSign(row)">{{
|
||||
isSingleAgreementRecord(row) ? "发起盖章" : "在线签订"
|
||||
}}</a>
|
||||
<a
|
||||
@@ -236,11 +236,11 @@
|
||||
@click="handleDownloadSigned(row)"
|
||||
>下载签署文件</a
|
||||
>
|
||||
<a v-if="canUploadSigned(row)" @click="handleUploadSigned(row)">{{
|
||||
<a v-if="SHOW_SIGN_ACTIONS_IN_LAUNCH && canUploadSigned(row)" @click="handleUploadSigned(row)">{{
|
||||
isSingleAgreementRecord(row) ? "上传盖章版" : "上传已签署版本"
|
||||
}}</a>
|
||||
<a
|
||||
v-if="canMockFinishSign(row)"
|
||||
v-if="SHOW_SIGN_ACTIONS_IN_LAUNCH && canMockFinishSign(row)"
|
||||
@click="handleMockFinishSign(row)"
|
||||
>{{
|
||||
isSingleAgreementRecord(row) ? "测试盖章完成" : "测试签署完成"
|
||||
@@ -346,6 +346,9 @@ const contractRecordStatusOptions = [
|
||||
{ label: "已终止", value: "terminated" },
|
||||
];
|
||||
|
||||
// 合同签署已拆到独立菜单,这里只隐藏入口,保留原有逻辑方便后续恢复。
|
||||
const SHOW_SIGN_ACTIONS_IN_LAUNCH = false;
|
||||
|
||||
const formRef = ref(null);
|
||||
const queryContainer = ref(null);
|
||||
const pageWrapperRef = ref(null);
|
||||
|
||||
@@ -0,0 +1,826 @@
|
||||
<template>
|
||||
<section class="section-container">
|
||||
<a-card
|
||||
:bordered="false"
|
||||
class="query-card"
|
||||
:body-style="queryCardBodyStyle"
|
||||
>
|
||||
<div ref="queryContainer" class="section-query-container">
|
||||
<a-form :model="queryParam" layout="inline">
|
||||
<a-row :gutter="16" style="width: 100%">
|
||||
<a-col :md="6" :sm="24">
|
||||
<a-form-item label="合同名称">
|
||||
<a-input
|
||||
v-model:value="queryParam.contractName"
|
||||
allow-clear
|
||||
placeholder="请输入合同名称"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="6" :sm="24">
|
||||
<a-form-item label="合同编码">
|
||||
<a-input
|
||||
v-model:value="queryParam.contractCode"
|
||||
allow-clear
|
||||
placeholder="请输入合同编码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="6" :sm="24">
|
||||
<a-form-item label="合同类型">
|
||||
<a-select
|
||||
v-model:value="queryParam.contractType"
|
||||
allow-clear
|
||||
placeholder="请选择合同类型"
|
||||
:options="contractTypeOptions"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="6" :sm="24">
|
||||
<a-form-item label="签署状态">
|
||||
<a-select
|
||||
v-model:value="queryParam.status"
|
||||
allow-clear
|
||||
placeholder="全部签署阶段"
|
||||
:options="signStatusOptions"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="12" :sm="24">
|
||||
<a-form-item label="发起日期">
|
||||
<a-range-picker
|
||||
v-model:value="queryParam.dateRange"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<a-card
|
||||
:bordered="false"
|
||||
class="table-card"
|
||||
:body-style="tableCardBodyStyle"
|
||||
>
|
||||
<vxe-toolbar ref="toolbarRef" custom>
|
||||
<template #buttons>
|
||||
<a-space wrap>
|
||||
<a-button type="primary" :icon="h(SearchOutlined)" @click="handleQuery"
|
||||
>查询</a-button
|
||||
>
|
||||
<a-button :icon="h(RedoOutlined)" @click="handleReset">重置</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</vxe-toolbar>
|
||||
|
||||
<vxe-table
|
||||
ref="tableRef"
|
||||
border
|
||||
:height="tableHeight"
|
||||
:row-config="{ isHover: true, height: 44 }"
|
||||
:column-config="{ resizable: true, minWidth: 120 }"
|
||||
:custom-config="{ storage: true }"
|
||||
:scroll-y="{ enabled: true, gt: 0, mode: 'wheel' }"
|
||||
:data="tableData"
|
||||
:loading="loading"
|
||||
:loading-config="{
|
||||
icon: 'vxe-icon-indicator roll',
|
||||
text: '正在拼命加载中...',
|
||||
}"
|
||||
>
|
||||
<vxe-column type="seq" title="序号" width="68" />
|
||||
<vxe-column
|
||||
field="contractCode"
|
||||
title="合同编码"
|
||||
width="160"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="lawContractCode"
|
||||
title="法务合同编号"
|
||||
width="160"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="contractName"
|
||||
title="合同名称"
|
||||
width="220"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="contractType"
|
||||
title="合同类型"
|
||||
width="160"
|
||||
show-overflow="title"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ getContractTypeSimpleLabel(row.contractTypeCode || row.contractType) }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="isElectron" title="签署方式" width="120">
|
||||
<template #default="{ row }">
|
||||
<a-tag :color="String(row.isElectron || '1') === '1' ? 'processing' : 'default'">
|
||||
{{ String(row.isElectron || '1') === '1' ? '电子合同' : '纸质合同' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="counterpartySummary"
|
||||
title="合同相对方"
|
||||
width="260"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column field="status" title="状态" width="120">
|
||||
<template #default="{ row }">
|
||||
<a-tag :color="getStatusColor(row.status)">{{ getStatusLabelByRow(row) }}</a-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="approveMessage"
|
||||
title="审批说明"
|
||||
width="220"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="initiator"
|
||||
title="发起人"
|
||||
width="100"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="initiatorDept"
|
||||
title="所属部门"
|
||||
width="140"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="createTime"
|
||||
title="发起时间"
|
||||
width="170"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column
|
||||
field="updateTime"
|
||||
title="最后更新时间"
|
||||
width="170"
|
||||
show-overflow="title"
|
||||
/>
|
||||
<vxe-column title="操作" width="660" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<div class="table-actions">
|
||||
<a @click="handleView(row)">查看</a>
|
||||
<a v-if="canViewApprove(row)" @click="handleViewApprove(row)">审批记录</a>
|
||||
<a v-if="canViewLawCallback(row)" @click="handleViewLawCallback(row)">回调data</a>
|
||||
<a v-if="canOnlineSign(row)" @click="handleOnlineSign(row)">
|
||||
{{ isSingleAgreementRecord(row) ? '发起盖章' : '在线签订' }}
|
||||
</a>
|
||||
<a v-if="canUploadSigned(row)" @click="handleUploadSigned(row)">
|
||||
{{ isSingleAgreementRecord(row) ? '上传盖章版' : '上传已签署版本' }}
|
||||
</a>
|
||||
<a v-if="canMockFinishSign(row)" @click="handleMockFinishSign(row)">
|
||||
{{ isSingleAgreementRecord(row) ? '测试盖章完成' : '测试签署完成' }}
|
||||
</a>
|
||||
<a v-if="canDownloadSigned(row)" @click="handleDownloadSigned(row)">下载签署文件</a>
|
||||
</div>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
|
||||
<a-pagination
|
||||
id="pageWrapper"
|
||||
ref="pageWrapperRef"
|
||||
v-model:pageSize="queryParam.pageSize"
|
||||
v-model:current="queryParam.pageNo"
|
||||
class="table-pagination"
|
||||
show-size-changer
|
||||
:page-size-options="['10', '20', '50', '100']"
|
||||
:total="total"
|
||||
:show-total="(value) => `共 ${value} 条`"
|
||||
@change="onShowSizeChange"
|
||||
@show-size-change="onShowSizeChange"
|
||||
/>
|
||||
</a-card>
|
||||
|
||||
<ContractPartyPlaceholderModal ref="contractModalRef" />
|
||||
<MixedSignModal
|
||||
ref="mixedSignModalRef"
|
||||
@business-updated="handleSignBusinessUpdated"
|
||||
/>
|
||||
<UploadSignedModal
|
||||
ref="uploadSignedModalRef"
|
||||
@done="handleSignBusinessUpdated"
|
||||
/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { h, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { RedoOutlined, SearchOutlined } from '@ant-design/icons-vue'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import ContractPartyPlaceholderModal from '../launch/components/ContractPartyPlaceholderModal.vue'
|
||||
import MixedSignModal from '../launch/components/mixedSignModal.vue'
|
||||
import UploadSignedModal from '../launch/components/UploadSignedModal.vue'
|
||||
import {
|
||||
contractTypeOptions,
|
||||
getContractTypeSimpleLabel,
|
||||
} from '../launch/contractLaunchOptions.js'
|
||||
import {
|
||||
downloadLaunchFile,
|
||||
finishLaunchSeal,
|
||||
getLaunchDetail,
|
||||
getLawApproveCallbackSample,
|
||||
queryApproveViewUrl,
|
||||
queryLaunchList,
|
||||
uploadWatermarkFile,
|
||||
} from '@/api/launch.js'
|
||||
|
||||
const SIGN_STAGE_STATUSES = ['pending_sign', 'signing']
|
||||
const SIGN_LIST_QUERY_PAGE_SIZE = 200
|
||||
const signStatusOptions = [
|
||||
{ label: '待签署', value: 'pending_sign' },
|
||||
{ label: '签署中', value: 'signing' },
|
||||
]
|
||||
|
||||
const contractModalRef = ref(null)
|
||||
const mixedSignModalRef = ref(null)
|
||||
const uploadSignedModalRef = ref(null)
|
||||
const queryContainer = ref(null)
|
||||
const pageWrapperRef = ref(null)
|
||||
const tableRef = ref(null)
|
||||
const toolbarRef = ref(null)
|
||||
|
||||
const loading = ref(false)
|
||||
const total = ref(0)
|
||||
const tableHeight = ref(420)
|
||||
const tableData = ref([])
|
||||
const allRecords = ref([])
|
||||
|
||||
const queryParam = ref({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
contractName: undefined,
|
||||
contractCode: undefined,
|
||||
contractType: undefined,
|
||||
status: undefined,
|
||||
dateRange: [],
|
||||
})
|
||||
|
||||
const queryCardBodyStyle = {
|
||||
padding: '20px 24px 6px',
|
||||
}
|
||||
|
||||
const tableCardBodyStyle = {
|
||||
display: 'flex',
|
||||
flexFlow: 'column nowrap',
|
||||
minHeight: '0',
|
||||
padding: '8px 12px 6px',
|
||||
}
|
||||
|
||||
const statusLabelMap = {
|
||||
pending_sign: '待签署',
|
||||
signing: '签署中',
|
||||
pending_collect: '待采集',
|
||||
pending_archive: '待归档',
|
||||
completed: '已完成',
|
||||
}
|
||||
|
||||
const statusColorMap = {
|
||||
pending_sign: 'warning',
|
||||
signing: 'processing',
|
||||
pending_collect: 'cyan',
|
||||
pending_archive: 'orange',
|
||||
completed: 'success',
|
||||
}
|
||||
|
||||
function isSuccessResponse(res) {
|
||||
return String(res?.code || '') === '0000'
|
||||
}
|
||||
|
||||
function getPageResult(res) {
|
||||
return res?.result || {}
|
||||
}
|
||||
|
||||
function getDataResult(res) {
|
||||
return res?.result || null
|
||||
}
|
||||
|
||||
function getResponseMessage(res, fallback = '操作失败') {
|
||||
return res?.message || res?.msg || fallback
|
||||
}
|
||||
|
||||
function sliceDateValue(value) {
|
||||
return value ? String(value).slice(0, 10) : ''
|
||||
}
|
||||
|
||||
function todayString() {
|
||||
const now = new Date()
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(now.getDate()).padStart(2, '0')
|
||||
return `${now.getFullYear()}-${month}-${day}`
|
||||
}
|
||||
|
||||
function formatDateTimeValue(value) {
|
||||
return value ? `${value} 00:00:00` : null
|
||||
}
|
||||
|
||||
function connectToolbar() {
|
||||
const table = tableRef.value
|
||||
const toolbar = toolbarRef.value
|
||||
if (table && toolbar && table.connect) {
|
||||
table.connect(toolbar)
|
||||
}
|
||||
}
|
||||
|
||||
function getStatusLabel(status) {
|
||||
return statusLabelMap[status] || status
|
||||
}
|
||||
|
||||
function getStatusColor(status) {
|
||||
return statusColorMap[status] || 'default'
|
||||
}
|
||||
|
||||
function getStatusLabelByRow(row) {
|
||||
if (isSingleAgreementRecord(row) && row?.status === 'pending_sign') {
|
||||
return '待盖章'
|
||||
}
|
||||
return getStatusLabel(row?.status)
|
||||
}
|
||||
|
||||
function isSingleAgreementRecord(row) {
|
||||
return String(row?.isSingleAgreement || '0') === '1'
|
||||
}
|
||||
|
||||
function canOnlineSign(row) {
|
||||
return (
|
||||
!isSingleAgreementRecord(row) &&
|
||||
String(row.isElectron || '1') === '1' &&
|
||||
SIGN_STAGE_STATUSES.includes(row.status)
|
||||
)
|
||||
}
|
||||
|
||||
function canUploadSigned(row) {
|
||||
return String(row.isElectron || '1') === '0' && SIGN_STAGE_STATUSES.includes(row.status)
|
||||
}
|
||||
|
||||
function canMockFinishSign(row) {
|
||||
if (isSingleAgreementRecord(row)) {
|
||||
return row.status === 'pending_sign'
|
||||
}
|
||||
return String(row.isElectron || '1') === '1' && SIGN_STAGE_STATUSES.includes(row.status)
|
||||
}
|
||||
|
||||
function canDownloadSigned(row) {
|
||||
return Boolean(row.signedFilePath)
|
||||
}
|
||||
|
||||
function canViewApprove(row) {
|
||||
return !isSingleAgreementRecord(row) && Boolean(row.lawContractId)
|
||||
}
|
||||
|
||||
function canViewLawCallback(row) {
|
||||
return !isSingleAgreementRecord(row)
|
||||
}
|
||||
|
||||
function buildQueryPayload(status, pageNo = 1, pageSize = SIGN_LIST_QUERY_PAGE_SIZE) {
|
||||
return {
|
||||
pageNo,
|
||||
pageSize,
|
||||
contractName: queryParam.value.contractName,
|
||||
contractCode: queryParam.value.contractCode,
|
||||
contractType: queryParam.value.contractType,
|
||||
status,
|
||||
dateStart: queryParam.value.dateRange?.[0],
|
||||
dateEnd: queryParam.value.dateRange?.[1],
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchRecordsByStatus(status) {
|
||||
let pageNo = 1
|
||||
let totalCount = 0
|
||||
const records = []
|
||||
|
||||
while (true) {
|
||||
const res = await queryLaunchList(buildQueryPayload(status, pageNo))
|
||||
if (!isSuccessResponse(res)) {
|
||||
throw new Error(getResponseMessage(res, '合同签署列表查询失败'))
|
||||
}
|
||||
const pageResult = getPageResult(res)
|
||||
const data = Array.isArray(pageResult?.data) ? pageResult.data : []
|
||||
totalCount = Number(pageResult?.totalSize ?? pageResult?.total ?? 0)
|
||||
records.push(...data)
|
||||
if (!data.length) {
|
||||
break
|
||||
}
|
||||
if (totalCount > 0 && records.length >= totalCount) {
|
||||
break
|
||||
}
|
||||
if (data.length < SIGN_LIST_QUERY_PAGE_SIZE) {
|
||||
break
|
||||
}
|
||||
pageNo += 1
|
||||
}
|
||||
|
||||
return records
|
||||
}
|
||||
|
||||
function compareRecord(a, b) {
|
||||
const aTime = new Date(a?.createTime || a?.updateTime || 0).getTime()
|
||||
const bTime = new Date(b?.createTime || b?.updateTime || 0).getTime()
|
||||
if (aTime !== bTime) {
|
||||
return bTime - aTime
|
||||
}
|
||||
return Number(b?.id || 0) - Number(a?.id || 0)
|
||||
}
|
||||
|
||||
function refreshTableData() {
|
||||
total.value = allRecords.value.length
|
||||
const pageNo = Number(queryParam.value.pageNo || 1)
|
||||
const pageSize = Number(queryParam.value.pageSize || 10)
|
||||
const startIndex = (pageNo - 1) * pageSize
|
||||
tableData.value = allRecords.value.slice(startIndex, startIndex + pageSize)
|
||||
}
|
||||
|
||||
async function getList() {
|
||||
loading.value = true
|
||||
try {
|
||||
const statuses = queryParam.value.status ? [queryParam.value.status] : SIGN_STAGE_STATUSES
|
||||
const mergedMap = new Map()
|
||||
for (const status of statuses) {
|
||||
const records = await fetchRecordsByStatus(status)
|
||||
records.forEach((item) => {
|
||||
if (!mergedMap.has(item.id)) {
|
||||
mergedMap.set(item.id, item)
|
||||
}
|
||||
})
|
||||
}
|
||||
allRecords.value = Array.from(mergedMap.values()).sort(compareRecord)
|
||||
refreshTableData()
|
||||
} catch (error) {
|
||||
tableData.value = []
|
||||
allRecords.value = []
|
||||
total.value = 0
|
||||
message.error(error?.message || '合同签署列表查询失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleQuery() {
|
||||
queryParam.value.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
queryParam.value = {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
contractName: undefined,
|
||||
contractCode: undefined,
|
||||
contractType: undefined,
|
||||
status: undefined,
|
||||
dateRange: [],
|
||||
}
|
||||
getList()
|
||||
}
|
||||
|
||||
function onShowSizeChange(current, pageSize) {
|
||||
queryParam.value.pageNo = current
|
||||
queryParam.value.pageSize = pageSize
|
||||
refreshTableData()
|
||||
}
|
||||
|
||||
function handleView(row) {
|
||||
getLaunchDetail(row.id).then((res) => {
|
||||
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
||||
message.error(getResponseMessage(res, '获取合同详情失败'))
|
||||
return
|
||||
}
|
||||
contractModalRef.value?.showModal(getDataResult(res), 'view')
|
||||
})
|
||||
}
|
||||
|
||||
function handleViewApprove(row) {
|
||||
if (!row.lawContractId) {
|
||||
message.warning('当前暂无法务合同ID,审批记录地址尚未生成')
|
||||
return
|
||||
}
|
||||
|
||||
queryApproveViewUrl({
|
||||
contractId: String(row.lawContractId),
|
||||
ivUser: row.initiator || 'admin',
|
||||
}).then((res) => {
|
||||
if (!isSuccessResponse(res)) {
|
||||
message.error(getResponseMessage(res, '获取审批记录查看地址失败'))
|
||||
return
|
||||
}
|
||||
const approveViewUrl = res?.result?.approveViewUrl
|
||||
if (!approveViewUrl) {
|
||||
message.warning('审批记录地址为空')
|
||||
return
|
||||
}
|
||||
window.open(approveViewUrl, '_blank')
|
||||
})
|
||||
}
|
||||
|
||||
function buildLawCallbackModalContent(sample) {
|
||||
const approvedJson = JSON.stringify({ data: sample?.approvedData || {} }, null, 2)
|
||||
const rejectedJson = JSON.stringify({ data: sample?.rejectedData || {} }, null, 2)
|
||||
return h(
|
||||
'div',
|
||||
{ style: { display: 'flex', flexDirection: 'column', gap: '12px' } },
|
||||
[
|
||||
h('div', [
|
||||
h('div', { style: { marginBottom: '6px', fontWeight: 600 } }, '回调地址'),
|
||||
h(
|
||||
'div',
|
||||
{ style: { color: '#666', wordBreak: 'break-all' } },
|
||||
`${sample?.callbackMethod || 'POST'} ${sample?.callbackUrl || '/contract/launch/callback/law/approve'}`,
|
||||
),
|
||||
]),
|
||||
h('div', [
|
||||
h('div', { style: { marginBottom: '6px', fontWeight: 600 } }, '审批通过 data'),
|
||||
h(
|
||||
'pre',
|
||||
{
|
||||
style: {
|
||||
margin: 0,
|
||||
maxHeight: '260px',
|
||||
overflow: 'auto',
|
||||
padding: '12px',
|
||||
borderRadius: '8px',
|
||||
background: '#f6f8fa',
|
||||
fontSize: '12px',
|
||||
lineHeight: '1.6',
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
},
|
||||
},
|
||||
approvedJson,
|
||||
),
|
||||
]),
|
||||
h('div', [
|
||||
h('div', { style: { marginBottom: '6px', fontWeight: 600 } }, '审批驳回 data'),
|
||||
h(
|
||||
'pre',
|
||||
{
|
||||
style: {
|
||||
margin: 0,
|
||||
maxHeight: '220px',
|
||||
overflow: 'auto',
|
||||
padding: '12px',
|
||||
borderRadius: '8px',
|
||||
background: '#f6f8fa',
|
||||
fontSize: '12px',
|
||||
lineHeight: '1.6',
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
},
|
||||
},
|
||||
rejectedJson,
|
||||
),
|
||||
]),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
function handleViewLawCallback(row) {
|
||||
getLawApproveCallbackSample(row.id).then((res) => {
|
||||
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
||||
message.error(getResponseMessage(res, '获取法务回调样例失败'))
|
||||
return
|
||||
}
|
||||
const sample = getDataResult(res)
|
||||
Modal.info({
|
||||
title: `法务回调 data 结构:${row.contractName}`,
|
||||
width: 900,
|
||||
okText: '知道了',
|
||||
content: buildLawCallbackModalContent(sample),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function handleMockFinishSign(row) {
|
||||
const singleAgreement = isSingleAgreementRecord(row)
|
||||
Modal.confirm({
|
||||
title: singleAgreement ? '测试操作:模拟盖章完成' : '测试操作:模拟签署完成',
|
||||
content: singleAgreement
|
||||
? `模拟单方协议【${row.contractName}】内部业务章加盖完成,状态将变为「已完成」。`
|
||||
: `模拟合同【${row.contractName}】签署完成,状态将变为「待采集」。`,
|
||||
okText: '确认模拟',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
const signDate = sliceDateValue(row.signDate) || todayString()
|
||||
const sealDate = sliceDateValue(row.sealDate) || signDate
|
||||
const activeDate =
|
||||
sliceDateValue(row.activeDate) || sliceDateValue(row.effectiveStart) || signDate
|
||||
return finishLaunchSeal(row.id, {
|
||||
sealContractId: row.sealContractId || `TEST-SEAL-${row.id}`,
|
||||
sealContractStatus: 2000,
|
||||
signedFileName: `${row.contractName || '合同'}-测试已签署.pdf`,
|
||||
signedFilePath: row.contractTextFilePath || `mock-signed/${row.id}.pdf`,
|
||||
signedFileType: 'pdf',
|
||||
signedFileSize: row.contractTextFileSize || null,
|
||||
signedOfflineReason: singleAgreement
|
||||
? '测试按钮模拟内部业务章加盖完成'
|
||||
: '测试按钮模拟签署完成,真实签章接入后可删除此按钮',
|
||||
signDate: formatDateTimeValue(signDate),
|
||||
sealDate: formatDateTimeValue(sealDate),
|
||||
activeDate: formatDateTimeValue(activeDate),
|
||||
}).then((res) => {
|
||||
if (!isSuccessResponse(res)) {
|
||||
message.error(
|
||||
getResponseMessage(res, singleAgreement ? '测试盖章完成失败' : '测试签署完成失败'),
|
||||
)
|
||||
return
|
||||
}
|
||||
message.success(
|
||||
singleAgreement ? '测试盖章完成,单方协议已办结' : '测试签署完成,合同已流转到待采集',
|
||||
)
|
||||
getList()
|
||||
})
|
||||
},
|
||||
icon: null,
|
||||
})
|
||||
}
|
||||
|
||||
function handleDownloadSigned(row) {
|
||||
const filePath = row.signedFilePath
|
||||
const fileName = row.signedFileName || `${row.contractName || '合同'}-已签署版.pdf`
|
||||
if (!filePath) {
|
||||
message.warning('暂无签署文件可下载')
|
||||
return
|
||||
}
|
||||
downloadLaunchFile(filePath)
|
||||
.then((res) => {
|
||||
const arrayBuffer = res?.data ?? res
|
||||
const blob = new Blob([arrayBuffer])
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = fileName
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
})
|
||||
.catch(() => {
|
||||
message.error('签署文件下载失败')
|
||||
})
|
||||
}
|
||||
|
||||
function handleUploadSigned(row) {
|
||||
uploadSignedModalRef.value?.showModal(row)
|
||||
}
|
||||
|
||||
function handleOnlineSign(row) {
|
||||
getLaunchDetail(row.id).then(async (res) => {
|
||||
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
||||
message.error(getResponseMessage(res, '获取合同详情失败'))
|
||||
return
|
||||
}
|
||||
const detail = getDataResult(res)
|
||||
if (!detail.contractTextFilePath) {
|
||||
await handleWatermarkUpload(row)
|
||||
return
|
||||
}
|
||||
mixedSignModalRef.value?.showModal({
|
||||
...detail,
|
||||
contractId: detail.sealContractId,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function handleWatermarkUpload(row) {
|
||||
let file
|
||||
const ok = await new Promise((resolve) => {
|
||||
Modal.confirm({
|
||||
title: '法务水印文件缺失',
|
||||
content: h('div', [
|
||||
h(
|
||||
'p',
|
||||
{ style: 'margin-bottom: 12px' },
|
||||
'法务审批回调未带回带水印的合同文件,请手动上传带水印的合同文件后继续在线签订。',
|
||||
),
|
||||
h('input', {
|
||||
type: 'file',
|
||||
accept: '.pdf,.doc,.docx',
|
||||
style: 'margin-top: 8px; width: 100%',
|
||||
onChange(e) {
|
||||
file = e.target.files?.[0]
|
||||
},
|
||||
}),
|
||||
]),
|
||||
okText: '上传并继续',
|
||||
cancelText: '取消',
|
||||
async onOk() {
|
||||
if (!file) {
|
||||
message.warning('请先选择文件')
|
||||
return false
|
||||
}
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
try {
|
||||
const res = await uploadWatermarkFile(row.id, formData)
|
||||
if (!isSuccessResponse(res)) {
|
||||
message.error(getResponseMessage(res, '上传水印文件失败'))
|
||||
return false
|
||||
}
|
||||
message.success('水印文件上传成功')
|
||||
resolve(true)
|
||||
} catch {
|
||||
message.error('上传水印文件失败')
|
||||
return false
|
||||
}
|
||||
},
|
||||
onCancel() {
|
||||
resolve(false)
|
||||
},
|
||||
})
|
||||
})
|
||||
if (!ok) return
|
||||
getLaunchDetail(row.id).then((res) => {
|
||||
if (!isSuccessResponse(res) || !getDataResult(res)) return
|
||||
const detail = getDataResult(res)
|
||||
mixedSignModalRef.value?.showModal({
|
||||
...detail,
|
||||
contractId: detail.sealContractId,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function handleSignBusinessUpdated() {
|
||||
getList()
|
||||
}
|
||||
|
||||
function syncTableHeight() {
|
||||
nextTick(() => {
|
||||
const viewportHeight = window.innerHeight
|
||||
const queryHeight = queryContainer.value?.offsetHeight || 0
|
||||
const pageHeight = pageWrapperRef.value?.$el?.offsetHeight || 56
|
||||
tableHeight.value = Math.max(320, viewportHeight - queryHeight - pageHeight - 320)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handleQuery()
|
||||
nextTick(() => {
|
||||
connectToolbar()
|
||||
syncTableHeight()
|
||||
})
|
||||
window.addEventListener('resize', syncTableHeight)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', syncTableHeight)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.section-container {
|
||||
height: calc(100vh - 116px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.query-card {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.table-card {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.section-query-container :deep(.ant-form-item) {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.section-query-container :deep(.ant-form-item .ant-form-item-control) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.table-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.table-pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 12px 4px 2px;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.section-container {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user