feat: 同步签约宝手机端功能到签约宝页面

- sign页面与签约宝精简版对齐:查看合同入口、AI积分区块(合同审查/我的报告/积分充值)
- 新增pages/ai/{points,review,reports}三个页面(与签约宝精简版一致)
- pages.json注册AI页面路由
This commit is contained in:
2026-10-23 19:57:27 +08:00
parent e134209d16
commit c4ca8a87fa
1301 changed files with 314434 additions and 207 deletions
+275 -207
View File
@@ -3,24 +3,44 @@
<!-- 吸顶 -->
<u-sticky offset-top="0" customNavHeight="0">
<!-- 顶部 -->
<uititle title="金久信签约宝"></uititle>
<uititle title="金久信签约宝" :show-back="false"></uititle>
</u-sticky>
<view class="p-20">
<common-swiper page="qyb"/>
<view class="my-15 ">
<u-button shape="circle" type="primary" @click="toContract">查看合同</u-button>
</view>
<view class="layout layout-publish m-top-20 bg-white p-20">
<view class="pb-30">
<text class="text-main">您剩余金久信签约宝次数: {{ userInfo.qybNum }}次</text>
<view class="qyb-action-row flex justify-start items-center mt-20">
<view class="qyb-buy-action">
<u-button size="mini" type="primary" @click.native="dialogVisible = true"
<u-button size="max" type="primary" @click.native="dialogVisible = true"
:custom-style="{padding: 0, width: '100%'}">立即购买</u-button>
</view>
<view class="qyb-log-action ml-25">
<u-button size="mini" type="success" @click.native="openLog"
<u-button size="max" type="success" @click.native="openLog"
:custom-style="{padding: 0, width: '100%'}">明细</u-button>
</view>
</view>
</view>
<view class="pb-30">
<text class="text-main">您AI积分剩余: {{ pointsBalance }}积分</text>
<view class="qyb-action-row flex justify-start items-center mt-20" style="display: flex;justify-content:space-between;">
<view class="qyb-buy-action">
<u-button size="max" type="warning" @click="vk.navigateTo('/pages/ai/review')"
:custom-style="{padding: 0, width: '100%'}">合同审查</u-button>
</view>
<view class="qyb-buy-action">
<u-button size="max" type="success" @click="vk.navigateTo('/pages/ai/reports')"
:custom-style="{padding: 0, width: '100%'}">我的报告</u-button>
</view>
<view class="qyb-buy-action">
<u-button size="max" type="primary" @click="vk.navigateTo('/pages/ai/points')"
:custom-style="{padding: 0, width: '100%'}">积分充值</u-button>
</view>
</view>
</view>
<view class="text-main mt-10">温馨提示:尊敬的用户如果您的剩余金久信签约宝次数为0次,请先进行购买,再进行签约。</view>
<view class="card m-20">
<view class="flex justify-between items-center" @click="showSignIntro = !showSignIntro">
@@ -60,7 +80,6 @@
<u-radio activeColor="#a92924" name='2' label="合同签约"></u-radio>
</u-radio-group>
</view>
<!-- <view class="flex justify-start align-center m-top-30">-->
<!-- <view class="lb need m-right-20">交易方式:</view>-->
<!-- <u-radio-group-->
@@ -149,8 +168,8 @@
:previewImage="false"
>
<u-button type="primary" v-if="!formData.files">点击上传</u-button>
<p class="mt-30" @click="previewDoc" v-if="fileName">预览: {{ fileName }}</p>
</u-upload>
<p class="mt-30" @click="previewDoc">预览: {{ fileName }}</p>
</view>
</view>
<view class="group flex justify-start m-top-30 flex-col">
@@ -238,110 +257,106 @@
<u-popup :show="showLog" title="金久信签约宝明细" mode="center" @close="showLog = false" z-index="997" :overlayStyle="{
zIndex: 997
}">
<view style="max-height: 80vh" class="p-20 overflow-y rounded">
<u-tabs :list="[{name: '使用明细'}, {name: '付费明细'}]" :activeStyle="{fontWeight: 'bold', fontSize: '18px'}" :current="logIndex" @click="changeLogTab"></u-tabs>
<view class="flex flex-col justify-center items-center m-20" style="max-height: 60vh;overflow-y: scroll">
<template v-if="logIndex === 0">
<view class="p-15 border-bottom w-100p" v-for="(item, index) in signLog.rows" :key="index">
<view>日期: {{ item._add_time_str }}</view>
<view>项目: {{ parseInt(item.contract.signType) === 1 ? '单据签约' : '合同签约' }}</view>
<view>合同编号: {{ item.contract.transactionId }}</view>
<view>签约身份: {{
item.mineContract ? (item.mineContract.role === 'third' ? '第三方' : (item.mineContract.userId === item.userId ? '发起方' : '接收方')) : ''
}}
</view>
<view>
<view>扣除次数: 1</view>
<view v-if="item.remainNum !== undefined && item.remainNum != null">剩余次数: {{
item.remainNum
}}
</view>
</view>
<u-tabs :list="[{name: '使用明细'}, {name: '付费明细'}]" :activeStyle="{fontWeight: 'bold', fontSize: '18px'}" :current="logIndex" @click="changeLogTab"></u-tabs>
<view class="flex flex-col justify-center items-center m-20" style="max-height: 60vh;overflow-y: scroll">
<template v-if="logIndex === 0">
<view class="p-15 border-bottom w-100p" v-for="(item, index) in signLog.rows" :key="index">
<view>日期: {{ item._add_time_str }}</view>
<view>项目: {{ parseInt(item.contract.signType) === 1 ? '单据签约' : '合同签约' }}</view>
<view>合同编号: {{ item.contract.transactionId }}</view>
<view>签约身份: {{
item.mineContract ? (item.mineContract.role === 'third' ? '第三方' : (item.mineContract.userId === item.userId ? '发起方' : '接收方')) : ''
}}
</view>
<view class="text-center mt-20">
<el-pagination
:page-size="10"
@current-change="getSignLog"
background
layout="prev, pager, next"
:total="signLog.total">
</el-pagination>
<view>
<view>扣除次数: 1</view>
<view v-if="item.remainNum !== undefined && item.remainNum != null">剩余次数: {{ item.remainNum }}</view>
</view>
</template>
<template v-else>
<view class="p-15 border-bottom w-100p" v-for="(item, index) in signLog.rows" :key="index">
<view>日期: {{ item._add_time_str }}</view>
<view>项目: {{ item.type === 12 ? '合同司法调证' : '金久信签约宝续费' }}</view>
<view>流水号: {{ item.orderNo }}</view>
<view>缴费金额: {{ item.type === 12 ? item.amount : item.payAmount }}
</view>
<view>
<view>采购次数: {{ item.goods[0].goodsName }}</view>
<view v-if="item.remainNum">剩余次数: {{ item.remainNum }}</view>
</view>
<view class="flex justify-start items-center">
<text class="mr-15">操作:</text>
<el-button size="mini" @click="openInvoice(item._id)" type="primary"
v-if="!item.isInvoice">申请发票
</view>
<view class="text-center mt-20">
<el-pagination
:page-size="10"
@current-change="getSignLog"
background
layout="prev, pager, next"
:total="signLog.total">
</el-pagination>
</view>
</template>
<template v-else>
<view class="p-15 border-bottom w-100p" v-for="(item, index) in signLog.rows" :key="index">
<view>日期: {{ item._add_time_str }}</view>
<view>项目: {{ item.type === 12 ? '合同司法调证' : '金久信签约宝续费' }}</view>
<view>流水号: {{ item.orderNo }}</view>
<view>缴费金额: {{ item.type === 12 ? item.amount : item.payAmount }}
</view>
<view>
<view>采购次数: {{ item.goods[0].goodsName }}</view>
<view v-if="item.remainNum">剩余次数: {{ item.remainNum }}</view>
</view>
<view class="flex justify-start items-center">
<text class="mr-15">申请发票:</text>
<el-button size="mini" @click="openInvoice(item._id)" type="primary"
v-if="!item.isInvoice">申请发票
</el-button>
<template v-else>
<el-button size="mini" v-if="item.isInvoice === 1">开票中</el-button>
<el-button size="mini" type="success" v-if="item.isInvoice === 2"
@click.native="copyInvoice(item)">下载
</el-button>
<template v-else>
<el-button size="mini" v-if="item.isInvoice === 1">开票中</el-button>
<el-button size="mini" type="success" v-if="item.isInvoice === 2"
@click.native="copyInvoice(item)">下载
</el-button>
</template>
<el-button size="mini" style="margin-left: 10px"
v-if="item.type === 12 && item.certificate && item.certificate.paymentMethod === 'advance'"
@click.native="openPayVoucher(item.certificate)">查看支付凭证
</el-button>
</view>
</template>
<el-button size="mini" style="margin-left: 10px"
v-if="item.type === 12 && item.certificate && item.certificate.paymentMethod === 'advance'"
@click.native="openPayVoucher(item.certificate)">查看支付凭证
</el-button>
</view>
<view class="text-center mt-20">
<el-pagination
:page-size="10"
@current-change="getSignBuyLog"
background
layout="prev, pager, next"
:total="signLog.total">
</el-pagination>
</view>
</template>
</view>
</view>
<view class="text-center mt-20">
<el-pagination
:page-size="10"
@current-change="getSignBuyLog"
background
layout="prev, pager, next"
:total="signLog.total">
</el-pagination>
</view>
</template>
</view>
<el-dialog title="申请发票" :visible.sync="showInvoice" width="80%" top="10px" append-to-body>
<p>请输入开票信息</p>
<u-form ref="invoiceForm" :model="invoiceForm" label-width="140px" labelPosition="top">
<u-form-item label="名称" required>
<u-input v-model="invoiceForm.name" placeholder="请输入企业名称或个人姓名"/>
</u-form-item>
<u-form-item label="税号/证号" required>
<u-input v-model="invoiceForm.code" placeholder="请输入税号或身份证号"/>
</u-form-item>
<u-form-item label="手机号" required>
<u-input v-model="invoiceForm.phone" placeholder="请输入手机号"/>
</u-form-item>
<u-form-item label="电子邮箱" required>
<u-input v-model="invoiceForm.email" placeholder="请输入电子邮箱"/>
</u-form-item>
<u-form-item label="开户行">
<u-input v-model="invoiceForm.bankName" placeholder="请输入开户行"/>
</u-form-item>
<u-form-item label="银行账号">
<u-input v-model="invoiceForm.bankAccount" placeholder="请输入银行账号"/>
</u-form-item>
<u-form-item label="地址" required>
<u-input v-model="invoiceForm.address" placeholder="请输入地址"/>
</u-form-item>
<u-form-item label="单位/个人电话">
<u-input v-model="invoiceForm.tel" placeholder="请输入单位/个人电话"/>
</u-form-item>
</u-form>
<div class="mt-20 flex justify-center items-center">
<el-button type="primary" @click.native="doInvoice">确定</el-button>
<el-button plain @click.native="showInvoice = false">取消</el-button>
</div>
</el-dialog>
</u-popup>
<el-dialog title="申请发票" :visible.sync="showInvoice" width="80%" top="10px" append-to-body>
<p>请输入开票信息</p>
<u-form ref="invoiceForm" :model="invoiceForm" label-width="140px" labelPosition="top">
<u-form-item label="名称" required>
<u-input v-model="invoiceForm.name" placeholder="请输入企业名称或个人姓名"/>
</u-form-item>
<u-form-item label="税号/证号" required>
<u-input v-model="invoiceForm.code" placeholder="请输入税号或身份证号"/>
</u-form-item>
<u-form-item label="手机号" required>
<u-input v-model="invoiceForm.phone" placeholder="请输入手机号"/>
</u-form-item>
<u-form-item label="电子邮箱" required>
<u-input v-model="invoiceForm.email" placeholder="请输入电子邮箱"/>
</u-form-item>
<u-form-item label="开户行">
<u-input v-model="invoiceForm.bankName" placeholder="请输入开户行"/>
</u-form-item>
<u-form-item label="银行账号">
<u-input v-model="invoiceForm.bankAccount" placeholder="请输入银行账号"/>
</u-form-item>
<u-form-item label="地址" required>
<u-input v-model="invoiceForm.address" placeholder="请输入地址"/>
</u-form-item>
<u-form-item label="单位/个人电话">
<u-input v-model="invoiceForm.tel" placeholder="请输入单位/个人电话"/>
</u-form-item>
</u-form>
<div class="mt-20 flex justify-center items-center">
<el-button type="primary" @click.native="doInvoice">确定</el-button>
<el-button plain @click.native="showInvoice = false">取消</el-button>
</div>
</el-dialog>
<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"
@@ -349,6 +364,10 @@
</view>
</el-dialog>
<collapse></collapse>
<!-- AI 客服悬浮按钮 -->
<view class="ai-float-btn" @click="openAiChat">
<text class="ai-float-icon">🤖</text>
</view>
</view>
</template>
@@ -370,11 +389,11 @@ const SIGN_CONFIRM_CONTENT = `1、您确认,若全体各签约方未完成全
export default {
components: {
Collapse,
uititle,
cmsearch,
cmempty,
uPdf,
Collapse,
},
data() {
return {
@@ -383,12 +402,13 @@ export default {
userInfo: {
qybNum: 0
},
pointsBalance: 0,
pdfUrl: '',
show: false,
showPdf: false,
title: '',
name: '',
companyMobile: '',
pdfUrl: '',
showPdf: false,
formData: {
signType: '1',
tradeMethod: 0,
@@ -398,7 +418,7 @@ export default {
userType: null,
third: null,
signEndDate: null,
contractFile: '',
contractFile: null,
buyerSignKeyword: null,
sellerSignKeyword: null,
otherFile: null,
@@ -408,17 +428,6 @@ export default {
expireTime: '',
minDate: dayjs().valueOf(),
showContactPicker: false,
invoiceForm: {
id: null,
name: null,
code: null,
phone: null,
email: null,
bankName: null,
bankAccount: null,
address: null,
tel: null,
},
userTypeList: [
{
value: 1,
@@ -448,23 +457,33 @@ export default {
qybPackageList: [],
selectedQyb: null,
showSignIntro: false,
showSignFlow: false,
signFlowTitle: '',
showSignTips: false,
signTips: '',
showLog: false,
signIntro: null,
signFlow: null,
signEndDate: null,
invoiceForm: {
id: null,
name: null,
code: null,
phone: null,
email: null,
bankName: null,
bankAccount: null,
address: null,
tel: null,
},
signLog: {},
logIndex: 0,
showInvoice: false,
showLog: false,
showSignFlow: false,
signFlow: null,
signFlowTitle: '',
showSignTips: false,
signTips: '',
}
},
methods: {
dayjs,
afterRead({file}) {
console.log(file)
if (!file) return
// 仅PDF
if (file.type !== 'application/pdf') {
@@ -483,7 +502,7 @@ export default {
}
uniCloud.uploadFile({
filePath: file.url,
cloudPath: dayjs().format('YYYY/MM/DD') + '/' + md5(file.name + Date.now().toString()) + `-${file.name}`,
cloudPath: dayjs().format('YYYY/MM/DD') + '/' + md5(file.name + Date.now().toString()) + '.pdf',
success: ({fileID}) => {
console.log(fileID)
uniCloud.getTempFileURL({
@@ -577,40 +596,38 @@ export default {
} else {
uni.removeStorageSync(`signInfo`)
uni.removeStorageSync(`fileName`)
uni.showModal({
title: '提示',
content: SIGN_CONFIRM_CONTENT,
confirmButtonText: '知悉同意',
showCancel: false,
type: 'warning',
success: async (res) => {
vk.callFunction({
url: 'client/contract/kh/addSign',
title: '请求中...',
data: this.formData,
success: (data) => {
// console.log(data)
vk.navigateTo('/pages/public/contract?tab=1')
},
fail: (err) => {
vk.confirm(err.msg, '提示');
},
});
}
})
// 此处统一使用 SIGN_CONFIRM_CONTENT
// confirmButtonText: '知悉同意',
// showCancelButton: false,
// type: 'warning'
// })
uni.showModal({
title: '提示',
content: SIGN_CONFIRM_CONTENT,
showCancel: false,
confirmText: '知悉同意',
success: res => {
if (res.confirm) {
vk.callFunction({
url: 'client/contract/kh/addSign',
title: '请求中...',
data: this.formData,
success: (data) => {
// console.log(data)
vk.navigateTo('/pages/public/contract?tab=1')
},
fail: (err) => {
vk.confirm(err.msg, '提示');
},
});
}
}
})
}
console.log('complete:', pdfRes)
},
})
},
async doBuyQyb() {
const {data} = await vk.callFunction({
@@ -644,35 +661,21 @@ export default {
})
this.signIntro = res.content
},
async getSignFlow() {
const res = await vk.callFunction({
url: 'client/dict/pub/getDetail',
data: {
id: 'd77d384f686b9bd204bd4dc744cf75d0'
},
})
this.signFlow = res.content
this.signFlowTitle = res.name
},
async getSignTips() {
const res = await vk.callFunction({
url: 'client/dict/pub/getDetail',
data: {
id: '614937966875c18505570c357541c6e0'
},
})
this.signTips = res.content
},
previewDoc() {
// uni.setStorageSync(`signInfo_${this.formData.contractId}`, JSON.stringify(this.formData))
// uni.setStorageSync(`signInfo`, JSON.stringify(this.formData))
// uni.setStorageSync(`fileName`, this.fileName)
// this.pdfUrl = `/hybrid/html/web/viewer.html?file=${this.formData.contractFile}`
// window.location.href = this.formData.contractFile
// window.open(this.formData.contractFile)
if (Array.isArray(this.formData.contractFile)) {
this.pdfUrl = this.formData.contractFile[0]
} else
this.pdfUrl = this.formData.contractFile
this.showPdf = true
},
toContract() {
vk.navigateTo(`/pages/public/contract`)
},
openLog() {
this.showLog = true
this.changeLogTab({index: 0})
@@ -693,6 +696,25 @@ export default {
this.signLog.rows = this.signLog.rows.filter(item => !!item.contract)
this.$forceUpdate()
},
async getSignFlow() {
const res = await vk.callFunction({
url: 'client/dict/pub/getDetail',
data: {
id: 'd77d384f686b9bd204bd4dc744cf75d0'
},
})
this.signFlow = res.content
this.signFlowTitle = res.name
},
async getSignTips() {
const res = await vk.callFunction({
url: 'client/dict/pub/getDetail',
data: {
id: '614937966875c18505570c357541c6e0'
},
})
this.signTips = res.content
},
async getSignBuyLog(pageIndex) {
this.signLog = await vk.callFunction({
url: 'client/order/kh/getList',
@@ -703,7 +725,7 @@ export default {
pageIndex,
pageSize: 10,
signMethod: null,
type: [12, 13]
type: 13
}
})
this.$forceUpdate()
@@ -743,57 +765,84 @@ export default {
// })
window.location.href = row.invoiceUrl
},
getUserInfo() {
vk.userCenter.getCurrentUserInfo({
success: async (data) => {
this.userInfo = data.userInfo
if (this.userInfo.qybNum < 1) {
uni.showModal({
title: '提示',
content: '您的金久信签约宝次数为0, 是否立即购买?',
success: res => {
if (res.confirm) {
this.dialogVisible = true
}
}
})
}
if (!this.userInfo.fadadaConfirm) {
uni.showModal({
title: '提示',
content: '您尚未认证, 请前往认证',
showCancel: false,
success: res => {
if (res.confirm) {
uni.setStorageSync('user_tab', 0)
uni.navigateTo({url: '/pages/user/user'})
}
}
})
}
this.invoiceForm.phone = this.userInfo.mobile
this.invoiceForm.email = this.userInfo.businessCard ? this.userInfo.businessCard.email : ''
if (this.userInfo.accountType === 2) {
if (this.userInfo.fadadaConfirm && this.userInfo.company) {
this.invoiceForm.name = this.userInfo.company.companyName
this.invoiceForm.code = this.userInfo.company.organization
this.invoiceForm.address = this.userInfo.company.addressDetail
this.invoiceForm.tel = this.userInfo.company.mobile
this.invoiceForm.bankName = this.userInfo.company.companyBankName
this.invoiceForm.bankAccount = this.userInfo.company.companyAccount
}
} else {
this.invoiceForm.name = this.userInfo.person.personName
this.invoiceForm.code = this.userInfo.person.idCard
this.invoiceForm.address = this.userInfo.person.address
this.invoiceForm.tel = this.userInfo.mobile
}
}
})
},
closePdf() {
this.pdfUrl = ''
this.showPdf = false
},
openPayVoucher(row) {
// window.open(row.payVoucher)
uni.previewImage({
uni.previewImage( {
urls: [row.payVoucher],
})
},
openAiChat() {
uni.navigateTo({ url: '/pages/ai/chat' })
},
goTo(url) {
uni.navigateTo({ url })
},
async getPointsBalance() {
const res = await vk.callFunction({ url: 'client/points/kh/getBalance' })
if (res.code === 0 && res.data) {
this.pointsBalance = res.data.balance || 0
}
},
},
onShow() {
this.getQybPackage()
this.getSignIntro()
this.getSignFlow()
this.getSignTips()
vk.userCenter.getCurrentUserInfo({
success: async (data) => {
this.userInfo = data.userInfo
if (!this.userInfo.fadadaConfirm) {
uni.showModal({
title: '提示',
content: '您尚未认证, 请前往认证',
showCancel: false,
success: res => {
if (res.confirm) {
uni.setStorageSync('user_tab', 0)
uni.switchTab({url: '/pages/user/user'})
}
}
})
}
this.invoiceForm.phone = this.userInfo.mobile
this.invoiceForm.email = this.userInfo.businessCard ? this.userInfo.businessCard.email : ''
if (this.userInfo.accountType === 2) {
if (this.userInfo.fadadaConfirm && this.userInfo.company) {
this.invoiceForm.name = this.userInfo.company.companyName
this.invoiceForm.code = this.userInfo.company.organization
this.invoiceForm.address = this.userInfo.company.addressDetail
this.invoiceForm.tel = this.userInfo.company.mobile
this.invoiceForm.bankName = this.userInfo.company.companyBankName
this.invoiceForm.bankAccount = this.userInfo.company.companyAccount
}
} else {
this.invoiceForm.name = this.userInfo.person.personName
this.invoiceForm.code = this.userInfo.person.idCard
this.invoiceForm.address = this.userInfo.person.address
this.invoiceForm.tel = this.userInfo.mobile
}
}
})
this.getUserInfo()
this.getPointsBalance()
if (uni.getStorageSync('signInfo')) {
this.formData = JSON.parse(uni.getStorageSync('signInfo'))
if (this.formData.signEndDate) this.signEndDate = dayjs(this.formData.signEndDate).format('YYYY-MM-DD')
@@ -831,4 +880,23 @@ page {
.qyb-log-action {
flex: 1;
}
.ai-float-btn {
position: fixed;
bottom: 195px;
right: 30rpx;
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background: linear-gradient(135deg, #a92924, #d4574e);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 20rpx rgba(169, 41, 36, 0.4);
z-index: 999;
}
.ai-float-icon {
font-size: 48rpx;
}
</style>