This commit is contained in:
2026-10-08 18:34:09 +08:00
commit 2933904f4c
905 changed files with 112129 additions and 0 deletions
+470
View File
@@ -0,0 +1,470 @@
<template> <!-- 创建合同 -->
<view class="wrap">
<!-- 吸顶 -->
<u-sticky offset-top="0" customNavHeight="0">
<uititle :title="isAdd ? '创建合同' : '修改合同'"></uititle>
</u-sticky>
<view class="layout layout-create">
<view class="group flex align-center">
<view class="label">
<text>标题:</text>
<text class="required">*</text>
</view>
<view class="flex-1 m-left-10">
<u--input
placeholder="请输入内容"
border="bottom"
v-model="formData.title"
></u--input>
</view>
</view>
<view v-for="(item, index) in formData.thirdList" :key="index"
class="flex justify-start items-start my-10 flex-col">
<view class="text-left">第{{ ['三', '四', '五', '六', '七', '八', '九', '十'][index] }}方:</view>
<view class="mt-10 w-100p">
<el-input v-model="formData.thirdList[index].phone"
:placeholder="`请输入第${['三', '四', '五', '六', '七', '八', '九', '十'][index]}方手机号`"/>
</view>
<view class="mt-10 w-100p flex justify-start items-center">
<el-input v-model="formData.thirdList[index].signKeyword"
:placeholder="`请输入第${['三', '四', '五', '六', '七', '八', '九', '十'][index]}方签章关键字`"/>
<view class="flex justify-center items-center mt-10">
<i class="el-icon-circle-plus text-main text-50 ml-35" @click="addThird"/>
<i class="el-icon-remove text-50 ml-35" @click="removeThird(index)"/>
</view>
</view>
</view>
<p class="text-25 text-gray mt-20">如无第三方签约可忽略,签章关键字使用方法同下。</p>
<view class="group flex align-center m-top-30">
<view class="label">
<text>签署截止日期:</text>
<text class="required">*</text>
</view>
<view class="flex-1 m-left-10" @click="showDatePicker = true">
<u--input
style="pointer-events:none"
placeholder="点击选择签署截止日期"
border="surround"
v-model="signEndDate"
suffixIcon="calendar"
suffixIconStyle="font-size: 26px;color: #909399"
:customStyle="inputClass"
></u--input>
</view>
</view>
<view class="text-24 text-gray mt-10">
发起方填写签署截止日期和时间即为各签约方共同认可的签署时间,请酌情填写仔细阅读签署文件。
</view>
<view class="group flex flex-col justify-start items-start mt-20">
<view class="label">签约发起方关键字:</view>
<view class="w-100p">
<u--input
placeholder="发起方盖章关键字"
v-model="formData.buyerSignKeyword"
></u--input>
</view>
<view class="text-24 text-gray mt-10">
请先在手机办公软件上(Office、WPS等)内,复制粘贴或输入预盖章部位关键字核验确认位置和内容,再点击上传文档核实无误后可返回签署页面输入测验关键字后进行签署(如同一关键字文档多次使用无误后可考虑省略检测流程)。系统将按此关键字进行签章位置的定位,将电子章盖在这个关键字上面。凡出现关键字的地方均会盖上指定用户的电子章,建议关键字在合同中保持唯一。
</view>
</view>
<view class="group flex flex-col justify-start items-start mt-20">
<view class="label">签约接收方关键字:</view>
<view class="w-100p">
<u--input
placeholder="接收方盖章关键字"
v-model="formData.sellerSignKeyword"
></u--input>
</view>
<view class="text-24 text-gray mt-10">
请先在手机办公软件上(Office、WPS等)内,复制粘贴或输入预盖章部位关键字核验确认位置和内容,再点击上传文档核实无误后可返回签署页面输入测验关键字后进行签署(如同一关键字文档多次使用无误后可考虑省略检测流程)。系统将按此关键字进行签章位置的定位,将电子章盖在这个关键字上面。凡出现关键字的地方均会盖上指定用户的电子章,建议关键字在合同中保持唯一。
</view>
</view>
<view class="group flex align-center m-top-30">
<view class="label">签署文档:</view>
</view>
<view class="file-box m-top-30">
<view class="file">
<view class="justify-between flex" v-if="formData.contractFile">
<view class="icon flex justify-start items-center text-25">
<file-pdf-one theme="outline" class="pointer" size="40" fill="#f5a623"
strokeLinecap="square" @click="previewDoc"/>
<text class="ml-10" v-if="Array.isArray(formData.contractFile)">
{{ formData.contractFile[formData.contractFile.length - 1].split('-').pop() }}
</text>
<text class="ml-10" v-else>{{ formData.contractFile.split('-').pop() }}</text>
</view>
<u-icon name="trash" size="26" @click="formData.contractFile = ''"></u-icon>
</view>
<view class="add" v-else @click="uploadContract">+上传合同</view>
</view>
</view>
<view class="text-25 text-gray flex-1 mt-15">
仅支持PDF文件,如有其他附件或多个单据签约请合并成一个PDF文档上传。且仅支持纵向文档上传,横向文档显示不完整。
电子签署文件中需添加以下内容以增强双方约束力和文件法律效力:签约双方均自愿通过以“电子签约”形式进行签约,电子签名和加盖电子印章双方均予以认可具有法律效力。
</view>
<!-- <view class="group flex items-start m-top-30 w-100p">-->
<!-- <view class="flex flex-col justify-start items-start flex-1">-->
<!-- <view class="flex justify-starts">-->
<!-- <view class="label">附件:</view>-->
<!-- <view class="nmb">({{ formData.otherFile.length }})</view>-->
<!-- </view>-->
<!-- <view class="text-20 text-gray mt-10">仅支持PDF</view>-->
<!-- </view>-->
<!-- <view class="add" @click="addOtherFile">+添加附件</view>-->
<!-- </view>-->
<!-- <view class="file-box m-top-30" v-if="formData.otherFile.length > 0">-->
<!-- <view class="file">-->
<!-- <view class="item flex justify-between" v-for="(item, index) in formData.otherFile" :key="index">-->
<!-- <view class="flex justify-start items-start">-->
<!-- <view class="icon">-->
<!-- <file-pdf-one theme="outline" class="pointer" size="40" fill="#f5a623"-->
<!-- strokeLinecap="square"/>-->
<!-- </view>-->
<!-- <view class="info flex-1 m-left-30">-->
<!-- <view class="name">附件{{ index + 1 }}</view>-->
<!-- </view>-->
<!-- </view>-->
<!-- <u-icon name="trash" size="26" @click="removeFile(index)"></u-icon>-->
<!-- </view>-->
<!-- </view>-->
<!-- </view>-->
</view>
<u-button class="public-btn" text="确定" @click="create"></u-button>
<u-datetime-picker :show="showDatePicker" v-model="expireTime" mode="datetime"
:minDate="minDate" @cancel="showDatePicker = false"
@confirm="handConfirmDate"></u-datetime-picker>
<el-dialog title="合同预览" :visible.sync="showPdf" width="90%" append-to-body>
<view style="height: 1000rpx">
<u-pdf :url="pdfUrl" v-if="pdfUrl" :showBottom="false" :show-timer="false" :disabled="false" viewHeight="800rpx"
@onClickReader="closePdf"/>
</view>
</el-dialog>
</view>
</template>
<script>
import uititle from '@/components/common/title'
import {FilePdfOne} from "@icon-park/vue";
import dayjs from "dayjs";
import md5 from 'js-md5';
import uPdf from '@/components/u-pdf/u-pdf.vue'
const SIGN_CONFIRM_CONTENT = `1、您确认,若全体各签约方未完成全部签约流程,本签约文件不发生法律效力,系统无法对该签约文件予以保存及提供司法调证服务;
2、您确认,当前签约的实名主体名称、手机号码与注册认证阶段提交的信息未发生变更;若发生变更,必须及时进行重新认证后方可继续签约,避免产生严重法律风险与经济损失;
3、您确认,本次签约为本系统实名认证主体操作,如若不是实名认证主体操作,授权委托人需本系统进行授权认证后签约;
4、您确认,您与其他签约方均系在平等、自愿的基础上完成本次签约;
5、你确认,此次签约内容符合《中华人民共和国电子签名法》《民法典》生效保护范畴;
6、如因法律法规、监管部门政策更新等原因,系统提示您重新进行实名认证、证书续期等重要提示时,请您必须在签约前完成,否则造成任何损失,平台不承担任何责任;以上签约条件您均已经知悉并确认,否则您有权拒绝本次签约。`
export default {
components: {
FilePdfOne,
uititle,
uPdf,
},
data() {
return {
inputClass: {
// height: '20px',
width: '200px',
borderRadius: 0,
},
formData: {
contractId: '',
signEndDate: '',
title: '',
contractFile: '',
buyerSignKeyword: '',
sellerSignKeyword: '',
otherFile: [],
thirdList: [{phone: '', signKeyword: ''}],
},
showDatePicker: false,
minDate: Number(new Date()),
signEndDate: '',
expireTime: '',
isAdd: true,
pdfUrl: '',
showPdf: false,
};
},
methods: {
handConfirmDate({value}) {
this.signEndDate = dayjs(value).format('YYYY-MM-DD HH:mm:ss')
this.formData.signEndDate = dayjs(value).toISOString()
this.showDatePicker = false
},
uploadContract() {
uni.chooseFile({
count: 1,
extension: ['.pdf'],
success: ({tempFiles}) => {
if ((new RegExp(/\s+/g).test(tempFiles[0].name))) {
uni.showToast({
title: '文件名不能有空格',
icon: 'none'
})
return
}
const filename = tempFiles[0].name.split('.')[0]
uniCloud.uploadFile({
filePath: tempFiles[0].path,
cloudPath: dayjs().format('YYYY/MM/DD') + '/' + md5(tempFiles[0].name + Math.random().toString()) + '-' + filename + '.pdf',
success: ({fileID}) => {
uniCloud.getTempFileURL({
fileList: [fileID]
}).then(res => {
this.formData.contractFile = res.fileList[0].tempFileURL
})
}
})
}
});
},
addOtherFile() {
uni.chooseFile({
count: 9,
extension: ['.pdf'],
success: ({tempFiles}) => {
tempFiles.forEach(file => {
uniCloud.uploadFile({
filePath: file.path,
cloudPath: dayjs().format('YYYY/MM/DD') + '/' + file.name,
success: ({fileID}) => {
uniCloud.getTempFileURL({
fileList: [fileID]
}).then(res => {
this.formData.otherFile.push(res.fileList[0].tempFileURL)
})
}
})
})
}
});
},
removeFile(index) {
this.formData.otherFile.splice(index, 1)
},
async create() {
if (!this.formData.title) return this.$message.error('标题不能为空')
if (!this.formData.signEndDate) return this.$message.error('签署截止日期不能为空')
if (!this.formData.buyerSignKeyword) return this.$message.error('签约发起方关键字不能为空')
if (!this.formData.sellerSignKeyword) return this.$message.error('签约接收方关键字不能为空')
if (!this.formData.contractFile) return this.$message.error('签署文档不能为空')
this.formData.thirdList = this.formData.thirdList.filter(item => item.phone && item.signKeyword)
if (this.formData.thirdList.length > 0) {
for (let i = 0; i < this.formData.thirdList.length; i++) {
const item = this.formData.thirdList[i]
if (item.phone === '') {
vk.toast('请填写手机号')
return false
}
if (item.signKeyword === '') {
vk.toast('请填写签章关键字')
return false
}
if (item.phone === this.formData.phone) {
vk.toast('第三方手机号不能与接收方手机号相同')
return false
}
}
}
const thirdKeywords = []
for (let i = 0; i < this.formData.thirdList.length; i++) {
const item = this.formData.thirdList[i]
if (item.phone && item.signKeyword) {
thirdKeywords.push(item.signKeyword)
}
}
uni.showLoading({title: '查验中'})
vk.request({
method: "POST",
url: 'https://hcsc.gxwebsoft.com/api/read-pdf',
header: {
"content-type": "application/json; charset=utf-8",
},
data: {
file: this.formData.contractFile,
buyerSignKeyword: this.formData.buyerSignKeyword,
sellerSignKeyword: this.formData.sellerSignKeyword,
thirdKeywords
},
success: async (pdfRes) => {
},
fail: (pdfRes) => {
uni.hideLoading()
this.$message.error(pdfRes.content.msg)
},
complete: async (pdfRes) => {
uni.hideLoading()
if (pdfRes.code !== 200) this.$message.error(pdfRes.content.msg)
else {
uni.removeStorageSync(`signInfo`)
uni.removeStorageSync(`fileName`)
uni.showModal({
title: '提示',
content: SIGN_CONFIRM_CONTENT,
showCancel: false,
confirmText: '知悉同意',
success: async res => {
if (res.confirm) {
await vk.callFunction({
url: 'client/contract/kh/launch',
title: '请求中...',
data: this.formData,
});
vk.redirectTo({url: '/pages/public/contract?tab=1'})
}
}
})
// 此处统一使用 SIGN_CONFIRM_CONTENT
// confirmButtonText: '知悉同意',
// showCancelButton: false,
// type: 'warning'
// })
//
}
console.log('complete:', pdfRes)
},
})
},
addThird() {
if (this.formData.thirdList.length < 8) this.formData.thirdList.push({
phone: '',
signKeyword: '',
})
},
removeThird(index) {
if (this.formData.thirdList.length === 1) return
this.formData.thirdList.splice(index, 1)
},
previewDoc() {
// uni.setStorageSync(`signInfo_${this.formData.contractId}`, JSON.stringify(this.formData))
// uni.setStorageSync(`fileName`, this.fileName)
// window.location.href = this.formData.contractFile
if (Array.isArray(this.formData.contractFile)) {
this.pdfUrl = this.formData.contractFile[0]
} else
this.pdfUrl = this.formData.contractFile
this.showPdf = true
},
closePdf() {
this.pdfUrl = ''
this.showPdf = false
}
},
async onLoad({contractId}) {
this.formData.contractId = contractId
const {data} = await vk.callFunction({
url: 'client/contract/kh/getDetail',
title: '请求中...',
data: {
contractId
},
});
this.signEndDate = dayjs(data.signEndDate).format('YYYY-MM-DD HH:mm:ss')
this.formData.signEndDate = data.signEndDate
this.formData.title = data.docTitle
if (Array.isArray(data.contractFile)) {
this.formData.contractFile = data.contractFile[0]
} else
this.formData.contractFile = data.contractFile
this.formData.buyerSignKeyword = data.buyerSignKeyword
this.formData.sellerSignKeyword = data.sellerSignKeyword
this.formData.otherFile = data.otherFile ?? []
this.formData.thirdList = (data.thirdList && data.thirdList.length > 0) ? data.thirdList : [{
phone: '',
signKeyword: ''
}]
console.log(this.formData.thirdList)
this.isAdd = data.status === 0
},
// onShow() {
// if (uni.getStorageSync(`signInfo_${this.formData.contractId}`)) {
// this.formData = JSON.parse(uni.getStorageSync(`signInfo_${this.formData.contractId}`))
// if (this.formData.signEndDate) this.signEndDate = dayjs(this.formData.signEndDate).format('YYYY-MM-DD')
// if (uni.getStorageSync('fileName')) this.fileName = uni.getStorageSync('fileName')
// }
// },
}
</script>
<style lang="scss">
page {
background: #ffffff;
}
.layout-create {
padding: 20rpx 30rpx;
}
.label {
font-size: 26rpx;
line-height: 40rpx;
color: #101010;
position: relative;
}
.nmb {
font-size: 26rpx;
font-weight: 600;
line-height: 40rpx;
color: #999999;
}
.add {
font-size: 26rpx;
line-height: 40rpx;
color: #A92924;
}
.file-box {
padding: 20rpx 20rpx;
border: 1px solid #f2f2f2;
.item {
margin-top: 20rpx;
padding: 10rpx 0;
.icon {
width: 90rpx;
height: 90rpx;
}
.info {
padding: 10rpx 0;
border-bottom: 1px solid #f9f9f9;
}
.name {
font-size: 26rpx;
line-height: 40rpx;
color: #666666;
}
.kb {
font-size: 24rpx;
line-height: 40rpx;
color: #999999;
}
}
}
.upload {
margin: 20rpx 0 0;
width: 200rpx;
height: 60rpx;
color: #A92924;
background-color: rgba(169, 41, 36, 0.3);
}
.public-btn {
width: 80%;
margin-top: 60rpx;
color: #ffffff;
background: #A92924;
}
</style>