This commit is contained in:
parent
14208fbfd2
commit
46a34253ab
|
|
@ -2,10 +2,10 @@
|
|||
<view class="signature-container">
|
||||
<view class="toolbar">
|
||||
<up-button text="清除" type="default" size="small" @tap="handleClear" />
|
||||
<up-button text="保存" type="primary" size="small" @tap="handleSave" />
|
||||
<!-- <up-button text="保存" type="primary" size="small" @tap="handleSave" /> -->
|
||||
</view>
|
||||
|
||||
<view> 签名区域 </view>
|
||||
<view class="title"> 签名区域 </view>
|
||||
<canvas
|
||||
class="signature-canvas"
|
||||
:canvas-id="canvasId"
|
||||
|
|
@ -19,7 +19,8 @@
|
|||
@mousemove="handleMove"
|
||||
@mouseup="handleEnd"
|
||||
@mouseleave="handleEnd"
|
||||
></canvas>
|
||||
>
|
||||
</canvas>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
|
@ -33,7 +34,7 @@ const props = defineProps({
|
|||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 240,
|
||||
default: 280,
|
||||
},
|
||||
lineWidth: {
|
||||
type: Number,
|
||||
|
|
@ -50,14 +51,21 @@ const emit = defineEmits(['save', 'clear'])
|
|||
const ctx = ref(null)
|
||||
const isDrawing = ref(false)
|
||||
const lastPoint = ref({ x: 0, y: 0 })
|
||||
// 是否已经有笔迹,用于判断空白签名
|
||||
const hasDrawn = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
const instance = getCurrentInstance()
|
||||
ctx.value = uni.createCanvasContext(props.canvasId, instance)
|
||||
const setupCtx = () => {
|
||||
if (!ctx.value) return
|
||||
ctx.value.setStrokeStyle(props.strokeColor)
|
||||
ctx.value.setLineWidth(props.lineWidth)
|
||||
ctx.value.setLineCap('round')
|
||||
ctx.value.setLineJoin('round')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const instance = getCurrentInstance()
|
||||
ctx.value = uni.createCanvasContext(props.canvasId, instance)
|
||||
setupCtx()
|
||||
ctx.value.draw()
|
||||
})
|
||||
|
||||
|
|
@ -94,6 +102,8 @@ const handleMove = (e) => {
|
|||
|
||||
ctx.value.stroke()
|
||||
ctx.value.draw(true)
|
||||
// 只要产生过一次笔迹,就认为有签名
|
||||
hasDrawn.value = true
|
||||
lastPoint.value = p
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +124,11 @@ const getPoint = (e) => {
|
|||
|
||||
const handleClear = () => {
|
||||
ctx.value.clearRect(0, 0, 1000, 600)
|
||||
ctx.value.draw()
|
||||
// draw(false) 会重置样式,需重新设置颜色等
|
||||
ctx.value.draw(false, () => {
|
||||
setupCtx()
|
||||
})
|
||||
hasDrawn.value = false
|
||||
emit('clear')
|
||||
}
|
||||
|
||||
|
|
@ -134,6 +148,33 @@ const handleSave = () => {
|
|||
instance,
|
||||
)
|
||||
}
|
||||
|
||||
const exportSignature = () => {
|
||||
// 空白画布,不导出图片,直接提示
|
||||
if (!hasDrawn.value) {
|
||||
uni.$u.toast('请先完成签名')
|
||||
return Promise.reject(new Error('EMPTY_SIGNATURE'))
|
||||
}
|
||||
|
||||
const instance = getCurrentInstance()
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.canvasToTempFilePath(
|
||||
{
|
||||
canvasId: props.canvasId,
|
||||
success: (res) => resolve(res.tempFilePath),
|
||||
fail: (err) => {
|
||||
uni.$u.toast('导出签名失败')
|
||||
reject(err)
|
||||
},
|
||||
},
|
||||
instance,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
exportSignature,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
@ -151,6 +192,14 @@ const handleSave = () => {
|
|||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
padding: 12rpx 0;
|
||||
text-align: center;
|
||||
border-bottom: 1rpx solid #e5e5e5;
|
||||
}
|
||||
|
||||
.signature-canvas {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,11 @@
|
|||
:borderBottom="true"
|
||||
required
|
||||
>
|
||||
<up-input v-model="form.partyA" placeholder="请输入甲方公司名称" />
|
||||
<up-input
|
||||
disabled
|
||||
v-model="form.partyA"
|
||||
placeholder="甲方公司名称"
|
||||
/>
|
||||
</up-form-item>
|
||||
<up-form-item
|
||||
label="地址"
|
||||
|
|
@ -69,7 +73,7 @@
|
|||
:borderBottom="true"
|
||||
required
|
||||
>
|
||||
<up-input v-model="form.partAAdress" placeholder="请输入地址" />
|
||||
<up-input disabled v-model="form.partAAdress" placeholder="地址" />
|
||||
</up-form-item>
|
||||
<up-form-item
|
||||
label="法定代表人"
|
||||
|
|
@ -78,17 +82,17 @@
|
|||
required
|
||||
>
|
||||
<up-input
|
||||
disabled
|
||||
v-model="form.legalPerson"
|
||||
placeholder="请输入法定代表人"
|
||||
placeholder="法定代表人"
|
||||
/>
|
||||
</up-form-item>
|
||||
<up-form-item
|
||||
label="联系电话"
|
||||
prop="partAPhone"
|
||||
:borderBottom="true"
|
||||
required
|
||||
>
|
||||
<up-input v-model="form.partAPhone" placeholder="请输入联系电话" />
|
||||
<up-form-item label="联系电话" :borderBottom="true">
|
||||
<up-input
|
||||
disabled
|
||||
v-model="form.partAPhone"
|
||||
placeholder="联系电话"
|
||||
/>
|
||||
</up-form-item>
|
||||
<up-form-item
|
||||
label="乙方(劳动者姓名)"
|
||||
|
|
@ -96,7 +100,7 @@
|
|||
:borderBottom="true"
|
||||
required
|
||||
>
|
||||
<up-input v-model="form.partyB" placeholder="请输入劳动者姓名" />
|
||||
<up-input disabled v-model="form.partyB" placeholder="劳动者姓名" />
|
||||
</up-form-item>
|
||||
<up-form-item
|
||||
label="身份证号"
|
||||
|
|
@ -104,7 +108,11 @@
|
|||
:borderBottom="true"
|
||||
required
|
||||
>
|
||||
<up-input v-model="form.partBIdCard" placeholder="请输入身份证号" />
|
||||
<up-input
|
||||
disabled
|
||||
v-model="form.partBIdCard"
|
||||
placeholder="身份证号"
|
||||
/>
|
||||
</up-form-item>
|
||||
<up-form-item
|
||||
label="乙方电话"
|
||||
|
|
@ -112,7 +120,11 @@
|
|||
:borderBottom="true"
|
||||
required
|
||||
>
|
||||
<up-input v-model="form.partBPhone" placeholder="请输入联系方式" />
|
||||
<up-input
|
||||
disabled
|
||||
v-model="form.partBPhone"
|
||||
placeholder="乙方电话"
|
||||
/>
|
||||
</up-form-item>
|
||||
<up-form-item
|
||||
label="联系住址"
|
||||
|
|
@ -120,7 +132,11 @@
|
|||
:borderBottom="true"
|
||||
required
|
||||
>
|
||||
<up-input v-model="form.partBAdress" placeholder="请输入联系住址" />
|
||||
<up-input
|
||||
disabled
|
||||
v-model="form.partBAdress"
|
||||
placeholder="联系住址"
|
||||
/>
|
||||
</up-form-item>
|
||||
<up-form-item
|
||||
label="合同生效日期"
|
||||
|
|
@ -143,10 +159,29 @@
|
|||
prop="workTask"
|
||||
:borderBottom="true"
|
||||
>
|
||||
<up-input v-model="form.workTask" placeholder="请输入工作岗位" />
|
||||
<up-input disabled v-model="form.workTask" placeholder="工作岗位" />
|
||||
</up-form-item>
|
||||
<up-form-item label="工作地点" prop="workAdress" :borderBottom="true">
|
||||
<up-input v-model="form.workAdress" placeholder="请输入工作地点" />
|
||||
<up-input
|
||||
disabled
|
||||
v-model="form.workAdress"
|
||||
placeholder="工作地点"
|
||||
/>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item
|
||||
label="是否为小包干班组"
|
||||
prop="isXbg"
|
||||
:borderBottom="true"
|
||||
>
|
||||
<view style="width: 100%">
|
||||
<CommonPicker
|
||||
v-model="form.isXbg"
|
||||
:options="xbgOptions"
|
||||
@change="handleSelectXbg"
|
||||
placeholder="请选择是否为小包干班组"
|
||||
/>
|
||||
</view>
|
||||
</up-form-item>
|
||||
</view>
|
||||
|
||||
|
|
@ -171,6 +206,7 @@
|
|||
<up-form-item
|
||||
label="核定标准(元)"
|
||||
prop="verStand"
|
||||
required
|
||||
:borderBottom="true"
|
||||
>
|
||||
<up-input v-model="form.verStand" placeholder="请输入核定标准" />
|
||||
|
|
@ -183,6 +219,7 @@
|
|||
label="绩效核定方式"
|
||||
prop="achievementsVerification"
|
||||
:borderBottom="true"
|
||||
required
|
||||
>
|
||||
<view style="width: 100%">
|
||||
<CommonPicker
|
||||
|
|
@ -198,6 +235,7 @@
|
|||
label="绩效奖金区间(元) 起"
|
||||
prop="bonusA"
|
||||
class="half"
|
||||
required
|
||||
:borderBottom="true"
|
||||
>
|
||||
<up-input v-model="form.bonusA" placeholder="起" />
|
||||
|
|
@ -207,6 +245,7 @@
|
|||
label="绩效奖金区间(元) 止"
|
||||
prop="bonusB"
|
||||
class="half"
|
||||
required
|
||||
:borderBottom="true"
|
||||
>
|
||||
<up-input v-model="form.bonusB" placeholder="止" />
|
||||
|
|
@ -268,26 +307,47 @@
|
|||
<view class="section">
|
||||
<view class="section-title">验证码</view>
|
||||
<view class="captcha-row">
|
||||
<up-input v-model="form.captcha" placeholder="请输入验证码" />
|
||||
<up-button
|
||||
text="获取验证码"
|
||||
type="primary"
|
||||
size="small"
|
||||
:customStyle="{ width: '180rpx', marginLeft: '12rpx' }"
|
||||
@tap="handleGetCaptcha"
|
||||
/>
|
||||
<!-- <up-input v-model="form.captcha" placeholder="请输入验证码" /> -->
|
||||
|
||||
<up-code-input
|
||||
v-model="form.captcha"
|
||||
maxlength="4"
|
||||
space="30"
|
||||
></up-code-input>
|
||||
<view>
|
||||
<up-button
|
||||
:text="
|
||||
captchaCountdown > 0
|
||||
? `${captchaCountdown}s后重试`
|
||||
: '获取验证码'
|
||||
"
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="captchaCountdown > 0"
|
||||
:customStyle="{ width: '180rpx', marginLeft: '12rpx' }"
|
||||
@tap="handleGetCaptcha"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 附件列表 -->
|
||||
<view class="section">
|
||||
<view class="section-title">附件预览</view>
|
||||
<view v-for="item in attachments" :key="item.title" class="attach-row">
|
||||
<view class="attach-title">{{ item.title }}</view>
|
||||
<view
|
||||
class="attach-title"
|
||||
@tap="handlePreview(item)"
|
||||
:style="{
|
||||
backgroundColor: item.isSign ? '#07c160' : '#ed7b2f',
|
||||
}"
|
||||
>
|
||||
{{ item.title }}
|
||||
</view>
|
||||
<up-button
|
||||
text="预览"
|
||||
type="primary"
|
||||
:text="item.isSign ? '已签订' : '未签订'"
|
||||
:type="item.isSign ? 'primary' : 'warning'"
|
||||
size="small"
|
||||
plain
|
||||
:customStyle="{ width: '160rpx' }"
|
||||
@tap="handlePreview(item)"
|
||||
/>
|
||||
|
|
@ -308,19 +368,25 @@
|
|||
>
|
||||
<view class="signature-modal">
|
||||
<Signature
|
||||
:height="380"
|
||||
ref="signatureRef"
|
||||
:height="300"
|
||||
@save="handleSignatureSave"
|
||||
@clear="handleSignatureClear"
|
||||
/>
|
||||
<view class="signature-modal-footer">
|
||||
<up-button
|
||||
text="关闭"
|
||||
type="default"
|
||||
type="primary"
|
||||
plain
|
||||
size="small"
|
||||
:customStyle="{ marginTop: '24rpx' }"
|
||||
@tap="handleCloseSignatureModal"
|
||||
/>
|
||||
<up-button
|
||||
text="确认"
|
||||
type="primary"
|
||||
size="small"
|
||||
@tap="handleConfirmSignature"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</up-popup>
|
||||
|
|
@ -345,16 +411,36 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { getContentStyle } from '@/utils/safeArea'
|
||||
import { realNameHttp } from '@/utils/realNameHttp'
|
||||
import {
|
||||
getWorkerInfoByIdNumberAPI,
|
||||
getFileAddressAPI,
|
||||
previewContractAPI,
|
||||
} from '@/services/realName/contract'
|
||||
|
||||
import dayjs from 'dayjs'
|
||||
import NavBarModal from '@/components/NavBarModal/index.vue'
|
||||
import Signature from '@/components/Signature/index.vue'
|
||||
import CommonPicker from '@/components/CommonPicker/index.vue'
|
||||
import DatePicker from '@/components/DatePicker/index.vue'
|
||||
import { getContentStyle } from '@/utils/safeArea'
|
||||
import { getWorkerInfoByIdNumberAPI } from '@/services/realName/contract'
|
||||
|
||||
const iptIdNumber = ref('')
|
||||
const videoInfo = ref(null)
|
||||
|
||||
const getFileAddressParams = ref({
|
||||
perviewState: 0,
|
||||
partA: '',
|
||||
partB: '',
|
||||
sex: '',
|
||||
workAdress: '',
|
||||
post: '',
|
||||
partBIdCard: '',
|
||||
partBPhone: '',
|
||||
effectDate: '',
|
||||
})
|
||||
const contentStyle = computed(() => {
|
||||
return getContentStyle({
|
||||
includeNavBar: true,
|
||||
|
|
@ -365,6 +451,7 @@ const contentStyle = computed(() => {
|
|||
|
||||
// 表单数据
|
||||
const form = ref({
|
||||
proId: '',
|
||||
partyA: '',
|
||||
partAAdress: '',
|
||||
legalPerson: '',
|
||||
|
|
@ -373,12 +460,13 @@ const form = ref({
|
|||
partBIdCard: '',
|
||||
partBPhone: '',
|
||||
partBAdress: '',
|
||||
effectDate: '',
|
||||
effectDate: dayjs().format('YYYY-MM-DD'),
|
||||
workTask: '',
|
||||
workAdress: '',
|
||||
verMethod: '',
|
||||
isXbg: '',
|
||||
verMethod: '每天',
|
||||
verStand: '',
|
||||
achievementsVerification: '',
|
||||
achievementsVerification: '每天',
|
||||
bonusA: '',
|
||||
bonusB: '',
|
||||
otherSupply: '',
|
||||
|
|
@ -389,8 +477,9 @@ const form = ref({
|
|||
})
|
||||
|
||||
// 日期选择
|
||||
const startDateValue = ref('')
|
||||
const startDateValue = ref(Date.now())
|
||||
const handleStartDateChange = (val) => {
|
||||
console.log(val)
|
||||
startDateValue.value = val
|
||||
const d = new Date(val)
|
||||
const y = d.getFullYear()
|
||||
|
|
@ -403,6 +492,19 @@ const handleStartDateChange = (val) => {
|
|||
const payTypeOptions = ['每天']
|
||||
const bonusTypeOptions = ['每天']
|
||||
|
||||
const xbgOptions = [
|
||||
{
|
||||
value: '0',
|
||||
label: '否',
|
||||
},
|
||||
{
|
||||
value: '1',
|
||||
label: '是',
|
||||
},
|
||||
]
|
||||
const handleSelectXbg = (val) => {
|
||||
form.value.isXbg = val.value
|
||||
}
|
||||
const handleSelectPayType = (val) => {
|
||||
form.value.verMethod = val.value
|
||||
}
|
||||
|
|
@ -430,24 +532,36 @@ const handleFaceDelete = () => {
|
|||
// 附件(全部为pdf)
|
||||
const attachments = ref([
|
||||
{
|
||||
title: '施工人员健康承诺书',
|
||||
url: 'https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf',
|
||||
isSign: false,
|
||||
fileType: '1',
|
||||
title: '施工人员健康承诺书',
|
||||
},
|
||||
{
|
||||
url: 'https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf',
|
||||
isSign: false,
|
||||
fileType: '2',
|
||||
title: '安全协议书',
|
||||
url: 'https://www.africau.edu/images/default/sample.pdf',
|
||||
},
|
||||
{
|
||||
title: '施工人员安全告知书',
|
||||
url: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
|
||||
fileType: '3',
|
||||
isSign: false,
|
||||
// url: 'http://192.168.0.14:1917/hnAma/gzRealName/contract/pdf/施工人员安全告知书.pdf',
|
||||
// url: 'http://192.168.0.14:1917/hnAma/gzRealName/contract/pdf/施工人员安全告知书.pdf',
|
||||
url: 'http://192.168.0.38:18080/bnscloud/realnameapp/gzRealName/contract/pdf/施工人员安全告知书.pdf',
|
||||
},
|
||||
{
|
||||
title: '签订用工协议承诺书',
|
||||
url: 'https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf',
|
||||
isSign: false,
|
||||
fileType: '5',
|
||||
title: '签订用工协议承诺书',
|
||||
},
|
||||
{
|
||||
url: 'https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf',
|
||||
isSign: false,
|
||||
fileType: '4',
|
||||
title: '安全承诺书',
|
||||
url: 'https://www.africau.edu/images/default/sample.pdf',
|
||||
},
|
||||
])
|
||||
|
||||
|
|
@ -470,57 +584,198 @@ const handleBack = () => {
|
|||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
const captchaCountdown = ref(0)
|
||||
let captchaTimer = null
|
||||
|
||||
// 获取验证码(前端本地生成,填充到输入框,并开启倒计时)
|
||||
const handleGetCaptcha = () => {
|
||||
uni.$u.toast('验证码已发送(示例)')
|
||||
if (captchaCountdown.value > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const captcha = generateCaptcha()
|
||||
form.value.captcha = captcha
|
||||
uni.$u.toast('验证码已生成,请查看输入框')
|
||||
startCaptchaCountdown()
|
||||
}
|
||||
|
||||
const startCaptchaCountdown = () => {
|
||||
captchaCountdown.value = 60
|
||||
captchaTimer && clearInterval(captchaTimer)
|
||||
captchaTimer = setInterval(() => {
|
||||
if (captchaCountdown.value <= 1) {
|
||||
clearInterval(captchaTimer)
|
||||
captchaTimer = null
|
||||
captchaCountdown.value = 0
|
||||
} else {
|
||||
captchaCountdown.value -= 1
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
// 随机生成一个4位数验证码(支持前导0,优先使用更安全的随机源)
|
||||
const generateCaptcha = () => {
|
||||
// 使用 crypto.getRandomValues 提升随机性,兼容小程序环境时回退到 Math.random
|
||||
const getSecureRandom = () => {
|
||||
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
||||
const buffer = new Uint32Array(1)
|
||||
crypto.getRandomValues(buffer)
|
||||
return buffer[0] % 10000
|
||||
}
|
||||
return Math.floor(Math.random() * 10000)
|
||||
}
|
||||
|
||||
return getSecureRandom().toString().padStart(4, '0')
|
||||
}
|
||||
|
||||
// 预览附件
|
||||
const handlePreview = (item) => {
|
||||
if (!item.url) {
|
||||
uni.$u.toast('附件地址缺失')
|
||||
uni.$u.toast('附件地址缺失,请输入身份证号码,或者通过人脸识别获取附件内容')
|
||||
return
|
||||
}
|
||||
const url = encodeURIComponent(item.url)
|
||||
uni.navigateTo({
|
||||
url: `/pages/work/contract/contractPreview/index?fileUrl=${url}&fileType=pdf&title=${encodeURIComponent(
|
||||
item.title,
|
||||
)}`,
|
||||
url: `/pages/work/contract/contractPreview/index?fileUrl=${url}&fileType=pdf&title=${encodeURIComponent(item.title)}&attachmentType=${item.fileType}&isSign=${item.isSign}&personInfo=${encodeURIComponent(JSON.stringify(getFileAddressParams.value))}`,
|
||||
events: {
|
||||
signed: ({ fileType }) => {
|
||||
console.log('收到签订事件:', { fileType })
|
||||
const target = attachments.value.find((attach) => attach.fileType === fileType)
|
||||
if (target) {
|
||||
target.isSign = true
|
||||
console.log('附件签订状态已更新:', target.title, target.isSign)
|
||||
} else {
|
||||
console.warn('未找到对应的附件:', fileType)
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 兜底:H5 等环境 eventChannel 不可用时,使用全局事件
|
||||
const handleSignedFallback = ({ fileType }) => {
|
||||
const target = attachments.value.find((attach) => attach.fileType === fileType)
|
||||
if (target) {
|
||||
target.isSign = true
|
||||
console.log('兜底事件更新附件签订状态:', target.title, target.isSign)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
uni.$on('contract-signed', handleSignedFallback)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
uni.$off('contract-signed', handleSignedFallback)
|
||||
if (captchaTimer) {
|
||||
clearInterval(captchaTimer)
|
||||
captchaTimer = null
|
||||
}
|
||||
})
|
||||
|
||||
// 预览合同按钮
|
||||
const handlePreviewContract = () => {
|
||||
const handlePreviewContract = async () => {
|
||||
// 基础校验
|
||||
const requiredFields = [
|
||||
{ key: 'partyA', label: '甲方(公司名称)' },
|
||||
{ key: 'partAAdress', label: '地址' },
|
||||
{ key: 'legalPerson', label: '法定代表人' },
|
||||
{ key: 'partAPhone', label: '联系电话' },
|
||||
// { key: 'partAPhone', label: '联系电话' },
|
||||
{ key: 'partyB', label: '乙方(劳动者姓名)' },
|
||||
{ key: 'partBIdCard', label: '身份证号' },
|
||||
{ key: 'partBPhone', label: '联系电话' },
|
||||
{ key: 'startDate', label: '合同生效日期' },
|
||||
{ key: 'effectDate', label: '合同生效日期' },
|
||||
{ key: 'verStand', label: '工资核定标准' },
|
||||
{ key: 'achievementsVerification', label: '绩效核定方式' },
|
||||
{ key: 'bonusA', label: '绩效奖金区间(元) 起' },
|
||||
{ key: 'bonusB', label: '绩效奖金区间(元) 止' },
|
||||
]
|
||||
|
||||
for (const item of requiredFields) {
|
||||
if (!form.value[item.key]) {
|
||||
uni.$u.toast(`${item.label}不能为空`)
|
||||
uni.$u.toast(
|
||||
`${item.label}不能为空,请通过身份证号码查询或者人脸识别获取相关信息并完善表单`,
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 绩效奖金区间校验:开始值不能大于结束值
|
||||
const bonusStart = Number(form.value.bonusA)
|
||||
const bonusEnd = Number(form.value.bonusB)
|
||||
if (Number.isNaN(bonusStart) || Number.isNaN(bonusEnd)) {
|
||||
uni.$u.toast('绩效奖金区间需填写数字')
|
||||
return
|
||||
}
|
||||
if (bonusStart > bonusEnd) {
|
||||
uni.$u.toast('绩效奖金起始值不能大于结束值')
|
||||
return
|
||||
}
|
||||
|
||||
// 签名校验
|
||||
if (!form.value.signaturePath) {
|
||||
uni.$u.toast('请先完成签名')
|
||||
return
|
||||
}
|
||||
|
||||
if (attachments.value.length === 0) {
|
||||
uni.$u.toast('暂无合同附件')
|
||||
// 判断是否所有附件都已签订
|
||||
const allSigned = attachments.value.every((item) => item.isSign)
|
||||
if (!allSigned) {
|
||||
uni.$u.toast('请先完成所有附件的签订')
|
||||
return
|
||||
}
|
||||
handlePreview(attachments.value[0])
|
||||
|
||||
// 判断是否获取短信验证码
|
||||
// if (!form.value.captcha) {
|
||||
// uni.$u.toast('请先获取短信验证码')
|
||||
// return
|
||||
// }
|
||||
|
||||
// 组装参数
|
||||
const message = form.value.captcha
|
||||
const shortMessage = `【博诺思】您正在进行合同签订,验证码为:${message},有效期为5分钟。`
|
||||
const nowDate = dayjs().format('YYYY-MM-DD')
|
||||
const nowDateTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
|
||||
|
||||
const params = {
|
||||
videoUrl: videoInfo.value?.videoUrl || '',
|
||||
partA: form.value.partyA,
|
||||
legalPerson: form.value.legalPerson,
|
||||
partAPhone: form.value.partAPhone,
|
||||
partAIdCard: null,
|
||||
partAAdress: form.value.partAAdress,
|
||||
partB: form.value.partyB,
|
||||
partBPhone: form.value.partBPhone,
|
||||
partBIdCard: form.value.partBIdCard,
|
||||
partBAdress: form.value.partBAdress,
|
||||
workTask: form.value.workTask,
|
||||
workAdress: form.value.workAdress,
|
||||
verMethod: form.value.verMethod,
|
||||
verStand: form.value.verStand,
|
||||
achievementsVerification: form.value.achievementsVerification,
|
||||
bonusA: form.value.bonusA,
|
||||
bonusB: form.value.bonusB,
|
||||
effectDate: form.value.effectDate,
|
||||
otherSupply: form.value.otherSupply,
|
||||
otherMatters: form.value.otherMatters,
|
||||
partBSign: form.value.signaturePath,
|
||||
message,
|
||||
signingDate: nowDate,
|
||||
faceUrl: videoInfo.value?.faceUrl || '',
|
||||
messageTime: nowDateTime,
|
||||
shortMessage,
|
||||
proId: videoInfo.value?.proId,
|
||||
contractTemplateType: 1, // 合同类型
|
||||
agreedMethod: videoInfo.value?.agreedMethod || '',
|
||||
secondContent: videoInfo.value?.secondContent || '',
|
||||
isXbg: form.value.isXbg,
|
||||
}
|
||||
|
||||
console.log('已组装的参数', params)
|
||||
|
||||
// const result = await previewContractAPI(params)
|
||||
|
||||
// handlePreview(attachments.value[0])
|
||||
}
|
||||
|
||||
// 签名
|
||||
|
|
@ -547,11 +802,114 @@ const handleFaceRecognize = () => {
|
|||
|
||||
// 根据身份证号码查询用户信息
|
||||
const handleGetWorkerInfo = async () => {
|
||||
const { data: res } = await getWorkerInfoByIdNumberAPI({
|
||||
const { obj: res } = await getWorkerInfoByIdNumberAPI({
|
||||
idNumber: iptIdNumber.value,
|
||||
})
|
||||
console.log(res, '用户信息')
|
||||
|
||||
if (res !== 'is null') {
|
||||
const {
|
||||
name,
|
||||
address,
|
||||
subPhone,
|
||||
phone,
|
||||
idNumber,
|
||||
postId,
|
||||
postName,
|
||||
represent,
|
||||
subAddress,
|
||||
subId,
|
||||
sex,
|
||||
subName,
|
||||
isXbg,
|
||||
proName,
|
||||
} = res
|
||||
|
||||
form.value.partyA = subName
|
||||
form.value.partAAdress = subAddress
|
||||
form.value.partAPhone = subPhone
|
||||
form.value.legalPerson = represent
|
||||
form.value.partBPhone = phone
|
||||
form.value.partyB = name
|
||||
form.value.partBIdCard = idNumber
|
||||
form.value.partBAdress = address
|
||||
form.value.isXbg = isXbg
|
||||
form.value.workAdress = proName
|
||||
form.value.workTask = postName
|
||||
|
||||
getFileAddressParams.value.partA = subName
|
||||
getFileAddressParams.value.partB = name
|
||||
getFileAddressParams.value.sex = sex
|
||||
getFileAddressParams.value.partBAdress = address
|
||||
getFileAddressParams.value.workAdress = address
|
||||
getFileAddressParams.value.post = postName
|
||||
getFileAddressParams.value.partBIdCard = idNumber
|
||||
getFileAddressParams.value.partBPhone = phone
|
||||
getFileAddressParams.value.effectDate = form.value.effectDate
|
||||
|
||||
// const file1 = await getFileAddressFun('1') // 施工人员健康承诺书
|
||||
// const file2 = await getFileAddressFun('2') // 安全协议书
|
||||
// const file4 = await getFileAddressFun('5') // 签订用工协议承诺书
|
||||
// const file5 = await getFileAddressFun('4') // 安全承诺书
|
||||
|
||||
// const fileList = [file1, file2, file4, file5]
|
||||
|
||||
// attachments.value.forEach((e) => {
|
||||
// const file = fileList.find((f) => f.type === e.fileType)
|
||||
// if (file) {
|
||||
// e.url = file.url
|
||||
// }
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
// 获取附件信息
|
||||
const getFileAddressFun = async (type) => {
|
||||
const { resMsg: res } = await getFileAddressAPI({ ...getFileAddressParams.value, type })
|
||||
return {
|
||||
url: res,
|
||||
type,
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfirmSignature = async () => {
|
||||
try {
|
||||
if (signatureRef.value?.exportSignature) {
|
||||
const tempPath = await signatureRef.value.exportSignature()
|
||||
|
||||
try {
|
||||
const uploadRes = await realNameHttp.uploadFile({
|
||||
url: '/user/uploadFile',
|
||||
filePath: tempPath,
|
||||
name: 'file',
|
||||
formData: {
|
||||
photoType: 1,
|
||||
},
|
||||
})
|
||||
|
||||
if (uploadRes.res === 1 && uploadRes.obj) {
|
||||
form.value.signaturePath = tempPath
|
||||
showSignatureModal.value = false
|
||||
} else {
|
||||
uni.$u.toast(uploadRes.resMsg || '上传失败')
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
} catch (error) {
|
||||
// 子组件已对“未签名”做了提示,这里不再重复提示
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面加载
|
||||
*/
|
||||
onLoad((options) => {
|
||||
if (options.params) {
|
||||
try {
|
||||
videoInfo.value = JSON.parse(decodeURIComponent(options.params))
|
||||
} catch {}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
@ -632,12 +990,13 @@ const handleGetWorkerInfo = async () => {
|
|||
|
||||
.sign-preview {
|
||||
flex: 1;
|
||||
min-height: 200rpx;
|
||||
border: 1rpx solid #e5e5e5;
|
||||
// min-height: 200rpx;
|
||||
}
|
||||
|
||||
.sign-empty-box {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
height: 260rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 8rpx;
|
||||
border: 2rpx solid #e5e5e5;
|
||||
|
|
@ -645,7 +1004,7 @@ const handleGetWorkerInfo = async () => {
|
|||
|
||||
.sign-image {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
height: 260rpx;
|
||||
}
|
||||
|
||||
.sign-buttons {
|
||||
|
|
@ -662,13 +1021,19 @@ const handleGetWorkerInfo = async () => {
|
|||
}
|
||||
|
||||
.signature-modal-footer {
|
||||
margin-top: 24rpx;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.captcha-row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.attach-row {
|
||||
|
|
@ -680,9 +1045,13 @@ const handleGetWorkerInfo = async () => {
|
|||
|
||||
.attach-title {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
padding: 12rpx 0;
|
||||
margin-right: 16rpx;
|
||||
flex: 1;
|
||||
background-color: #07c160;
|
||||
border-radius: 10rpx;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.button-section {
|
||||
|
|
|
|||
|
|
@ -203,14 +203,9 @@ const handleDocumentError = (data) => {
|
|||
* 填写合同
|
||||
*/
|
||||
const handleFillContract = () => {
|
||||
// TODO: 跳转到填写合同页面
|
||||
// uni.showToast({
|
||||
// title: '填写合同功能待实现',
|
||||
// icon: 'none',
|
||||
// })
|
||||
|
||||
const params = encodeURIComponent(JSON.stringify(contractData.value))
|
||||
uni.navigateTo({
|
||||
url: '/pages/work/contract/contractDetails/index',
|
||||
url: `/pages/work/contract/contractDetails/index?params=${params}`,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
</NavBarModal>
|
||||
|
||||
<view class="content-wrapper" :style="contentStyle">
|
||||
<view class="file-title">{{ title }}</view>
|
||||
<DocumentPreview
|
||||
:file-url="fileUrl"
|
||||
file-type="pdf"
|
||||
|
|
@ -17,17 +18,92 @@
|
|||
:show-retry="true"
|
||||
:show-download="true"
|
||||
:container-style="{ width: '100%', height: '100%' }"
|
||||
@load="handleDocumentLoad"
|
||||
@error="handleDocumentError"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="bottom-bar">
|
||||
<up-button
|
||||
v-if="isSafeNotice"
|
||||
:text="readButtonText"
|
||||
type="primary"
|
||||
:disabled="readCountdown > 0 || !!previewError"
|
||||
:customStyle="primaryButtonStyle"
|
||||
@tap="handleReadConfirm"
|
||||
/>
|
||||
<up-button
|
||||
v-else
|
||||
text="去签订"
|
||||
type="primary"
|
||||
:customStyle="primaryButtonStyle"
|
||||
@tap="handleOpenSignatureModal"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<up-popup
|
||||
:show="showSignatureModal"
|
||||
mode="center"
|
||||
round="20"
|
||||
:closeable="false"
|
||||
@close="showSignatureModal = false"
|
||||
customStyle="width: 90%; max-width: 620rpx;"
|
||||
>
|
||||
<view class="signature-modal">
|
||||
<view v-if="isSafetyProtocol" class="protocol-section">
|
||||
<view class="protocol-title">协议时间</view>
|
||||
<view class="protocol-row">
|
||||
<view class="protocol-label">开始时间</view>
|
||||
<view class="protocol-value">{{ protocolStartText }}</view>
|
||||
</view>
|
||||
<view class="protocol-row">
|
||||
<view class="protocol-label">结束时间</view>
|
||||
<view class="protocol-picker">
|
||||
<DatePicker
|
||||
v-model="protocolEndDate"
|
||||
format="YYYY-MM-DD"
|
||||
placeholder="请选择结束时间"
|
||||
:minDate="todayTimestamp"
|
||||
:showArrow="true"
|
||||
@change="handleEndDateChange"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<Signature
|
||||
:height="380"
|
||||
ref="signatureRef"
|
||||
@save="handleSignatureSave"
|
||||
@clear="handleSignatureClear"
|
||||
/>
|
||||
|
||||
<view class="signature-modal-footer">
|
||||
<up-button
|
||||
text="确认签订"
|
||||
type="primary"
|
||||
size="small"
|
||||
:customStyle="{ flex: 1 }"
|
||||
@tap="handleConfirmSignature"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</up-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, onUnmounted, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { getContentStyle } from '@/utils/safeArea'
|
||||
import { realNameHttp } from '@/utils/realNameHttp'
|
||||
import { signProtocolAPI } from '@/services/realName/contract'
|
||||
|
||||
import dayjs from 'dayjs'
|
||||
import NavBarModal from '@/components/NavBarModal/index.vue'
|
||||
import DocumentPreview from '@/components/DocumentPreview/index.vue'
|
||||
import { getContentStyle } from '@/utils/safeArea'
|
||||
import Signature from '@/components/Signature/index.vue'
|
||||
import DatePicker from '@/components/DatePicker/index.vue'
|
||||
|
||||
const contentStyle = computed(() => {
|
||||
return getContentStyle({
|
||||
|
|
@ -38,6 +114,36 @@ const contentStyle = computed(() => {
|
|||
})
|
||||
|
||||
const fileUrl = ref('')
|
||||
const signatureRef = ref(null)
|
||||
const attachmentType = ref('')
|
||||
const title = ref('')
|
||||
const isSign = ref(false)
|
||||
const personInfo = ref({})
|
||||
const type = ref('')
|
||||
const eventChannel = ref(null)
|
||||
const previewError = ref('')
|
||||
|
||||
const showSignatureModal = ref(false)
|
||||
const signaturePath = ref('')
|
||||
const readCountdown = ref(10)
|
||||
const countdownTimer = ref(null)
|
||||
const protocolEndDate = ref(dayjs().startOf('day').valueOf())
|
||||
const todayTimestamp = dayjs().startOf('day').valueOf()
|
||||
|
||||
const isSafeNotice = computed(() => attachmentType.value === '3')
|
||||
const isSafetyProtocol = computed(() => attachmentType.value === '2')
|
||||
|
||||
const protocolStartText = computed(() => dayjs(todayTimestamp).format('YYYY-MM-DD'))
|
||||
const readButtonText = computed(() =>
|
||||
readCountdown.value > 0 ? `我已阅读完毕(${readCountdown.value}s)` : '我已阅读完毕',
|
||||
)
|
||||
const primaryButtonStyle = computed(() => ({
|
||||
flex: 1,
|
||||
backgroundColor: '#07c160',
|
||||
borderColor: '#07c160',
|
||||
height: '88rpx',
|
||||
fontSize: '32rpx',
|
||||
}))
|
||||
|
||||
const handleBack = () => {
|
||||
uni.navigateBack()
|
||||
|
|
@ -46,6 +152,14 @@ const handleBack = () => {
|
|||
onLoad((options) => {
|
||||
if (options.fileUrl) {
|
||||
fileUrl.value = decodeURIComponent(options.fileUrl)
|
||||
attachmentType.value = options.attachmentType || ''
|
||||
title.value = options.title ? decodeURIComponent(options.title) : ''
|
||||
isSign.value = options.isSign === 'true'
|
||||
personInfo.value = options.personInfo
|
||||
? JSON.parse(decodeURIComponent(options.personInfo))
|
||||
: {}
|
||||
eventChannel.value =
|
||||
typeof uni.getOpenerEventChannel === 'function' ? uni.getOpenerEventChannel() : null
|
||||
} else {
|
||||
uni.$u.toast('缺少文件地址')
|
||||
setTimeout(() => {
|
||||
|
|
@ -53,6 +167,157 @@ onLoad((options) => {
|
|||
}, 800)
|
||||
}
|
||||
})
|
||||
|
||||
const handleDocumentLoad = () => {
|
||||
previewError.value = ''
|
||||
if (isSafeNotice.value) {
|
||||
startCountdown()
|
||||
}
|
||||
}
|
||||
|
||||
const handleDocumentError = (err) => {
|
||||
previewError.value = err?.error || '文档加载失败'
|
||||
stopCountdown()
|
||||
}
|
||||
|
||||
const startCountdown = () => {
|
||||
stopCountdown()
|
||||
readCountdown.value = 10
|
||||
countdownTimer.value = setInterval(() => {
|
||||
if (readCountdown.value > 0) {
|
||||
readCountdown.value -= 1
|
||||
} else {
|
||||
stopCountdown()
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const stopCountdown = () => {
|
||||
if (countdownTimer.value) {
|
||||
clearInterval(countdownTimer.value)
|
||||
countdownTimer.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const emitSigned = () => {
|
||||
const payload = {
|
||||
fileType: attachmentType.value,
|
||||
title: title.value,
|
||||
}
|
||||
if (eventChannel.value?.emit) {
|
||||
eventChannel.value.emit('signed', payload)
|
||||
console.log('已发送签订事件(eventChannel):', payload)
|
||||
} else {
|
||||
console.warn('eventChannel 不可用,使用全局事件兜底通知父页面')
|
||||
uni.$emit('contract-signed', payload)
|
||||
}
|
||||
}
|
||||
|
||||
const handleReadConfirm = () => {
|
||||
if (readCountdown.value > 0) return
|
||||
isSign.value = true
|
||||
emitSigned()
|
||||
uni.$u.toast('已阅读并确认')
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
const handleOpenSignatureModal = () => {
|
||||
if (previewError.value) {
|
||||
uni.$u.toast('附件加载失败,暂无法签订')
|
||||
return
|
||||
}
|
||||
showSignatureModal.value = true
|
||||
}
|
||||
|
||||
const handleCloseSignatureModal = () => {
|
||||
showSignatureModal.value = false
|
||||
}
|
||||
|
||||
const handleSignatureSave = (payload) => {
|
||||
signaturePath.value = payload?.tempFilePath || ''
|
||||
}
|
||||
|
||||
const handleSignatureClear = () => {
|
||||
signaturePath.value = ''
|
||||
}
|
||||
|
||||
const handleEndDateChange = (val) => {
|
||||
if (val < todayTimestamp) {
|
||||
protocolEndDate.value = todayTimestamp
|
||||
uni.$u.toast('结束时间不能早于今天')
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfirmSignature = async () => {
|
||||
try {
|
||||
if (signatureRef.value?.exportSignature) {
|
||||
const tempPath = await signatureRef.value.exportSignature()
|
||||
signaturePath.value = tempPath || ''
|
||||
console.log('signaturePath', signaturePath.value)
|
||||
|
||||
try {
|
||||
const uploadRes = await realNameHttp.uploadFile({
|
||||
url: '/user/uploadFile',
|
||||
filePath: tempPath,
|
||||
name: 'file',
|
||||
formData: {
|
||||
photoType: 1,
|
||||
},
|
||||
})
|
||||
|
||||
if (uploadRes.res === 1 && uploadRes.obj) {
|
||||
handleSignProtocol(uploadRes.obj, attachmentType.value)
|
||||
} else {
|
||||
uni.$u.toast(uploadRes.resMsg || '上传失败')
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
} catch (error) {
|
||||
// 子组件已对“未签名”做了提示,这里不再重复提示
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 调用接口签订协议
|
||||
const handleSignProtocol = async (signaturePath, type) => {
|
||||
try {
|
||||
// 组装参数
|
||||
const params = {
|
||||
partBSign: signaturePath,
|
||||
type,
|
||||
perviewState: 1,
|
||||
partA: personInfo.value.partA,
|
||||
partB: personInfo.value.partB,
|
||||
sex: personInfo.value.sex,
|
||||
workAdress: personInfo.value.workAdress,
|
||||
partBIdCard: personInfo.value.partBIdCard,
|
||||
partBPhone: personInfo.value.partBPhone,
|
||||
effectDate: personInfo.value.effectDate,
|
||||
startDate: type == 2 ? protocolStartText.value : '',
|
||||
endDate: type == 2 ? dayjs(protocolEndDate.value).format('YYYY-MM-DD') : '',
|
||||
post: personInfo.value.post,
|
||||
}
|
||||
const result = await signProtocolAPI(params)
|
||||
if (result.res === 1) {
|
||||
// 通知父页面更新附件签订状态
|
||||
emitSigned()
|
||||
uni.$u.toast('签订协议成功')
|
||||
// 延迟返回,确保事件已发送
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 300)
|
||||
} else {
|
||||
uni.$u.toast(res.resMsg || '签订协议失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('签订协议失败:', error)
|
||||
uni.$u.toast('签订协议失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
stopCountdown()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
@ -64,10 +329,78 @@ onLoad((options) => {
|
|||
}
|
||||
|
||||
.content-wrapper {
|
||||
flex: 1;
|
||||
// flex: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
// overflow: hidden;
|
||||
}
|
||||
|
||||
.file-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
padding: 12rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
padding: 24rpx 32rpx;
|
||||
background: #fff;
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.04);
|
||||
padding-bottom: calc(env(safe-area-inset-bottom, 16px) + 24rpx);
|
||||
}
|
||||
|
||||
.signature-modal {
|
||||
padding: 32rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.signature-modal-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.protocol-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
padding: 12rpx 0;
|
||||
}
|
||||
|
||||
.protocol-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.protocol-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.protocol-label {
|
||||
width: 160rpx;
|
||||
color: #666;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.protocol-value {
|
||||
flex: 1;
|
||||
padding: 16rpx 20rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 8rpx;
|
||||
color: #333;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.protocol-picker {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
|
|
@ -86,4 +419,3 @@ onLoad((options) => {
|
|||
transform: scale(0.95);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -139,13 +139,13 @@ const checkVideoSize = (size) => {
|
|||
*/
|
||||
const checkVideoDuration = (duration) => {
|
||||
// 限制最大60秒
|
||||
const maxDuration = 60
|
||||
const maxDuration = 20
|
||||
if (duration > maxDuration) {
|
||||
uni.$u.toast(`视频时长过长,请选择不超过${maxDuration}秒的视频`)
|
||||
return false
|
||||
}
|
||||
if (duration < 1) {
|
||||
uni.$u.toast('视频时长过短,请选择至少1秒的视频')
|
||||
if (duration < 2) {
|
||||
uni.$u.toast('视频时长过短,请选择至少2秒的视频')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -72,8 +72,9 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import NavBarModal from '@/components/NavBarModal/index.vue'
|
||||
import { getContentStyle } from '@/utils/safeArea'
|
||||
import { realNameHttp } from '@/utils/realNameHttp'
|
||||
import NavBarModal from '@/components/NavBarModal/index.vue'
|
||||
|
||||
const contentStyle = computed(() => {
|
||||
return getContentStyle({
|
||||
|
|
@ -190,26 +191,43 @@ const handleReRecord = () => {
|
|||
/**
|
||||
* 下一步 - 跳转到合同信息页面
|
||||
*/
|
||||
const handleNext = () => {
|
||||
const handleNext = async () => {
|
||||
if (!selectedTemplate.value) {
|
||||
uni.$u.toast('请选择合同模板')
|
||||
return
|
||||
}
|
||||
|
||||
// 传递模板信息和视频信息到合同信息页面
|
||||
const contractData = encodeURIComponent(
|
||||
JSON.stringify({
|
||||
template: selectedTemplate.value,
|
||||
templateValue: templateOptions.value.find(
|
||||
(item) => item.text === selectedTemplate.value,
|
||||
)?.value,
|
||||
videoInfo: videoInfo.value,
|
||||
}),
|
||||
)
|
||||
// 上传视频
|
||||
try {
|
||||
const uploadRes = await realNameHttp.uploadFile({
|
||||
url: '/user/uploadFile',
|
||||
filePath: videoInfo.value.videoUrl,
|
||||
name: 'file',
|
||||
formData: {
|
||||
photoType: 1,
|
||||
},
|
||||
})
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/work/contract/contractInfo/index?contractData=${contractData}`,
|
||||
})
|
||||
if (uploadRes.res === 1 && uploadRes.obj) {
|
||||
// 传递模板信息和视频信息到合同信息页面
|
||||
const contractData = encodeURIComponent(
|
||||
JSON.stringify({
|
||||
template: selectedTemplate.value,
|
||||
templateValue: templateOptions.value.find(
|
||||
(item) => item.text === selectedTemplate.value,
|
||||
)?.value,
|
||||
videoInfo: videoInfo.value,
|
||||
videoPath: uploadRes.obj,
|
||||
}),
|
||||
)
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/work/contract/contractInfo/index?contractData=${contractData}`,
|
||||
})
|
||||
} else {
|
||||
uni.$u.toast(uploadRes.resMsg || '上传失败')
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,3 +17,19 @@ export const getFileAddressAPI = (data) => {
|
|||
method: 'POST',
|
||||
})
|
||||
}
|
||||
|
||||
// 签订协议
|
||||
export const signProtocolAPI = (data) => {
|
||||
return realNameHttp({
|
||||
url: `/workPerson/contractCnsPdf?${initParams(data)}`,
|
||||
method: 'POST',
|
||||
})
|
||||
}
|
||||
|
||||
// 预览合同
|
||||
export const previewContractAPI = (data) => {
|
||||
return realNameHttp({
|
||||
url: `/workPerson/contractPdf?${initParams(data)}`,
|
||||
method: 'POST',
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export const createHttpClient = ({ baseURL, clientTag, tokenSelector }) => {
|
|||
}
|
||||
|
||||
options.timeout = 60000
|
||||
options[contentType] = ''
|
||||
options.header = {
|
||||
...options.header,
|
||||
}
|
||||
|
|
@ -219,7 +220,10 @@ export const createHttpClient = ({ baseURL, clientTag, tokenSelector }) => {
|
|||
url: '/pages/login/index',
|
||||
})
|
||||
reject(res)
|
||||
} else if (responseData.code === 500 || responseData.status === 'error') {
|
||||
} else if (
|
||||
responseData.code === 500 ||
|
||||
responseData.status === 'error'
|
||||
) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: responseData?.msg || '上传失败',
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ export default defineConfig({
|
|||
// 实名制系统代理规则
|
||||
'/bmw': {
|
||||
// target: 'http://192.168.0.234:1917/hnAma/',
|
||||
target: 'http://192.168.0.14:1917/hnAma/',
|
||||
// target: 'http://192.168.0.14:1917/hnAma/',
|
||||
target: 'http://192.168.0.38:18080/bnscloud/realnameapp/',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => {
|
||||
return path.replace(/\/bmw/, '')
|
||||
|
|
|
|||
Loading…
Reference in New Issue