3179 lines
107 KiB
Vue
3179 lines
107 KiB
Vue
<template>
|
||
<view class="">
|
||
<top-bar></top-bar>
|
||
<common-nav></common-nav>
|
||
<view class="w-1200">
|
||
<common-swiper page="qyb" v-if="isSign" />
|
||
<el-row :gutter="20">
|
||
<el-col :span="6">
|
||
<el-menu @select="menuSelect" :default-active="menuIndex" class="el-menu-vertical-demo">
|
||
<el-menu-item index="0">
|
||
<i class="el-icon-edit"></i>
|
||
<span slot="title">待创建</span>
|
||
</el-menu-item>
|
||
<el-menu-item index="1">
|
||
<i class="el-icon-warning-outline"></i>
|
||
<span slot="title">待签署</span>
|
||
</el-menu-item>
|
||
<el-menu-item index="2">
|
||
<i class="el-icon-circle-check"></i>
|
||
<span slot="title">已完成</span>
|
||
</el-menu-item>
|
||
<!-- <el-menu-item index="-1">-->
|
||
<!-- <i class="el-icon-delete"></i>-->
|
||
<!-- <span slot="title">已作废</span>-->
|
||
<!-- </el-menu-item>-->
|
||
</el-menu>
|
||
<view class="mt-20 bg-white flex justify-start items-center sign pointer" @click="selectSign">
|
||
<view class="icon">
|
||
<agreement theme="outline" size="18" fill="#999" strokeLinecap="square" />
|
||
</view>
|
||
<text class="ml-20" :class="[isSign ? 'text-main' : '']">金久信签约宝</text>
|
||
</view>
|
||
<view class="bg-white flex justify-start items-center sign pointer" @click="selectSignIntro">
|
||
<view class="icon">
|
||
<agreement theme="outline" size="18" fill="#999" strokeLinecap="square" />
|
||
</view>
|
||
<text class="ml-20" :class="[isSignIntro ? 'text-main' : '']">金久信签约宝简介</text>
|
||
</view>
|
||
<view class="bg-white flex justify-start items-center sign pointer" @click="selectSignLog">
|
||
<view class="icon">
|
||
<agreement theme="outline" size="18" fill="#999" strokeLinecap="square" />
|
||
</view>
|
||
<text class="ml-20" :class="[isSignLog ? 'text-main' : '']">金久信签约宝消费明细</text>
|
||
</view>
|
||
<view class="bg-white flex justify-start items-center sign pointer" @click="selectSignTips">
|
||
<view class="icon">
|
||
<agreement theme="outline" size="18" fill="#999" strokeLinecap="square" />
|
||
</view>
|
||
<text class="ml-20" :class="[isSignTips ? 'text-main' : '']">金久信签约宝重要提示</text>
|
||
</view>
|
||
<view class="bg-white flex justify-start items-center sign pointer" @click="selectSignFlow">
|
||
<view class="icon">
|
||
<agreement theme="outline" size="18" fill="#999" strokeLinecap="square" />
|
||
</view>
|
||
<text class="ml-20" :class="[isSignFlow ? 'text-main' : '']">{{ signFlowTitle }}</text>
|
||
</view>
|
||
<view class="bg-white flex justify-start items-center sign pointer" @click="changeAuthInfo">
|
||
<view class="icon">
|
||
<agreement theme="outline" size="18" fill="#999" strokeLinecap="square" />
|
||
</view>
|
||
<text class="ml-20">认证信息变更</text>
|
||
</view>
|
||
<div class="menu-group-title">AI 智能服务</div>
|
||
<el-menu @select="menuSelect" :default-active="menuIndex" class="el-menu-vertical-demo">
|
||
<el-menu-item index="3">
|
||
<span class="icon">🤖</span> AI 客服
|
||
</el-menu-item>
|
||
<el-menu-item index="4">
|
||
<span class="icon">📋</span> 合同审查
|
||
</el-menu-item>
|
||
<el-menu-item index="5">
|
||
<span class="icon">📈</span> 我的报告
|
||
</el-menu-item>
|
||
<el-menu-item index="6">
|
||
<span class="icon">💎</span> 积分管理
|
||
</el-menu-item>
|
||
</el-menu>
|
||
</el-col>
|
||
|
||
<el-col :span="18">
|
||
<view class="flex flex-col justify-start items-start" v-if="isSignLog">
|
||
<view class="trade-method">
|
||
<view @click="getSignLog(1)" class="trade-method-item"
|
||
:class="{active: signLogType === 'log'}">使用明细
|
||
</view>
|
||
<view @click="getSignBuyLog(1)" class="trade-method-item"
|
||
:class="{active: signLogType === 'payLog'}">付费明细
|
||
</view>
|
||
</view>
|
||
<template v-if="signLogType === 'log'">
|
||
<el-table :data="signLog.rows" style="width: 100%">
|
||
<el-table-column prop="_add_time_str" label="日期" />
|
||
<el-table-column label="项目">
|
||
<template slot-scope="scope">
|
||
{{ parseInt(scope.row.contract.signType) === 1 ? '单据签约' : '合同签约' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="contract.transactionId" label="合同编号" />
|
||
<el-table-column label="签约身份">
|
||
<template slot-scope="scope">
|
||
{{
|
||
scope.row.mineContract ? (scope.row.mineContract.role === 'third' ? '第三方' : (scope.row.mineContract.userId === scope.row.userId ? '发起方' : '接收方')) : ''
|
||
}}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="扣除次数">
|
||
<template slot-scope="scope">
|
||
<p>扣除次数:1次</p>
|
||
<p v-if="scope.row.remainNum !== undefined && scope.row.remainNum !== null">
|
||
剩余次数:{{ scope.row.remainNum }}次</p>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<view class="flex justify-center items-center w-100p mt-20">
|
||
<el-pagination @current-change="getSignLog" background layout="prev, pager, next"
|
||
:total="signLog.total"></el-pagination>
|
||
</view>
|
||
</template>
|
||
<template v-else>
|
||
<el-table :data="signBuyLog.rows" style="width: 100%">
|
||
<el-table-column prop="_add_time_str" label="日期" />
|
||
<el-table-column label="项目">
|
||
<template slot-scope="scope">
|
||
{{ scope.row.type === 12 ? '合同司法调证' : '金久信签约宝续费' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="orderNo" label="流水号" />
|
||
<el-table-column label="缴费金额">
|
||
<template slot-scope="scope">
|
||
{{ scope.row.type === 12 ? scope.row.amount : scope.row.payAmount }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="采购次数">
|
||
<template slot-scope="scope">
|
||
<p>{{ scope.row.goods[0].goodsName }}</p>
|
||
<p v-if="scope.row.remainNum">剩余{{ scope.row.remainNum }}次</p>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作">
|
||
<template slot-scope="scope">
|
||
<view class="flex flex-col justify-start items-start">
|
||
<el-button size="mini" @click="openInvoice(scope.row._id)" type="primary"
|
||
v-if="!scope.row.isInvoice">申请发票
|
||
</el-button>
|
||
<template v-else>
|
||
<el-button size="mini" v-if="scope.row.isInvoice === 1">开票中</el-button>
|
||
<el-button size="mini" type="success" v-if="scope.row.isInvoice === 2"
|
||
@click.native="copyInvoice(scope.row)">下载
|
||
</el-button>
|
||
</template>
|
||
<el-button size="mini" style="margin-left: 0; margin-top: 10px"
|
||
v-if="scope.row.type === 12 && scope.row.certificate && scope.row.certificate.paymentMethod === 'advance'"
|
||
@click.native="openPayVoucher(scope.row.certificate)">查看支付凭证
|
||
</el-button>
|
||
</view>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<view class="flex justify-center items-center w-100p mt-20">
|
||
<el-pagination @current-change="getSignBuyLog" background layout="prev, pager, next"
|
||
:total="signBuyLog.total"></el-pagination>
|
||
</view>
|
||
</template>
|
||
</view>
|
||
<view class="contract-list"
|
||
v-if="!isSign && !isSignIntro && !isSignLog && !isSignFlow && !isSignTips">
|
||
<view class="trade-method">
|
||
<!-- <view @click="changeTradeMethod(null)" class="trade-method-item"-->
|
||
<!-- :class="{active: (formData.tradeMethod === null && formData.type === null)}">全部-->
|
||
<!-- </view>-->
|
||
<!-- <view @click="changeTradeMethod(0)" class="trade-method-item"-->
|
||
<!-- :class="{active: formData.tradeMethod === 0}">自主交易-->
|
||
<!-- </view>-->
|
||
<!-- <view @click="changeTradeMethod(1)" class="trade-method-item"-->
|
||
<!-- :class="{active: formData.tradeMethod === 1}">金久信公正-->
|
||
<!-- </view>-->
|
||
<view @click="changeType('sign')" class="trade-method-item"
|
||
:class="{active: formData.type === 'sign'}">金久信签约宝
|
||
</view>
|
||
</view>
|
||
<view class="flex justify-start items-center" v-if="menuIndex === '2'">
|
||
<view style="flex: 0 0 200px">
|
||
<el-radio-group v-model="signTypeFilter" fill="#A92924" @change="query(1)">
|
||
<el-radio-button label="2">合同签约</el-radio-button>
|
||
<el-radio-button label="1">单据签约</el-radio-button>
|
||
</el-radio-group>
|
||
</view>
|
||
<el-input class="ml-10" @keyup.enter.native="query(1)" clearable placeholder="输入签约方关键字搜索"
|
||
v-model="keyword">
|
||
<i slot="prefix" class="el-input__icon el-icon-search"></i>
|
||
<el-button @click="query(1)" type="primary" slot="append" style="border-radius: 0">搜索
|
||
</el-button>
|
||
</el-input>
|
||
</view>
|
||
<view v-for="(item,index) in tableData.rows" class="contract-box" :key="index">
|
||
<view class="contract-left">
|
||
<view class="contract-left-top">
|
||
<el-tag size="mini" v-if="item.contract.type === 'enquiry'" type="danger"
|
||
effect="plain">询价单
|
||
</el-tag>
|
||
<el-tag size="mini" v-else-if="item.contract.type === 'process'" type="warning"
|
||
effect="plain">加工单
|
||
</el-tag>
|
||
<el-tag size="mini" v-else-if="item.contract.type === 'sign'" type="success"
|
||
effect="plain">金久信签约宝
|
||
</el-tag>
|
||
<el-tag size="mini" v-else-if="item.contract.type === 'old'" type="primary"
|
||
effect="plain">旧货交易
|
||
</el-tag>
|
||
<el-tag size="mini" v-else-if="item.contract.type === 'self-support'" type="primary"
|
||
effect="plain">自营询价
|
||
</el-tag>
|
||
<template v-if="item.sheet">
|
||
<view class="ml-20">
|
||
<el-tag size="mini" v-if="item.sheet.tradeMethod === 0" type="primary">自主交易
|
||
</el-tag>
|
||
<el-tag size="mini" v-if="item.sheet.tradeMethod === 1" type="success">金久信公证
|
||
</el-tag>
|
||
</view>
|
||
<view class="ml-20">
|
||
<el-tag size="mini" effect="plain" v-if="item.sheet.signMethod === 0"
|
||
type="success">标准合同
|
||
</el-tag>
|
||
<el-tag size="mini" effect="plain" v-if="item.sheet.signMethod === 2"
|
||
type="primary">双方协商合同
|
||
</el-tag>
|
||
<el-tag size="mini" effect="plain" v-if="item.sheet.signMethod === 1"
|
||
type="warning">线下合同
|
||
</el-tag>
|
||
</view>
|
||
</template>
|
||
<el-tag size="mini" v-if="item.contract.tradeMethod === 1" type="success">金久信公证
|
||
</el-tag>
|
||
<view class="contract-product" v-if="item.contract.status > 0">
|
||
{{ item.contract.docTitle }}
|
||
</view>
|
||
<template v-else>
|
||
<text class="contract-product" v-if="item.sheet">
|
||
{{ item.sheet.title }}
|
||
</text>
|
||
</template>
|
||
<view class="contract-status">
|
||
<template v-if="item.contract.status == 1">
|
||
<view class="contract-status-tag"
|
||
v-for="(party, pIndex) in item.contract.partyList" :key="pIndex">
|
||
<el-tag size="mini"
|
||
v-if="party.userId !== vk.getVuex('$user.userInfo._id') && !party.sign && party.role !== 'third'"
|
||
type="warning" effect="plain">待交易方签署
|
||
</el-tag>
|
||
<el-tag size="mini"
|
||
v-if="party.userId !== vk.getVuex('$user.userInfo._id') && !party.sign && party.role === 'third'"
|
||
type="warning" effect="plain">待第三方签署
|
||
</el-tag>
|
||
<el-tag size="mini"
|
||
v-if="party.userId === vk.getVuex('$user.userInfo._id') && !party.sign"
|
||
type="primary" effect="plain">待我签署
|
||
</el-tag>
|
||
</view>
|
||
</template>
|
||
<!-- <text v-if="item.contract.status == -1">已作废</text>-->
|
||
</view>
|
||
<view v-if="item.order" class="text-main">
|
||
(订单状态:{{
|
||
item.order.orderStatus === 4 ? '已完成' : ([5, 6].includes(item.order.orderStatus) ? '订单终止' : '执行中')
|
||
}})
|
||
</view>
|
||
</view>
|
||
<view class="contract-left-center">
|
||
<view>
|
||
合同编码:{{ item.contract.transactionId }}
|
||
</view>
|
||
<view v-if="item.sheet && item.contract.type === 'old'">
|
||
交易编码:{{
|
||
item.contract.type === 'enquiry' ? item.sheet.enquiryNo : item.sheet.orderNo
|
||
}}
|
||
</view>
|
||
<view>
|
||
创建时间:{{ item.contract._add_time_str }}
|
||
</view>
|
||
<view v-if="item.contract.signEndDate">
|
||
签订截止时间:{{
|
||
dayjs(item.contract.signEndDate).format('YYYY-MM-DD HH:mm:ss')
|
||
}}
|
||
</view>
|
||
<view
|
||
v-if="item.contract.type === 'sign' && item.contract.payUser && item.contract.payUser.user">
|
||
费用承担方:{{
|
||
item.contract.payUser.user.accountType === 2 ?
|
||
item.contract.payUser.user.company.companyName : item.contract.payUser.user.person.personName
|
||
}}
|
||
</view>
|
||
<template v-if="parseInt(item.contract.status) > 0">
|
||
<view
|
||
v-for="(party, partyIndex) in item.contract.partyList.filter(p => p.role !== 'third')"
|
||
:key="`p_${partyIndex}`">
|
||
<template v-if="party.userId === item.contract.userId">
|
||
发起方签订完成时间:{{
|
||
party.signedAt ? dayjs(party.signedAt).format('YYYY-MM-DD HH:mm:ss') : ''
|
||
}}
|
||
</template>
|
||
<template v-if="party.userId !== item.contract.userId">
|
||
接收方签订完成时间:{{
|
||
party.signedAt ? dayjs(party.signedAt).format('YYYY-MM-DD HH:mm:ss') : ''
|
||
}}
|
||
</template>
|
||
<template v-if="party.role === 'third'">
|
||
第三方签订完成时间:{{
|
||
party.signedAt ? dayjs(party.signedAt).format('YYYY-MM-DD HH:mm:ss') : ''
|
||
}}
|
||
</template>
|
||
</view>
|
||
<view
|
||
v-for="(party, partyIndex) in item.contract.partyList.filter(p => p.role === 'third')"
|
||
:key="`t_${partyIndex}`">
|
||
第{{ numToChForThird[partyIndex] }}方签订完成时间:{{
|
||
party.signedAt ? dayjs(party.signedAt).format('YYYY-MM-DD HH:mm:ss') : ''
|
||
}}
|
||
</view>
|
||
</template>
|
||
</view>
|
||
<view class="contract-left-bottom">
|
||
<el-button size="mini" plain @click.native="showParty(item)">
|
||
查看交易方
|
||
</el-button>
|
||
<el-button
|
||
v-if="item.contract.status === 0 && item.contract.userId === vk.getVuex('$user.userInfo._id')"
|
||
@click="createContract(item.contract)" size="mini" type="primary">创建合同
|
||
</el-button>
|
||
<el-button v-if="item.contract.isMine" @click="delContract(item.contract)"
|
||
size="mini" type="text">
|
||
删除合同
|
||
</el-button>
|
||
<template v-if="parseInt(item.contract.status) === 1">
|
||
<el-button @click="extSign(item.sign, item.contract)" size="mini" type="primary"
|
||
:disabled="item.sign">
|
||
{{ item.sign ? '已签署' : '签署文件' }}
|
||
</el-button>
|
||
</template>
|
||
<template v-if="parseInt(item.contract.status) === 2">
|
||
<el-button @click="viewContract(item.contract)" size="mini" plain>
|
||
{{ parseInt(item.contract.signType) === 1 ? '查看单据' : '查看合同' }}
|
||
</el-button>
|
||
<el-button @click="downloadContract(item.contract.transactionId)" type="primary"
|
||
size="mini" plain>
|
||
{{ parseInt(item.contract.signType) === 1 ? '下载单据' : '下载合同' }}
|
||
</el-button>
|
||
<el-button @click="payDownloadContract(item.contract)" size="mini" plain>
|
||
{{
|
||
parseInt(item.contract.signType) === 1 ? '下载单据(付费司法调证)' : '下载合同(付费司法调证)'
|
||
}}
|
||
</el-button>
|
||
</template>
|
||
<el-button
|
||
v-if="item.order && parseInt(item.contract.status) === 2 && ((item.sheet && item.sheet.tradeMethod === 1) || item.contract.tradeMethod === 1)"
|
||
@click="navTo(`/pages/order/order?menuIndex=${item.role === 'buyer' ? 1 : 0}&keyword=${item.order.orderNo}`)"
|
||
size="mini"
|
||
:type="(item.order && item.order.orderStatus > 4) ? 'info' :'success'">
|
||
{{
|
||
(item.order && item.order.orderStatus === 4) ? '订单已结束' : ([5, 6].includes(item.order.orderStatus) ? '订单已终止' : (item.role === 'buyer' ? '去付款' : '去请款'))
|
||
}}
|
||
</el-button>
|
||
<el-button v-if="item.contract.otherFile && item.contract.otherFile.length > 0"
|
||
@click="openDownload(item.contract.otherFile)" plain size="mini"
|
||
type="info">查看附件
|
||
</el-button>
|
||
</view>
|
||
</view>
|
||
<view class="contract-right">
|
||
<!-- <view class="">
|
||
{{item._add_time_str}}到期,到期将无法签署合同
|
||
</view> -->
|
||
<view class="">
|
||
<el-button @click.native="changeContract(item.contract)"
|
||
v-if="((item.sheet && item.sheet.signMethod === 2) || item.contract.type === 'sign' || item.contract.type === 'self-support') && item.contract.status === 1
|
||
&& item.contract.userId === vk.getVuex('$user.userInfo._id') && item.contract.signCount === 0" size="mini"
|
||
type="primary">修改合同内容
|
||
</el-button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="paginatio">
|
||
<el-pagination @current-change="query" background layout="prev, pager, next"
|
||
:total="tableData.total"></el-pagination>
|
||
</view>
|
||
</view>
|
||
<view v-if="isSign" class="bg-white p-20">
|
||
<view class="flex justify-start items-center pb-30">
|
||
<text class="text-main mr-35">您剩余金久信签约宝次数: {{ userInfo.qybNum }}次</text>
|
||
<el-button size="mini" type="primary" @click.native="dialogVisible = true">立即购买
|
||
</el-button>
|
||
</view>
|
||
<view class="text-main mt-10">温馨提示:尊敬的用户如果您的剩余金久信签约宝次数为0次,请先进行购买,再进行签约。</view>
|
||
<vk-data-form ref="form2" v-model="form2.data" :action="form2.props.action"
|
||
:columns="form2.props.columns" :rules="form2.props.rules" :form-type="form2.props.formType"
|
||
:loading.sync="form2.props.loading" :show-cancel="false" :auto-close="true"
|
||
label-width="140px" @success="onFormSuccess">
|
||
<template v-slot:download>
|
||
<el-button size="mini" type="text" @click="downloadContractTemplate">点击下载
|
||
</el-button>
|
||
</template>
|
||
<template v-slot:contractFile>
|
||
<vk-data-upload v-model="form2.data.contractFile" :cloudPathRemoveChinese="false"
|
||
list-type="text" accept=".pdf" :on-success="uploadSuccess" ref="uploader"
|
||
:before-upload="beforeUpload" :show-file-list="false">
|
||
</vk-data-upload>
|
||
<view class="text-main text-24 my-15 pointer"
|
||
@click="openPreview(form2.data.contractFile)" v-if="form2.data.contractFile">预览:
|
||
{{ form2.data.contractFile[form2.data.contractFile.length - 1].split('-').pop() }}
|
||
</view>
|
||
<view class="tips text-24 text-gray">
|
||
仅支持PDF文件,如有其他附件或多个单据签约请合并成一个PDF文档上传。且仅支持纵向文档上传,横向文档显示不完整。电子签署文件中需添加以下内容以增强双方约束力和文件法律效力:签约双方均自愿通过以“电子签约”形式进行签约,电子签名和加盖电子印章双方均予以认可具有法律效力。
|
||
</view>
|
||
</template>
|
||
<template v-slot:signEndDate>
|
||
<el-date-picker :popper-class="'currentDatePickerClass'"
|
||
v-model="form2.data.signEndDate" type="datetime" placeholder="选择签署截止日期">
|
||
</el-date-picker>
|
||
</template>
|
||
<template v-slot:tips>
|
||
<view class="tips text-24 text-gray">注意事项(电子签名不适用文件):</view>
|
||
<view class="tips text-24 text-gray">1.婚姻、继承、收养等人身关系的;</view>
|
||
<view class="tips text-24 text-gray">2.涉及停止供水、供热、供气等公用事业服务的;</view>
|
||
<view class="tips text-24 text-gray">3.其他法律、行政法规规定的不适用电子文书的其他形式;</view>
|
||
</template>
|
||
<template v-slot:third>
|
||
<div v-for="(item, index) in form2.data.thirdList" :key="index"
|
||
class="flex justify-start items-center mb-10 ">
|
||
<span>第{{ numToChForThird[index] }}方</span>
|
||
<div class="mx-15">
|
||
<el-input v-model="form2.data.thirdList[index].phone" placeholder="请输入第三方手机号" />
|
||
</div>
|
||
<div>
|
||
<el-input v-model="form2.data.thirdList[index].signKeyword"
|
||
placeholder="请输入第三方签章关键字" />
|
||
</div>
|
||
<div class="flex justify-start items-center">
|
||
<i class="el-icon-circle-plus pointer text-main text-50 ml-35"
|
||
@click="addThird" />
|
||
<i class="el-icon-remove pointer text-50 ml-35" @click="removeThird(index)" />
|
||
<i title="上移" class="el-icon-arrow-up pointer text-50 ml-35" v-if="index > 0"
|
||
@click="thirdMoveUp(index)" />
|
||
<i title="下移" class="el-icon-arrow-down pointer text-50 ml-35"
|
||
@click="thirdMoveDown(index)"
|
||
v-if="index < form2.data.thirdList.length - 1" />
|
||
</div>
|
||
</div>
|
||
<p class="text-25 text-gray mt-20">如无第三方签约可忽略,签章关键字使用方法同下。</p>
|
||
<p class="text-25 text-gray mt-20">手机号请连续输入,请勿添加空格和断开输入,系统无法准确识别。</p>
|
||
</template>
|
||
<template v-slot:footer="{ loading }">
|
||
<view style="text-align: center;">
|
||
<el-button :loading="loading" type="primary"
|
||
@click="form2.props.action">提交</el-button>
|
||
</view>
|
||
</template>
|
||
</vk-data-form>
|
||
</view>
|
||
<view v-if="isSignIntro" class="bg-white p-20">
|
||
<div v-html="signIntro"></div>
|
||
</view>
|
||
<view v-if="isSignFlow" class="bg-white p-20">
|
||
<div v-html="signFlow"></div>
|
||
</view>
|
||
<view v-if="isSignTips" class="bg-white p-20">
|
||
<div v-html="signTips"></div>
|
||
</view>
|
||
<!-- AI 客服(嵌入式) -->
|
||
<view v-if="menuIndex === '3'" class="ai-chat-embed">
|
||
<view class="chat-header-bar">
|
||
<text class="chat-title">🤖 金久信签约宝 AI 智能客服</text>
|
||
</view>
|
||
<scroll-view scroll-y class="chat-msg-list" :scroll-into-view="chatScrollId"
|
||
scroll-with-animation>
|
||
<view v-if="chatMessages.length === 0" class="chat-row left" id="chat-welcome">
|
||
<view class="chat-avatar">🤖</view>
|
||
<view class="chat-bubble">
|
||
您好!我是金久信签约宝AI智能客服,可以帮您解答金久信签约宝使用过程中注册、操作、适用场景等问题。请问有什么可以帮您?
|
||
<view class="quick-btns">
|
||
<view class="quick-btn" @click="chatQuickSend('注册准备资料事项')">注册准备资料事项</view>
|
||
<view class="quick-btn" @click="chatQuickSend('关键字无法识别')">关键字无法识别</view>
|
||
<view class="quick-btn" @click="chatQuickSend('金久信签约宝法律效力')">金久信签约宝法律效力</view>
|
||
<view class="quick-btn" @click="chatQuickSend('电子签约不适用场景')">电子签约不适用场景</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view v-for="(msg, i) in chatMessages" :key="i" :id="'chat-msg' + i"
|
||
:class="['chat-row', msg.role === 'user' ? 'right' : 'left']">
|
||
<view v-if="msg.role !== 'user'" class="chat-avatar">🤖</view>
|
||
<view class="chat-bubble" v-html="formatChatContent(msg.content)"></view>
|
||
</view>
|
||
<view v-if="chatLoading" class="chat-row left" id="chat-loading">
|
||
<view class="chat-avatar">🤖</view>
|
||
<view class="chat-bubble typing">思考中...</view>
|
||
</view>
|
||
</scroll-view>
|
||
<view class="chat-input-bar">
|
||
<input v-model="chatInput" @confirm="chatSend" placeholder="输入您的问题..." />
|
||
<view class="chat-send-btn" @click="chatSend">发送</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 合同审查 -->
|
||
<view v-if="menuIndex === '4'" class="review-container">
|
||
<view class="review-title">📋 AI 合同审查</view>
|
||
<!-- 积分提示 -->
|
||
<view class="points-notice">
|
||
<span class="pn-icon">💎</span>
|
||
<view class="pn-text">
|
||
<view class="pn-title">合同审查消耗积分</view>
|
||
<view class="pn-desc">根据文件大小自动计算,每 100KB 约 200 积分。审查后余额需保留至少 200 积分。</view>
|
||
</view>
|
||
<view class="pn-balance">余额:{{ pointsBalance }} 分</view>
|
||
</view>
|
||
<!-- 免责声明 -->
|
||
<view class="disclaimer-notice">
|
||
<span class="dn-icon">⚠️</span>
|
||
<view class="dn-text">请确保合同内容清晰可见。如因合同不清晰导致分析失败,平台不承担后果,积分正常消耗。</view>
|
||
</view>
|
||
<!-- 上传区 -->
|
||
<view v-if="!reviewFile" class="upload-area" @click="selectReviewFile">
|
||
<view class="upload-icon">📤</view>
|
||
<view class="upload-title">上传合同文件</view>
|
||
<view class="upload-desc">仅支持 PDF / Word(.pdf .doc .docx),最大 20MB</view>
|
||
<view class="upload-btn">选择文件上传</view>
|
||
</view>
|
||
<!-- 上传中提示 -->
|
||
<view v-if="reviewUploading" style="text-align:center;padding:30px 0;">
|
||
<i class="el-icon-loading" style="font-size:32px;color:#a92924;"></i>
|
||
<p style="font-size:14px;color:#666;margin-top:8px;">正在上传文件到审查系统...</p>
|
||
</view>
|
||
<!-- 文件信息 + 审查选项 -->
|
||
<view v-if="reviewFile && !reviewResult && !reviewUploading">
|
||
<view class="file-info">
|
||
<span class="fi-icon">📄</span>
|
||
<view>
|
||
<view class="fi-name">{{ reviewFile.name }}</view>
|
||
<view class="fi-size">{{ formatFileSize(reviewFile.size) }}</view>
|
||
</view>
|
||
<span class="fi-remove" @click="reviewFile = null">✕</span>
|
||
</view>
|
||
<view class="review-opts">
|
||
<view :class="['review-opt', reviewMode === 0 && 'selected']" @click="reviewMode = 0">
|
||
<view class="opt-icon">🔍</view>
|
||
<view class="opt-title">标准审查</view>
|
||
<view class="opt-desc">风险条款识别 · 合规性检查</view>
|
||
<!-- <view class="opt-points">{{ reviewPoints }} 积分</view> -->
|
||
</view>
|
||
<!-- <view :class="['review-opt', reviewMode === 1 && 'selected']" @click="reviewMode = 1">
|
||
<view class="opt-icon">📊</view>
|
||
<view class="opt-title">深度分析</view>
|
||
<view class="opt-desc">标准审查 + 估价参考 + 修改建议</view>
|
||
<view class="opt-points">{{ reviewPoints * 2 }} 积分</view>
|
||
</view> -->
|
||
</view>
|
||
<view style="text-align:center;margin-top:16px;">
|
||
<el-button type="primary" :loading="reviewLoading"
|
||
:disabled="pointsBalance < reviewPoints + 200"
|
||
@click="startReview">
|
||
{{ reviewLoading ? '审查中...' : '开始审查' }}
|
||
</el-button>
|
||
<p v-if="pointsBalance < reviewPoints + 200" style="font-size:12px;color:#f56c6c;margin-top:6px;">
|
||
⚠️ 积分不足,当前余额 {{ pointsBalance }},需要 {{ reviewPoints }} 积分且审查后余额需保留至少 200 积分。
|
||
<span style="color:#a92924;cursor:pointer;text-decoration:underline;" @click="goRecharge">去充值</span>
|
||
</p>
|
||
</view>
|
||
</view>
|
||
<!-- 审查中 -->
|
||
<view v-if="reviewLoading" style="text-align:center;padding:40px 0;">
|
||
<div style="font-size:48px;margin-bottom:16px;">🤖</div>
|
||
<div style="font-size:16px;font-weight:600;margin-bottom:8px;">AI 正在分析合同...</div>
|
||
<el-progress :percentage="reviewProgress" :stroke-width="8"
|
||
style="max-width:400px;margin:0 auto;"></el-progress>
|
||
</view>
|
||
<!-- 审查结果 -->
|
||
<view v-if="reviewResult">
|
||
<view class="result-header">
|
||
<h3>📋 {{ reviewStreaming ? 'AI 正在分析...' : '审查结果' }} — {{ reviewFile.name }}</h3>
|
||
<span class="result-status">{{ reviewStreaming ? '⏳ 分析中...' : '✅ 审查完成' }}</span>
|
||
</view>
|
||
<view class="result-body" :style="reviewStreaming ? 'max-height:400px;overflow-y:auto;' : ''">
|
||
<div v-html="reviewResult"></div>
|
||
<div v-if="reviewStreaming" style="margin-top:8px;">
|
||
<span style="display:inline-block;width:8px;height:8px;background:#a92924;border-radius:50%;animation:pulse 1s infinite;"></span>
|
||
<span style="font-size:12px;color:#999;margin-left:6px;">AI 正在输出分析结果...</span>
|
||
</div>
|
||
</view>
|
||
<view class="result-footer">
|
||
<el-button v-if="reviewHtmlUrl" type="success" size="small" @click="openUrl(reviewHtmlUrl)">📄 查看完整 HTML 报告</el-button>
|
||
<el-button size="small" @click="resetReview">重新审查</el-button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 我的报告 -->
|
||
<view v-if="menuIndex === '5'" class="review-container">
|
||
<view class="review-title">
|
||
📈 我的报告
|
||
<el-button size="mini" type="primary" style="margin-left:12px;" @click="queryReports()">🔍 查询报告</el-button>
|
||
</view>
|
||
<view v-if="reportList.length === 0" style="text-align:center;padding:60px 0;color:#999;">
|
||
<div style="font-size:48px;margin-bottom:12px;">📋</div>
|
||
暂无审查报告,点击上方「查询报告」从服务器加载
|
||
</view>
|
||
<view v-for="(r, i) in reportList" :key="i" class="report-item">
|
||
<view class="report-info">
|
||
<view class="report-name">📄 {{ r.fileName }}</view>
|
||
<view class="report-time">{{ r.time }} · 消耗 {{ Math.abs(r.amount) }} 积分</view>
|
||
</view>
|
||
<el-tag v-if="r.status === 'uploaded'" size="mini" type="info">待审查</el-tag>
|
||
<el-tag v-else-if="r.status === 'reviewing'" size="mini" type="warning">排队中</el-tag>
|
||
<el-tag v-else-if="r.status === 'processing'" size="mini" type="warning">审查中</el-tag>
|
||
<el-tag v-else-if="r.status === 'completed'" size="mini" type="success">已完成</el-tag>
|
||
<el-tag v-else-if="r.status === 'failed'" size="mini" type="danger">失败</el-tag>
|
||
<el-button v-if="r.htmlUrl" size="mini" type="text" @click="openUrl(r.htmlUrl)">报告下载</el-button>
|
||
<el-button v-if="r.docxUrl" size="mini" type="text" @click="openUrl(r.docxUrl)">下载 Word</el-button>
|
||
<el-button v-if="r.fileUrl" size="mini" type="text" @click="previewFile(r)">查看上传文件</el-button>
|
||
<el-button v-if="r.status === 'completed' && r.result" size="mini" type="text" @click="viewReport(r)">在线查看报告</el-button>
|
||
<el-button v-if="r.status === 'uploaded'" size="mini" type="primary" plain :loading="r._submitting" @click="reReview(r)">重新审查</el-button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 积分管理 -->
|
||
<view v-if="menuIndex === '6'" class="review-container">
|
||
<view class="review-title">💎 积分管理</view>
|
||
<view class="points-summary">
|
||
<view class="ps-card">
|
||
<view class="ps-label">可用积分</view>
|
||
<view class="ps-value">{{ pointsBalance }}</view>
|
||
</view>
|
||
<view class="ps-card secondary">
|
||
<view class="ps-label">累计获得</view>
|
||
<view class="ps-value">{{ pointsTotalEarned }}</view>
|
||
</view>
|
||
</view>
|
||
<!-- 积分充值 -->
|
||
<view class="review-title" style="font-size:14px;margin-top:16px;">💰 积分充值</view>
|
||
<view class="recharge-grid">
|
||
<view v-for="(pkg, i) in rechargePackages" :key="i"
|
||
:class="['recharge-card', selectedRecharge === i && 'selected']"
|
||
@click="selectedRecharge = i">
|
||
<view class="rc-points">{{ pkg.points }}</view>
|
||
<view class="rc-price">{{ pkg.price }}</view>
|
||
<view v-if="pkg.bonus" class="rc-bonus">{{ pkg.bonus }}</view>
|
||
</view>
|
||
</view>
|
||
<view style="text-align:center;margin-bottom:24px;">
|
||
<el-button type="primary" size="medium" @click="doRecharge">
|
||
立即充值 {{ rechargePackages[selectedRecharge].price }}
|
||
({{ rechargePackages[selectedRecharge].points }} 积分)
|
||
</el-button>
|
||
<p style="font-size:11px;color:#999;margin-top:6px;">💡 积分长期有效,可用于合同审查等功能</p>
|
||
</view>
|
||
<!-- 使用说明 -->
|
||
<view class="review-title" style="font-size:14px;margin-top:16px;">📖 积分使用说明</view>
|
||
<view style="font-size:13px;color:#666;line-height:2;margin-bottom:20px;">
|
||
<p>• <strong>合同审查</strong>:每 100KB 约 200 积分(根据文件大小自动计算)</p>
|
||
<p>• <strong>AI 客服</strong>:免费 — 签约宝专属问答</p>
|
||
<p>• 新用户注册自动赠送 500 体验积分</p>
|
||
<p>• 积分长期有效,不过期</p>
|
||
</view>
|
||
<view class="review-title" style="font-size:14px;">
|
||
📜 积分明细
|
||
<el-button size="mini" type="text" style="margin-left:12px;" @click="getPointsLog()">🔍 查看</el-button>
|
||
</view>
|
||
<el-table :data="pointsLogList" style="width:100%">
|
||
<el-table-column prop="_add_time_str" label="时间" width="180"></el-table-column>
|
||
<el-table-column label="类型" width="80">
|
||
<template slot-scope="scope">
|
||
<el-tag size="mini" :type="scope.row.type === 'earn' ? 'success' : 'danger'">
|
||
{{ scope.row.type === 'earn' ? '获得' : '消耗' }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="reason" label="原因"></el-table-column>
|
||
<el-table-column label="变动" width="100">
|
||
<template slot-scope="scope">
|
||
<span :style="{color: scope.row.type === 'earn' ? '#67c23a' : '#f56c6c'}">
|
||
{{ scope.row.type === 'earn' ? '+' : '' }}{{ scope.row.amount }}
|
||
</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="balance_after" label="余额" width="100"></el-table-column>
|
||
</el-table>
|
||
</view>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
</view>
|
||
<common-footer></common-footer>
|
||
|
||
<el-dialog :visible.sync="form1.props.show" width="70%">
|
||
<vk-data-form ref="form1" v-model="form1.data" :action="form1.props.action" :columns="form1.props.columns"
|
||
:rules="form1.props.rules" :form-type="form1.props.formType" :loading.sync="form1.props.loading"
|
||
:show-cancel="false" :auto-close="true" label-width="140px" @success="onFormSuccess">
|
||
<template v-slot:contractFile>
|
||
<vk-data-upload v-model="form1.data.contractFile" :cloudPathRemoveChinese="false" list-type="text"
|
||
accept=".pdf" :on-success="uploadSuccess1" ref="uploader1" :before-upload="beforeUpload"
|
||
:show-file-list="false">
|
||
</vk-data-upload>
|
||
<view class="text-main text-24 my-15 pointer" @click="openPreview(form1.data.contractFile)"
|
||
v-if="form1.data.contractFile">预览:
|
||
{{ form1.data.contractFile[form1.data.contractFile.length - 1].split('-').pop() }}
|
||
</view>
|
||
<view class="tips text-24 text-gray">
|
||
仅支持PDF文件,如有其他附件或多个单据签约请合并成一个PDF文档上传。且仅支持纵向文档上传,横向文档显示不完整。电子签署文件中需添加以下内容以增强双方约束力和文件法律效力:签约双方均自愿通过以“电子签约”形式进行签约,电子签名和加盖电子印章双方均予以认可具有法律效力。
|
||
</view>
|
||
</template>
|
||
<template v-slot:download>
|
||
<el-button size="mini" type="text" @click="downloadContractTemplate">点击下载</el-button>
|
||
</template>
|
||
<template v-slot:third>
|
||
<div v-for="(item, index) in form1.data.thirdList" :key="index"
|
||
class="flex justify-start items-center mb-10 ">
|
||
<span>第{{ numToChForThird[index] }}方</span>
|
||
<div class="mx-15">
|
||
<el-input v-model="form1.data.thirdList[index].phone" placeholder="请输入第三方手机号" />
|
||
</div>
|
||
<div>
|
||
<el-input v-model="form1.data.thirdList[index].signKeyword" placeholder="请输入第三方签章关键字" />
|
||
</div>
|
||
<div class="flex justify-start items-center">
|
||
<i class="el-icon-circle-plus pointer text-main text-50 ml-35" @click="addThird1" />
|
||
<i class="el-icon-remove pointer text-50 ml-35" @click="removeThird1(index)" />
|
||
<i title="上移" class="el-icon-arrow-up pointer text-50 ml-35" v-if="index > 0"
|
||
@click="thirdMoveUp1(index)" />
|
||
<i title="下移" class="el-icon-arrow-down pointer text-50 ml-35"
|
||
@click="thirdMoveDown1(index)" v-if="index < form1.data.thirdList.length - 1" />
|
||
</div>
|
||
</div>
|
||
<p class="text-25 text-gray mt-20">如无第三方签约可忽略,签章关键字使用方法同下。</p>
|
||
<p class="text-25 text-gray mt-20">手机号请连续输入,请勿添加空格和断开输入,系统无法准确识别。</p>
|
||
</template>
|
||
<template v-slot:footer="{ loading }">
|
||
<view style="text-align: center;">
|
||
<el-button :loading="loading" type="primary" @click="form1.props.action">提交</el-button>
|
||
</view>
|
||
</template>
|
||
</vk-data-form>
|
||
</el-dialog>
|
||
|
||
<el-dialog title="金久信签约宝次数" :visible.sync="dialogVisible" width="50%">
|
||
<p>请选择购买次数</p>
|
||
<div class="flex justify-center items-center">
|
||
<div class="p-20 rounded m-20 shadow flex flex-col justify-center items-center flex-1 pointer"
|
||
:class="[selectedQyb === item._id ? 'buy-quote-mode-active' : '']"
|
||
v-for="(item, index) in qybPackageList" :key="index" @click="selectedQyb = item._id">
|
||
<p>{{ item.amount }}元</p>
|
||
<p>可获得{{ item.getNum }}次</p>
|
||
</div>
|
||
</div>
|
||
<div class="mt-20 text-main font-bold">
|
||
剩余金久信签约宝次数:{{ userInfo.qybNum }}次
|
||
</div>
|
||
<div class="mt-20 flex justify-center items-center">
|
||
<el-button type="primary" :disabled="!selectedQyb" @click.native="doBuyQyb">确定</el-button>
|
||
<el-button plain @click.native="dialogVisible = false">取消</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<el-dialog title="申请发票" :visible.sync="showInvoice" width="50%">
|
||
<p>请输入开票信息</p>
|
||
<el-form ref="form" label-width="140px">
|
||
<el-form-item label="名称" required>
|
||
<el-input v-model="invoiceForm.name" placeholder="请输入企业名称或个人姓名"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="税号/证号" required>
|
||
<el-input v-model="invoiceForm.code" placeholder="请输入税号或身份证号"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="手机号" required>
|
||
<el-input v-model="invoiceForm.phone" placeholder="请输入手机号"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="电子邮箱" required>
|
||
<el-input v-model="invoiceForm.email" placeholder="请输入电子邮箱"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="开户行">
|
||
<el-input v-model="invoiceForm.bankName" placeholder="请输入开户行"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="银行账号">
|
||
<el-input v-model="invoiceForm.bankAccount" placeholder="请输入银行账号"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="地址" required>
|
||
<el-input v-model="invoiceForm.address" placeholder="请输入地址"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="单位/个人电话">
|
||
<el-input v-model="invoiceForm.tel" placeholder="请输入单位/个人电话"></el-input>
|
||
</el-form-item>
|
||
|
||
</el-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="showPayVoucher" width="50%">
|
||
<el-image :src="payVoucher" :preview-src-list="[payVoucher]" />
|
||
</el-dialog>
|
||
<!-- 右侧悬浮工具栏 -->
|
||
<div class="float-toolbar">
|
||
<div class="tool-item ai-hot">
|
||
<a rel="nofollow" href="javascript:;" @click="$refs.aiChat.toggle()">
|
||
<div class="item">
|
||
<i class="wag-icon"><img src="@/static/images/cl4.png"></i>
|
||
</div>
|
||
<span class="tool-label">AI客服</span>
|
||
</a>
|
||
</div>
|
||
<!-- <div class="tool-item ai-hot">
|
||
<span class="tool-icon">📑</span>
|
||
<span class="tool-label">合同审查</span>
|
||
</div> -->
|
||
</div>
|
||
<ai-chat ref="aiChat" />
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
Agreement
|
||
} from "@icon-park/vue";
|
||
import dayjs from "dayjs";
|
||
import {
|
||
Loading
|
||
} from "element-ui";
|
||
|
||
|
||
export default {
|
||
components: {
|
||
Agreement
|
||
},
|
||
data() {
|
||
return {
|
||
numToChForThird: ['三', '四', '五', '六', '七', '八', '九', '十'],
|
||
userInfo: {
|
||
qybNum: 0
|
||
},
|
||
dialogVisible: false,
|
||
qybPackageList: [],
|
||
selectedQyb: null,
|
||
contractTemplate: '',
|
||
pageIndex: 1,
|
||
menuIndex: '0',
|
||
isSign: false,
|
||
isSignIntro: false,
|
||
signIntro: '',
|
||
signFlow: '',
|
||
signFlowTitle: '',
|
||
signType: '1',
|
||
signTypeFilter: '2',
|
||
keyword: '',
|
||
formData: {
|
||
status: 0,
|
||
tradeMethod: null,
|
||
type: 'sign'
|
||
},
|
||
showPayVoucher: false,
|
||
payVoucher: '',
|
||
isSignLog: false,
|
||
isSignFlow: false,
|
||
signLog: {},
|
||
signBuyLog: {},
|
||
signLogType: 'log',
|
||
showInvoice: false,
|
||
isSignTips: false,
|
||
signTips: '',
|
||
// AI 客服数据
|
||
chatMessages: [],
|
||
chatInput: '',
|
||
chatLoading: false,
|
||
chatConversationId: null,
|
||
chatScrollId: '',
|
||
// 积分数据
|
||
pointsBalance: 0,
|
||
pointsTotalEarned: 0,
|
||
pointsLogList: [],
|
||
// 合同审查数据
|
||
reviewFile: null,
|
||
reviewFileId: null,
|
||
reviewRecordId: null,
|
||
reviewHtmlUrl: '',
|
||
reviewPdfUrl: '',
|
||
reviewContractInfo: {},
|
||
reviewStreaming: false,
|
||
reviewUploading: false,
|
||
reviewMode: 0,
|
||
reviewPoints: 100,
|
||
reviewLoading: false,
|
||
reviewProgress: 0,
|
||
reviewResult: null,
|
||
reviewConversationId: null,
|
||
// 积分充值
|
||
selectedRecharge: 1,
|
||
rechargePackages: [{
|
||
points: 10000,
|
||
price: '20元',
|
||
bonus: ''
|
||
},
|
||
{
|
||
points: 50000,
|
||
price: '100元',
|
||
bonus: ''
|
||
},
|
||
{
|
||
points: 100000,
|
||
price: '200元',
|
||
bonus: ''
|
||
},
|
||
{
|
||
points: 450000,
|
||
price: '900元',
|
||
bonus: ''
|
||
},
|
||
{
|
||
points: 1000000,
|
||
price: '2000元',
|
||
bonus: ''
|
||
},
|
||
{
|
||
points: 2000000,
|
||
price: '4000元',
|
||
bonus: ''
|
||
}
|
||
],
|
||
// 我的报告
|
||
reportList: [],
|
||
invoiceForm: {
|
||
id: null,
|
||
name: null,
|
||
code: null,
|
||
phone: null,
|
||
email: null,
|
||
bankName: null,
|
||
bankAccount: null,
|
||
address: null,
|
||
tel: null,
|
||
},
|
||
tableData: {},
|
||
// 表单相关开始-----------------------------------------------------------
|
||
form1: {
|
||
// 表单请求数据,此处可以设置默认值
|
||
data: {
|
||
contractId: '',
|
||
signEndDate: '',
|
||
title: '',
|
||
contractFile: '',
|
||
buyerSignKeyword: '',
|
||
sellerSignKeyword: '',
|
||
otherFile: '',
|
||
thirdList: [{
|
||
phone: '',
|
||
signKeyword: ''
|
||
}],
|
||
},
|
||
// 表单属性
|
||
props: {
|
||
// 表单请求地址
|
||
action: async () => {
|
||
const thirdKeywords = []
|
||
for (let i = 0; i < this.form1.data.thirdList.length; i++) {
|
||
const item = this.form1.data.thirdList[i]
|
||
if (item.phone && item.signKeyword) {
|
||
thirdKeywords.push(item.signKeyword)
|
||
}
|
||
}
|
||
uni.showLoading({
|
||
title: '查验中'
|
||
})
|
||
let file = this.form1.data.contractFile[this.form1.data.contractFile.length - 1]
|
||
if (Array.isArray(file)) file = file[file.length - 1]
|
||
vk.request({
|
||
method: "POST",
|
||
url: 'https://hcsc.gxwebsoft.com/api/read-pdf',
|
||
header: {
|
||
"content-type": "application/json; charset=utf-8",
|
||
},
|
||
data: {
|
||
file,
|
||
buyerSignKeyword: this.form1.data.buyerSignKeyword,
|
||
sellerSignKeyword: this.form1.data.sellerSignKeyword,
|
||
thirdKeywords
|
||
},
|
||
success: async (pdfRes) => {
|
||
|
||
},
|
||
fail: (pdfRes) => {
|
||
uni.hideLoading()
|
||
this.$message.error(pdfRes.content.msg)
|
||
},
|
||
complete: async (pdfRes) => {
|
||
if (pdfRes.code !== 200) this.$message.error(pdfRes.content.msg)
|
||
else {
|
||
uni.hideLoading()
|
||
this.form1.props.loading = true
|
||
this.form1.data.thirdList = this.form1.data.thirdList.filter(
|
||
item => item.phone && item.signKeyword)
|
||
if (this.form1.data.thirdList.length > 0) {
|
||
for (let i = 0; i < this.form1.data.thirdList.length; i++) {
|
||
const item = this.form1.data.thirdList[i]
|
||
if (item.phone === '') {
|
||
vk.toast('请填写手机号')
|
||
this.form1.props.loading = false
|
||
return false
|
||
}
|
||
if (item.signKeyword === '') {
|
||
vk.toast('请填写签章关键字')
|
||
this.form1.props.loading = false
|
||
return false
|
||
}
|
||
if (item.phone === this.form1.data.phone) {
|
||
vk.toast('第三方手机号不能与接收方手机号相同')
|
||
this.form1.props.loading = false
|
||
return false
|
||
}
|
||
}
|
||
}
|
||
if (!this.form1.data.signEndDate) {
|
||
vk.toast('请选择签署截止时间')
|
||
this.form1.props.loading = false
|
||
return false
|
||
}
|
||
if (dayjs(this.form1.data.signEndDate).isBefore(dayjs())) {
|
||
vk.toast('签署截止日期不能早于当前时间')
|
||
this.form1.props.loading = false
|
||
return false
|
||
}
|
||
const res = await this.$confirm(
|
||
`1、您确认,若全体各签约方未完成全部签约流程,本签约文件不发生法律效力,系统无法对该签约文件予以保存及提供司法调证服务;<br>2、您确认,当前签约的实名主体名称、手机号码与注册认证阶段提交的信息未发生变更;若发生变更,必须及时进行重新认证后方可继续签约,避免产生严重法律风险与经济损失;<br>3、您确认,本次签约为本系统实名认证主体操作,如若不是实名认证主体操作,授权委托人需本系统进行授权认证后签约;<br>4、您确认,您与其他签约方均系在平等、自愿的基础上完成本次签约;<br>5、你确认,此次签约内容符合《中华人民共和国电子签名法》《民法典》生效保护范畴;<br>6、如因法律法规、监管部门政策更新等原因,系统提示您重新进行实名认证、证书续期等重要提示时,请您必须在签约前完成,否则造成任何损失,平台不承担任何责任;以上签约条件您均已经知悉并确认,否则您有权拒绝本次签约。`,
|
||
'提示', {
|
||
confirmButtonText: '知悉同意',
|
||
showCancelButton: false,
|
||
type: 'warning',
|
||
dangerouslyUseHTMLString: true
|
||
})
|
||
if (res === 'confirm') {
|
||
await vk.callFunction({
|
||
url: 'client/contract/kh/launch',
|
||
data: this.form1.data
|
||
})
|
||
vk.redirectTo({
|
||
url: '/pages/contract/index?tab=1'
|
||
})
|
||
}
|
||
}
|
||
uni.hideLoading()
|
||
},
|
||
})
|
||
|
||
},
|
||
// 表单字段显示规则
|
||
columns: [{
|
||
key: "title",
|
||
title: "标题",
|
||
type: "text"
|
||
},
|
||
{
|
||
key: "third",
|
||
title: "",
|
||
type: "html",
|
||
tips: "如无第三方签约可忽略,签章关键字使用方法同下。"
|
||
},
|
||
{
|
||
key: "signEndDate",
|
||
title: "签署截止日期",
|
||
type: "date",
|
||
tips: '发起方填写签署截止日期和时间即为各签约方共同认可的签署时间,请酌情填写仔细阅读签署文件。',
|
||
dateType: "datetime",
|
||
pickerOptions: {
|
||
disabledDate: (date) => {
|
||
const now = new Date()
|
||
return now > date
|
||
}
|
||
}
|
||
},
|
||
{
|
||
key: "contractFile",
|
||
title: "签署文档",
|
||
type: "file",
|
||
limit: 1,
|
||
accept: ".pdf",
|
||
tips: "仅支持PDF文件,如有其他附件或多个单据签约请合并成一个PDF文档上传。且仅支持纵向文档上传,横向文档显示不完整。"
|
||
},
|
||
// {
|
||
// key: "download",
|
||
// title: "合同模板下载",
|
||
// type: "html"
|
||
// },
|
||
{
|
||
key: "buyerSignKeyword",
|
||
title: "签约发起方关键字",
|
||
type: "text",
|
||
tips: '先点击上传文档后,在线预览文档,点击ctrl+f,复制粘贴或输入预盖章部位关键字至弹框内,再次点击ctrl+f进行核验确认位置和内容,核实无误后可返回签署页面输入测验关键字后进行签署(如同一关键字文档多次使用无误后可考虑省略检测流程)。系统将按此关键字进行签章位置的定位,将电子章盖在这个关键字上面。凡出现关键字的地方均会盖上指定用户的电子章,建议关键字在合同中保持唯一。',
|
||
placeholder: '发起方盖章关键字',
|
||
},
|
||
{
|
||
key: "sellerSignKeyword",
|
||
title: "签约接收方关键字",
|
||
type: "text",
|
||
placeholder: '签约接收方关键字',
|
||
tips: '先点击上传文档后,在线预览文档,点击ctrl+f,复制粘贴或输入预盖章部位关键字至弹框内,再次点击ctrl+f进行核验确认位置和内容,核实无误后可返回签署页面输入测验关键字后进行签署(如同一关键字文档多次使用无误后可考虑省略检测流程)。系统将按此关键字进行签章位置的定位,将电子章盖在这个关键字上面。凡出现关键字的地方均会盖上指定用户的电子章,建议关键字在合同中保持唯一。'
|
||
},
|
||
// {
|
||
// key: "otherFile",
|
||
// title: "附件",
|
||
// type: "file",
|
||
// accept: ".pdf",
|
||
// tips: "仅支持PDF文件(附件将按顺序附在合同末尾一并签章)",
|
||
// },
|
||
],
|
||
// 表单验证规则
|
||
rules: {
|
||
title: [{
|
||
required: true,
|
||
message: "标题不能为空",
|
||
trigger: "change"
|
||
}, ],
|
||
signEndDate: [{
|
||
required: true,
|
||
message: "签署截止日期不能为空",
|
||
trigger: "change"
|
||
}],
|
||
buyerSignKeyword: [{
|
||
required: true,
|
||
message: "签约发起方关键字不能为空",
|
||
trigger: "change"
|
||
}],
|
||
sellerSignKeyword: [{
|
||
required: true,
|
||
message: "签约接收方关键字不能为空",
|
||
trigger: "change"
|
||
}],
|
||
contractFile: [{
|
||
required: true,
|
||
message: "签署文档不能为空",
|
||
trigger: "change"
|
||
}, ]
|
||
},
|
||
// add 代表添加 update 代表修改
|
||
formType: "",
|
||
// 表单是否在请求中
|
||
loading: false,
|
||
// 是否显示表单1 的弹窗
|
||
show: false,
|
||
}
|
||
},
|
||
form2: {
|
||
// 表单请求数据,此处可以设置默认值
|
||
data: {
|
||
contractId: '',
|
||
signEndDate: '',
|
||
title: '',
|
||
contractFile: '',
|
||
buyerSignKeyword: '',
|
||
sellerSignKeyword: '',
|
||
signType: '1',
|
||
tradeMethod: 0,
|
||
payBy: 0,
|
||
otherFile: [],
|
||
thirdList: [{
|
||
phone: '',
|
||
signKeyword: ''
|
||
}],
|
||
},
|
||
// 表单属性
|
||
props: {
|
||
// 表单请求地址
|
||
action: async () => {
|
||
const thirdKeywords = []
|
||
for (let i = 0; i < this.form2.data.thirdList.length; i++) {
|
||
const item = this.form2.data.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.form2.data.contractFile[this.form2.data.contractFile.length -
|
||
1],
|
||
buyerSignKeyword: this.form2.data.buyerSignKeyword,
|
||
sellerSignKeyword: this.form2.data.sellerSignKeyword,
|
||
thirdKeywords
|
||
},
|
||
success: async (pdfRes) => {
|
||
|
||
},
|
||
fail: (pdfRes) => {
|
||
uni.hideLoading()
|
||
this.$message.error(pdfRes.content.msg)
|
||
},
|
||
complete: async (pdfRes) => {
|
||
if (pdfRes.code !== 200) this.$message.error(pdfRes.content.msg)
|
||
else {
|
||
uni.hideLoading()
|
||
this.form2.props.loading = true
|
||
this.form2.data.thirdList = this.form2.data.thirdList.filter(
|
||
item => item.phone && item.signKeyword)
|
||
if (this.form2.data.thirdList.length > 0) {
|
||
for (let i = 0; i < this.form2.data.thirdList.length; i++) {
|
||
const item = this.form2.data.thirdList[i]
|
||
if (item.phone === '') {
|
||
vk.toast('请填写手机号')
|
||
this.form2.props.loading = false
|
||
return false
|
||
}
|
||
if (item.signKeyword === '') {
|
||
vk.toast('请填写签章关键字')
|
||
this.form2.props.loading = false
|
||
return false
|
||
}
|
||
if (item.phone === this.form2.data.phone) {
|
||
vk.toast('第三方手机号不能与接收方手机号相同')
|
||
this.form2.props.loading = false
|
||
return false
|
||
}
|
||
}
|
||
}
|
||
if (!this.form2.data.signEndDate) {
|
||
vk.toast('请选择签署截止时间')
|
||
this.form2.props.loading = false
|
||
return false
|
||
}
|
||
if (this.form2.data.payBy === undefined || this.form2.data
|
||
.payBy === '' || this.form2.data.payBy === null) {
|
||
vk.toast('请选择费用承担方')
|
||
this.form2.props.loading = false
|
||
return false
|
||
}
|
||
if (dayjs(this.form2.data.signEndDate).isBefore(dayjs())) {
|
||
vk.toast('签署截止日期不能早于当前时间')
|
||
this.form2.props.loading = false
|
||
return false
|
||
}
|
||
const res = await this.$confirm(
|
||
`1、您确认,若全体各签约方未完成全部签约流程,本签约文件不发生法律效力,系统无法对该签约文件予以保存及提供司法调证服务;<br>2、您确认,当前签约的实名主体名称、手机号码与注册认证阶段提交的信息未发生变更;若发生变更,必须及时进行重新认证后方可继续签约,避免产生严重法律风险与经济损失;<br>3、您确认,本次签约为本系统实名认证主体操作,如若不是实名认证主体操作,授权委托人需本系统进行授权认证后签约;<br>4、您确认,您与其他签约方均系在平等、自愿的基础上完成本次签约;<br>5、你确认,此次签约内容符合《中华人民共和国电子签名法》《民法典》生效保护范畴;<br>6、如因法律法规、监管部门政策更新等原因,系统提示您重新进行实名认证、证书续期等重要提示时,请您必须在签约前完成,否则造成任何损失,平台不承担任何责任;以上签约条件您均已经知悉并确认,否则您有权拒绝本次签约。`,
|
||
'提示', {
|
||
confirmButtonText: '知悉同意',
|
||
showCancelButton: false,
|
||
type: 'warning',
|
||
dangerouslyUseHTMLString: true
|
||
})
|
||
if (res === 'confirm') {
|
||
await vk.callFunction({
|
||
url: 'client/contract/kh/addSign',
|
||
data: this.form2.data
|
||
})
|
||
vk.redirectTo({
|
||
url: '/pages/contract/index?tab=1'
|
||
})
|
||
}
|
||
}
|
||
console.log('complete:', pdfRes)
|
||
uni.hideLoading()
|
||
},
|
||
})
|
||
|
||
},
|
||
// 表单字段显示规则
|
||
columns: [{
|
||
key: "signType",
|
||
title: "",
|
||
type: "radio",
|
||
data: [{
|
||
value: '1',
|
||
label: "单据签约"
|
||
},
|
||
{
|
||
value: '2',
|
||
label: "合同签约"
|
||
}
|
||
]
|
||
},
|
||
// {
|
||
// key: "tradeMethod",
|
||
// title: "交易方式",
|
||
// type: "radio",
|
||
// data: [
|
||
// {
|
||
// value: 0,
|
||
// label: "自主交易"
|
||
// },
|
||
// {
|
||
// value: 1,
|
||
// label: "金久信公正"
|
||
// }
|
||
// ],
|
||
// showRule: (data) => {
|
||
// return data.signType === '2';
|
||
// }
|
||
// },
|
||
{
|
||
key: "payBy",
|
||
title: "签约费用",
|
||
type: "radio",
|
||
data: [{
|
||
value: 0,
|
||
label: "发起方承担"
|
||
},
|
||
{
|
||
value: 1,
|
||
label: "接收方承担"
|
||
},
|
||
{
|
||
value: 2,
|
||
label: "第三方承担"
|
||
},
|
||
]
|
||
},
|
||
// {
|
||
// key: "payByThird",
|
||
// title: "承担签约费用的第三方手机号",
|
||
// type: "text",
|
||
// showRule: (data) => {
|
||
// return data.payBy === 2;
|
||
// }
|
||
// },
|
||
{
|
||
key: "title",
|
||
title: "标题",
|
||
placeholder: `请输入自定义合同或单据名称`,
|
||
type: "text"
|
||
},
|
||
{
|
||
key: "phone",
|
||
title: "对方手机号",
|
||
type: "text",
|
||
placeholder: "请输入对方金久信实名认证手机号码",
|
||
tips: '手机号请连续输入,请勿添加空格和断开输入,系统无法准确识别。'
|
||
},
|
||
{
|
||
key: "userType",
|
||
title: "对方身份",
|
||
type: "select",
|
||
data: [{
|
||
value: 1,
|
||
label: "买家"
|
||
},
|
||
{
|
||
value: 2,
|
||
label: "卖家"
|
||
},
|
||
{
|
||
value: 3,
|
||
label: "甲方"
|
||
},
|
||
{
|
||
value: 4,
|
||
label: "乙方"
|
||
},
|
||
{
|
||
value: 5,
|
||
label: "授权签约方"
|
||
},
|
||
]
|
||
},
|
||
{
|
||
key: "signEndDate",
|
||
title: "签署截止时间",
|
||
tips: '发起方填写签署截止日期和时间即为各签约方共同认可的签署时间,请酌情填写仔细阅读签署文件。',
|
||
pickerOptions: {
|
||
disabledDate: (date) => {
|
||
const now = new Date()
|
||
return now > date
|
||
}
|
||
}
|
||
},
|
||
{
|
||
key: "contractFile",
|
||
title: "签署文档",
|
||
type: "file",
|
||
limit: 1,
|
||
accept: ".pdf",
|
||
tips: "仅支持PDF文件,如有其他附件或多个单据签约请合并成一个PDF文档上传。且仅支持纵向文档上传,横向文档显示不完整。电子签署文件中需添加以下内容以增强双方约束力和文件法律效力:签约双方均自愿通过以“电子签约”形式进行签约,电子签名和加盖电子印章双方均予以认可具有法律效力。",
|
||
},
|
||
// {
|
||
// key: "download",
|
||
// title: "单据签约模板下载",
|
||
// type: "html",
|
||
// showRule: () => {
|
||
// return this.signType === '1'
|
||
// }
|
||
// },
|
||
{
|
||
key: "buyerSignKeyword",
|
||
title: "签约发起方关键字",
|
||
type: "text",
|
||
placeholder: '发起方盖章关键字',
|
||
tips: '先点击上传文档后,在线预览文档,点击ctrl+f,复制粘贴或输入预盖章部位关键字至弹框内,再次点击ctrl+f进行核验确认位置和内容,核实无误后可返回签署页面输入测验关键字后进行签署(如同一关键字文档多次使用无误后可考虑省略检测流程)。系统将按此关键字进行签章位置的定位,将电子章盖在这个关键字上面。凡出现关键字的地方均会盖上指定用户的电子章,建议关键字在合同中保持唯一。'
|
||
},
|
||
{
|
||
key: "sellerSignKeyword",
|
||
title: "签约接收方关键字",
|
||
type: "text",
|
||
placeholder: '签约接收方关键字',
|
||
tips: '先点击上传文档后,在线预览文档,点击ctrl+f,复制粘贴或输入预盖章部位关键字至弹框内,再次点击ctrl+f进行核验确认位置和内容,核实无误后可返回签署页面输入测验关键字后进行签署(如同一关键字文档多次使用无误后可考虑省略检测流程)。系统将按此关键字进行签章位置的定位,将电子章盖在这个关键字上面。凡出现关键字的地方均会盖上指定用户的电子章,建议关键字在合同中保持唯一。'
|
||
},
|
||
{
|
||
key: "third",
|
||
title: "",
|
||
type: "html"
|
||
},
|
||
{
|
||
key: "otherFile",
|
||
title: "合同附件",
|
||
type: "file",
|
||
showRule: () => {
|
||
return this.signType === '2'
|
||
}
|
||
},
|
||
{
|
||
key: "tips",
|
||
title: "",
|
||
type: "text",
|
||
placeholder: '',
|
||
},
|
||
],
|
||
// 表单验证规则
|
||
rules: {
|
||
title: [{
|
||
required: true,
|
||
message: "标题不能为空",
|
||
trigger: "change"
|
||
}, ],
|
||
phone: [{
|
||
required: true,
|
||
message: "请输入对方金久信实名注册手机号码 不能为空",
|
||
trigger: "change"
|
||
}, ],
|
||
signEndDate: [{
|
||
required: true,
|
||
message: "签署截止日期不能为空",
|
||
trigger: "change"
|
||
}],
|
||
buyerSignKeyword: [{
|
||
required: true,
|
||
message: "签约发起方关键字不能为空",
|
||
trigger: "change"
|
||
}],
|
||
sellerSignKeyword: [{
|
||
required: true,
|
||
message: "签约接收方关键字不能为空",
|
||
trigger: "change"
|
||
}],
|
||
contractFile: [{
|
||
required: true,
|
||
message: "签署文档不能为空",
|
||
trigger: "change"
|
||
}, ],
|
||
payBy: [{
|
||
required: true,
|
||
message: "请选择签约承担方",
|
||
trigger: "change"
|
||
}, ]
|
||
},
|
||
// add 代表添加 update 代表修改
|
||
formType: "",
|
||
// 表单是否在请求中
|
||
loading: false,
|
||
// 是否显示表单1 的弹窗
|
||
show: false,
|
||
},
|
||
},
|
||
// 表单相关结束-----------------------------------------------------------
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
if (options.tab) {
|
||
if (options.tab === 'sign') {
|
||
// this.menuIndex = null
|
||
// this.formData.tradeMethod = null
|
||
// this.isSign = true
|
||
// this.isSignIntro = false
|
||
// this.isSignFlow = false
|
||
// this.isSignLog = false
|
||
// this.isSignTips = false
|
||
this.selectSign()
|
||
} else if (options.tab === 'signPayLog') {
|
||
this.getSignBuyLog(1)
|
||
} else {
|
||
this.menuIndex = String(options.tab)
|
||
if (options.tradeMethod) this.formData.tradeMethod = (parseInt(options.tradeMethod))
|
||
this.formData.status = Number(options.tab)
|
||
this.isSign = false
|
||
if (options.result_desc) {
|
||
uni.showLoading({
|
||
title: '请求中...'
|
||
})
|
||
setTimeout(async () => {
|
||
await this.query(1)
|
||
uni.hideLoading()
|
||
}, 1500)
|
||
} else this.query(1)
|
||
}
|
||
} else {
|
||
this.menuIndex = '0'
|
||
this.formData.status = 0
|
||
this.isSign = false
|
||
this.query(1)
|
||
}
|
||
this.getCommonContractUrl()
|
||
this.getQybPackage()
|
||
this.getSignFlow()
|
||
},
|
||
onShow() {
|
||
this.userInfo = vk.getVuex('$user.userInfo')
|
||
this.getUserInfo()
|
||
this.getPointsBalance()
|
||
if (this.menuIndex === '6') this.getPointsLog();
|
||
if (this.menuIndex === '5') this.getReportList();
|
||
},
|
||
methods: {
|
||
dayjs,
|
||
// 获取当前登录用户uid(vuex优先,storage兜底)
|
||
getUid() {
|
||
let uid = (typeof vk.getVuex === 'function' && vk.getVuex('$user.userInfo._id')) || ''
|
||
if (!uid) {
|
||
const cache = vk.getStorageSync('uni_id_user_info') || {}
|
||
uid = cache._id || cache.uid || ''
|
||
}
|
||
return uid
|
||
},
|
||
menuSelect(e) {
|
||
this.menuIndex = String(e)
|
||
this.formData.status = Number(e)
|
||
this.isSign = false
|
||
this.isSignIntro = false
|
||
this.isSignLog = false
|
||
this.isSignFlow = false
|
||
this.isSignTips = false
|
||
// AI 智能服务菜单(3/4/5/6)不调 query,加载对应数据
|
||
if (e >= 3) {
|
||
if (e === 4 || e === 6) this.getPointsBalance();
|
||
return;
|
||
}
|
||
this.query(1)
|
||
},
|
||
selectSign() {
|
||
this.isSign = true
|
||
this.isSignIntro = false
|
||
this.isSignLog = false
|
||
this.isSignFlow = false
|
||
this.isSignTips = false
|
||
this.menuIndex = ''
|
||
},
|
||
selectSignLog() {
|
||
this.isSign = false
|
||
this.isSignIntro = false
|
||
this.isSignLog = true
|
||
this.isSignFlow = false
|
||
this.isSignTips = false
|
||
this.menuIndex = ''
|
||
this.getSignLog(1)
|
||
},
|
||
async selectSignTips() {
|
||
this.isSign = false
|
||
this.isSignIntro = false
|
||
this.isSignLog = false
|
||
this.isSignTips = true
|
||
this.menuIndex = ''
|
||
const res = await vk.callFunction({
|
||
url: 'client/dict/pub/getDetail',
|
||
data: {
|
||
id: '614937966875c18505570c357541c6e0'
|
||
},
|
||
})
|
||
this.signTips = res.content
|
||
},
|
||
async getSignLog(pageIndex) {
|
||
this.signLogType = 'log'
|
||
this.signLog = await vk.callFunction({
|
||
url: 'client/qyb/list/kh/payList',
|
||
data: {
|
||
pageIndex: pageIndex,
|
||
pageSize: 10
|
||
}
|
||
})
|
||
this.signLog.rows = this.signLog.rows.filter(item => !!item.contract)
|
||
this.$forceUpdate()
|
||
},
|
||
async getSignBuyLog(pageIndex) {
|
||
this.isSign = false
|
||
this.isSignIntro = false
|
||
this.isSignFlow = false
|
||
this.isSignLog = true
|
||
this.isSignTips = false
|
||
this.menuIndex = ''
|
||
this.signLogType = 'payLog'
|
||
this.signBuyLog = await vk.callFunction({
|
||
url: 'client/order/kh/getList',
|
||
data: {
|
||
isSeller: 0,
|
||
keyword: null,
|
||
orderStatus: 4,
|
||
pageIndex,
|
||
pageSize: 10,
|
||
signMethod: null,
|
||
type: [12, 13]
|
||
}
|
||
})
|
||
this.$forceUpdate()
|
||
},
|
||
async selectSignIntro(pageIndex) {
|
||
this.isSign = false
|
||
this.isSignIntro = true
|
||
this.isSignLog = false
|
||
this.isSignTips = false
|
||
this.isSignFlow = false
|
||
this.menuIndex = ''
|
||
const res = await vk.callFunction({
|
||
url: 'client/dict/pub/getDetail',
|
||
data: {
|
||
id: 'ef45322e671cece4110ec73b36ca2b7d'
|
||
},
|
||
})
|
||
this.signIntro = res.content
|
||
},
|
||
async selectSignFlow() {
|
||
this.isSign = false
|
||
this.isSignIntro = false
|
||
this.isSignLog = false
|
||
this.isSignFlow = true
|
||
this.isSignTips = false
|
||
this.menuIndex = ''
|
||
},
|
||
// AI 客服方法
|
||
chatQuickSend(text) {
|
||
this.chatInput = text;
|
||
this.chatSend();
|
||
},
|
||
async chatSend() {
|
||
const msg = this.chatInput.trim();
|
||
if (!msg || this.chatLoading) return;
|
||
this.chatInput = '';
|
||
this.chatMessages.push({
|
||
role: 'user',
|
||
content: msg
|
||
});
|
||
this.chatLoading = true;
|
||
this.chatScrollToBottom();
|
||
|
||
try {
|
||
const res = await vk.callFunction({
|
||
url: 'client/ai/pub/chat',
|
||
data: {
|
||
message: msg,
|
||
bot_id: '7683509749475819554'
|
||
}
|
||
});
|
||
if (res.data.conversation_id) this.chatConversationId = res.data.conversation_id;
|
||
this.chatMessages.push({
|
||
role: 'assistant',
|
||
content: res.data.answer
|
||
});
|
||
} catch (e) {
|
||
this.chatMessages.push({
|
||
role: 'assistant',
|
||
content: '网络异常,请稍后重试'
|
||
});
|
||
}
|
||
this.chatLoading = false;
|
||
this.chatScrollToBottom();
|
||
},
|
||
chatScrollToBottom() {
|
||
setTimeout(() => {
|
||
if (this.chatMessages.length === 0) {
|
||
this.chatScrollId = 'chat-welcome';
|
||
} else if (this.chatLoading) {
|
||
this.chatScrollId = 'chat-loading';
|
||
} else {
|
||
this.chatScrollId = 'chat-msg' + (this.chatMessages.length - 1);
|
||
}
|
||
}, 100);
|
||
},
|
||
// ===== 格式化 AI 回复内容 =====
|
||
formatChatContent(text) {
|
||
if (!text) return '';
|
||
let html = text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
|
||
html = html.replace(/^[\-\•]\s+(.+)$/gm, '<div style="padding-left:12px;">• $1</div>');
|
||
html = html.replace(/^(\d+)\.\s+(.+)$/gm, '<div style="padding-left:12px;">$1. $2</div>');
|
||
html = html.replace(/\n/g, '<br>');
|
||
return html;
|
||
},
|
||
openUrl(url) {
|
||
if (url) window.open(url, '_blank');
|
||
},
|
||
fileToBase64(file) {
|
||
return new Promise((resolve, reject) => {
|
||
const reader = new FileReader();
|
||
reader.onload = () => resolve(reader.result.split(',')[1]);
|
||
reader.onerror = reject;
|
||
reader.readAsDataURL(file);
|
||
});
|
||
},
|
||
formatReviewResult(text) {
|
||
if (!text) return '';
|
||
let html = text;
|
||
// 处理 markdown 标题 ## / ###
|
||
html = html.replace(/^###\s+(.+)$/gm, '<h4 style="margin:16px 0 8px;color:#333;">$1</h4>');
|
||
html = html.replace(/^##\s+(.+)$/gm, '<h3 style="margin:20px 0 10px;color:#222;">$1</h3>');
|
||
// 处理加粗
|
||
html = html.replace(/\*\*(.*?)\*\*/g, '<strong style="color:#a92924;">$1</strong>');
|
||
// 处理链接(PDF下载链接等)— 自动识别 http/https 链接
|
||
html = html.replace(/(https?:\/\/[^\s\)\]\"\<]+)/g, '<a href="$1" target="_blank" style="color:#a92924;text-decoration:underline;word-break:break-all;">$1</a>');
|
||
// 处理列表
|
||
html = html.replace(/^[\-\•]\s+(.+)$/gm, '<div style="padding-left:16px;margin:4px 0;">• $1</div>');
|
||
html = html.replace(/^(\d+)\.\s+(.+)$/gm, '<div style="padding-left:16px;margin:4px 0;">$1. $2</div>');
|
||
// 处理表格行(简单的 | 分隔表格)
|
||
html = html.replace(/\|(.+)\|/g, function(match) {
|
||
const cells = match.split('|').filter(c => c.trim());
|
||
if (cells.length > 1) {
|
||
return '<div style="display:flex;gap:8px;padding:6px 0;border-bottom:1px solid #eee;">' +
|
||
cells.map(c => '<span style="flex:1;font-size:13px;">' + c.trim() + '</span>').join('') + '</div>';
|
||
}
|
||
return match;
|
||
});
|
||
// 换行
|
||
html = html.replace(/\n/g, '<br>');
|
||
return html;
|
||
},
|
||
// ===== 积分相关 =====
|
||
// 积分不足时跳转到「积分管理」并刷新余额与明细
|
||
goRecharge() {
|
||
this.menuIndex = '6';
|
||
this.isSign = false;
|
||
this.isSignIntro = false;
|
||
this.isSignLog = false;
|
||
this.isSignFlow = false;
|
||
this.isSignTips = false;
|
||
this.getPointsBalance();
|
||
this.getPointsLog();
|
||
},
|
||
async getPointsBalance() {
|
||
try {
|
||
const res = await vk.callFunction({
|
||
url: 'client/points/kh/getBalance'
|
||
});
|
||
if (res.code === 0) {
|
||
this.pointsBalance = res.data.balance || 0;
|
||
this.pointsTotalEarned = res.data.total_earned || 0;
|
||
}
|
||
} catch (e) {
|
||
/* 静默 */ }
|
||
},
|
||
async getPointsLog() {
|
||
try {
|
||
const res = await vk.callFunction({
|
||
url: 'client/points/kh/getLog',
|
||
data: {
|
||
pageIndex: 1,
|
||
pageSize: 50
|
||
}
|
||
});
|
||
if (res.code === 0) {
|
||
this.pointsLogList = (res.data.rows || []).map(r => ({
|
||
...r,
|
||
_add_time_str: dayjs(r._add_time && r._add_time['$date'] ? r._add_time['$date'] : r._add_time).format('YYYY-MM-DD HH:mm:ss')
|
||
}));
|
||
}
|
||
} catch (e) {
|
||
/* 静默 */ }
|
||
},
|
||
async getReportList() {
|
||
try {
|
||
const reports = JSON.parse(localStorage.getItem('jjx_review_reports') || '[]');
|
||
this.reportList = reports.map(r => ({
|
||
fileName: r.fileName,
|
||
fileSize: r.fileSize,
|
||
time: r.time,
|
||
amount: r.points,
|
||
result: r.result,
|
||
status: 'completed'
|
||
}));
|
||
} catch (e) { /* 静默 */ }
|
||
},
|
||
async queryReports() {
|
||
try {
|
||
const res = await vk.callFunction({
|
||
url: 'client/ai/pub/getMyReports',
|
||
data: { pageIndex: 1, pageSize: 50, uid: this.getUid() }
|
||
});
|
||
if (res.code === 0 && res.data && res.data.rows) {
|
||
this.reportList = res.data.rows.map(r => ({
|
||
fileName: r.file_name,
|
||
fileSize: r.file_size,
|
||
fileId: r.file_id || '',
|
||
fileUrl: r.file_url || '',
|
||
htmlUrl: r.html_url || '',
|
||
docxUrl: r.docx_url || '',
|
||
time: dayjs(r._add_time && r._add_time['$date'] ? r._add_time['$date'] : r._add_time).format('YYYY-MM-DD HH:mm:ss'),
|
||
amount: r.points_used || 0,
|
||
result: r.result,
|
||
status: r.status,
|
||
recordId: r._id
|
||
}));
|
||
this.$message.success('加载了 ' + this.reportList.length + ' 条报告');
|
||
}
|
||
} catch (e) {
|
||
this.$message.error('查询失败');
|
||
}
|
||
},
|
||
viewReport(r) {
|
||
// 跳转到合同审查页并显示该报告的结果
|
||
this.menuIndex = '4';
|
||
this.reviewResult = r.result || '暂无审查结果';
|
||
this.reviewFile = { name: r.fileName, size: r.fileSize || 0 };
|
||
},
|
||
// 待审查记录 → 重新发起审查
|
||
async reReview(r) {
|
||
if (!r.fileId) {
|
||
this.$message.warning('该记录缺少文件信息,请重新上传合同');
|
||
return;
|
||
}
|
||
try {
|
||
await this.$confirm(
|
||
`确认对「${r.fileName}」发起审查?审查完成后将按文件大小扣除相应积分。`,
|
||
'重新审查',
|
||
{ confirmButtonText: '确认审查', cancelButtonText: '取消', type: 'warning' }
|
||
);
|
||
} catch (e) {
|
||
return;
|
||
}
|
||
// 积分校验
|
||
try {
|
||
const balanceRes = await vk.callFunction({ url: 'client/points/kh/getBalance' });
|
||
if (balanceRes.code !== 0 || !balanceRes.data || balanceRes.data.balance < 200) {
|
||
this.$message.warning('积分不足,请先充值');
|
||
return;
|
||
}
|
||
} catch (e) {
|
||
this.$message.error('积分查询失败,请稍后重试');
|
||
return;
|
||
}
|
||
this.$set(r, '_submitting', true);
|
||
try {
|
||
const res = await vk.callFunction({
|
||
url: 'client/ai/pub/review',
|
||
data: {
|
||
record_id: r.recordId,
|
||
file_id: r.fileId,
|
||
file_name: r.fileName,
|
||
file_size: r.fileSize || 0,
|
||
uid: this.getUid()
|
||
}
|
||
});
|
||
if (res.code === 0) {
|
||
r.status = (res.data && res.data.status) || 'reviewing';
|
||
this.$message.success('已提交审查,AI 正在分析中,预计 3-5 分钟完成');
|
||
setTimeout(() => this.queryReports(), 1500);
|
||
} else {
|
||
this.$message.error(res.msg || '提交审查失败');
|
||
}
|
||
} catch (e) {
|
||
this.$message.error('提交审查异常');
|
||
}
|
||
this.$set(r, '_submitting', false);
|
||
},
|
||
previewFile(r) {
|
||
if (!r.fileUrl) {
|
||
this.$message.warning('文件链接不存在');
|
||
return;
|
||
}
|
||
// 新窗口打开文件(PDF直接预览,Word下载)
|
||
window.open(r.fileUrl, '_blank');
|
||
},
|
||
formatFileSize(bytes) {
|
||
if (bytes < 1024) return bytes + ' B';
|
||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
||
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
||
},
|
||
// ===== 合同审查相关 =====
|
||
selectReviewFile() {
|
||
const input = document.createElement('input');
|
||
input.type = 'file';
|
||
input.accept = '.pdf,.doc,.docx';
|
||
input.onchange = async (e) => {
|
||
const file = e.target.files[0];
|
||
if (!file) return;
|
||
if (file.size > 20 * 1024 * 1024) {
|
||
this.$message.error('文件大小超过 20MB');
|
||
return;
|
||
}
|
||
this.reviewFile = file;
|
||
this.reviewFileId = null;
|
||
this.reviewPoints = Math.max(200, Math.ceil(file.size / (100 * 1024)) * 200);
|
||
|
||
// 立即上传文件到扣子
|
||
this.reviewUploading = true;
|
||
try {
|
||
const fileBase64 = await new Promise((resolve, reject) => {
|
||
const reader = new FileReader();
|
||
reader.onload = () => resolve(reader.result.split(',')[1]);
|
||
reader.onerror = reject;
|
||
reader.readAsDataURL(file);
|
||
});
|
||
|
||
const res = await vk.callFunction({
|
||
url: 'client/ai/pub/review',
|
||
data: {
|
||
file_base64: fileBase64,
|
||
file_name: file.name,
|
||
file_size: file.size,
|
||
uid: this.getUid(),
|
||
upload_only: true
|
||
}
|
||
});
|
||
|
||
if (res.code === 0 && res.data && res.data.file_id) {
|
||
this.reviewFileId = res.data.file_id;
|
||
this.reviewRecordId = res.data.record_id; // 保存待审查记录ID
|
||
this.$message.success('文件上传成功');
|
||
} else {
|
||
this.$message.error(res.msg || '文件上传失败');
|
||
this.reviewFile = null;
|
||
}
|
||
} catch (err) {
|
||
this.$message.error('文件上传异常');
|
||
this.reviewFile = null;
|
||
}
|
||
this.reviewUploading = false;
|
||
};
|
||
input.click();
|
||
},
|
||
// 前端 startReview() 完整改造
|
||
async startReview() {
|
||
if (!this.reviewFileId) {
|
||
this.$message.warning('请先选择合同文件并等待上传完成');
|
||
return;
|
||
}
|
||
|
||
// 1. 检查积分
|
||
const balanceRes = await vk.callFunction({
|
||
url: 'client/points/kh/getBalance'
|
||
});
|
||
if (balanceRes.code !== 0 || !balanceRes.data || balanceRes.data.balance < 200) {
|
||
this.$message.warning('积分不足,请先充值');
|
||
return;
|
||
}
|
||
|
||
this.reviewLoading = true;
|
||
|
||
// 2. 发起审查(更新已有记录状态为 reviewing,用 selectReviewFile 时保存的 file_id)
|
||
const reviewRes = await vk.callFunction({
|
||
url: 'client/ai/pub/review',
|
||
data: {
|
||
record_id: this.reviewRecordId,
|
||
file_id: this.reviewFileId,
|
||
file_name: this.reviewFile.name,
|
||
file_size: this.reviewFile.size,
|
||
uid: this.getUid()
|
||
}
|
||
});
|
||
|
||
this.reviewLoading = false;
|
||
|
||
if (reviewRes.code !== 0) {
|
||
this.$message.error(reviewRes.msg || '发起审查失败');
|
||
return;
|
||
}
|
||
|
||
// 3. 提示用户 + 跳转到我的报告
|
||
this.$alert(
|
||
'合同已提交审查,AI 正在分析中,预计 3-5 分钟完成。您可以在「我的报告」中查看审查结果。',
|
||
'审查已提交',
|
||
{
|
||
confirmButtonText: '查看我的报告',
|
||
callback: () => {
|
||
this.menuSelect('5');
|
||
}
|
||
}
|
||
);
|
||
|
||
// 重置上传区
|
||
this.resetReview();
|
||
},
|
||
resetReview() {
|
||
this.reviewFile = null;
|
||
this.reviewFileId = null;
|
||
this.reviewRecordId = null;
|
||
this.reviewUploading = false;
|
||
this.reviewResult = null;
|
||
this.reviewProgress = 0;
|
||
this.reviewConversationId = null;
|
||
},
|
||
async doRecharge() {
|
||
const pkg = this.rechargePackages[this.selectedRecharge];
|
||
const priceMap = {
|
||
'20元': 2000, '100元': 10000, '200元': 20000,
|
||
'900元': 90000, '2000元': 200000, '4000元': 400000
|
||
}
|
||
const price = priceMap[pkg.price] || 2000;
|
||
const pointsMatch = pkg.points;
|
||
|
||
try {
|
||
// 创建充值订单
|
||
const res = await vk.callFunction({
|
||
url: 'client/points/kh/createOrder',
|
||
data: {
|
||
points: pointsMatch,
|
||
price: price
|
||
}
|
||
});
|
||
if (res.code === 0 && res.data) {
|
||
// 跳转到支付页面(复用现有银联支付)
|
||
vk.navigateTo({
|
||
url: '/pages/index/shop/pay?id=' + res.data
|
||
});
|
||
} else {
|
||
this.$message.error(res.msg || '创建订单失败');
|
||
}
|
||
} catch (e) {
|
||
this.$message.error('充值异常,请稍后重试');
|
||
}
|
||
},
|
||
changeAuthInfo() {
|
||
vk.navigateTo({
|
||
url: '/pages/user/user?menu=fadada'
|
||
})
|
||
},
|
||
async getSignFlow() {
|
||
const res = await vk.callFunction({
|
||
url: 'client/dict/pub/getDetail',
|
||
data: {
|
||
id: 'd77d384f686b9bd204bd4dc744cf75d0'
|
||
},
|
||
})
|
||
this.signFlow = res.content
|
||
this.signFlowTitle = res.name
|
||
},
|
||
async query(pageIndex) {
|
||
this.tableData = await vk.callFunction({
|
||
url: 'client/contract/kh/getList',
|
||
title: '请求中...',
|
||
data: {
|
||
pageIndex,
|
||
pageSize: 10,
|
||
status: this.formData.status,
|
||
tradeMethod: this.formData.tradeMethod,
|
||
type: this.formData.type,
|
||
keyword: this.keyword,
|
||
signType: this.signTypeFilter === '1' ? '1' : '2',
|
||
},
|
||
});
|
||
// this.tableData.rows.forEach(item => {
|
||
// if (item.contract.type === 'sign' && !item.contract.payUser.user) console.log(item.contract.docTitle, item.contract._id)
|
||
// })
|
||
this.$forceUpdate()
|
||
},
|
||
/**
|
||
* 创建合同
|
||
* @param contract
|
||
*/
|
||
createContract(contract) {
|
||
this.form1.data.contractId = contract._id
|
||
this.form1.props.action = 'client/contract/kh/launch'
|
||
this.form1.props.show = true
|
||
},
|
||
async changeContract(contract) {
|
||
this.form1.data.contractId = contract._id
|
||
const {
|
||
data
|
||
} = await vk.callFunction({
|
||
url: 'client/contract/kh/getDetail',
|
||
title: '请求中...',
|
||
data: {
|
||
contractId: contract._id
|
||
},
|
||
});
|
||
this.form1.data.signEndDate = dayjs(data.signEndDate).toDate()
|
||
// console.log(this.form1.data.signEndDate, dayjs(data.signEndDate), dayjs(data.signEndDate).date())
|
||
this.form1.data.title = data.docTitle
|
||
if (Array.isArray(data.contractBase)) this.form1.data.contractFile = data.contractBase
|
||
else this.form1.data.contractFile = [data.contractBase]
|
||
console.log('this.form1.data.contractFile', this.form1.data.contractFile)
|
||
this.form1.data.buyerSignKeyword = data.buyerSignKeyword
|
||
this.form1.data.sellerSignKeyword = data.sellerSignKeyword
|
||
this.form1.data.otherFile = data.otherFile
|
||
this.form1.props.show = true
|
||
this.form1.data.thirdList = data.partyList.filter(item => item.role === 'third').map(item => {
|
||
return {
|
||
phone: item.user.mobile,
|
||
signKeyword: item.keyword
|
||
}
|
||
})
|
||
console.log(this.form1.data)
|
||
if (!this.form1.data.thirdList || !this.form1.data.thirdList.length) {
|
||
this.form1.data.thirdList = [{
|
||
phone: '',
|
||
signKeyword: ''
|
||
}]
|
||
}
|
||
},
|
||
onFormSuccess() {
|
||
this.form1.props.show = false
|
||
// this.form1.data.contractId = null
|
||
// this.formData.status = 1
|
||
// this.menuIndex = '1'
|
||
// this.isSign = false
|
||
// this.$refs.form2.resetForm()
|
||
// this.$refs.form1.resetForm()
|
||
// this.query(1)
|
||
vk.redirectTo({
|
||
url: '/pages/contract/index?tab=1'
|
||
})
|
||
},
|
||
/**
|
||
* 签署合同
|
||
* @param sign
|
||
* @param contract
|
||
*/
|
||
async extSign(sign, contract) {
|
||
if (sign) return
|
||
try {
|
||
await this.$confirm(
|
||
`1、您确认,若全体各签约方未完成全部签约流程,本签约文件不发生法律效力,系统无法对该签约文件予以保存及提供司法调证服务;<br>2、您确认,当前签约的实名主体名称、手机号码与注册认证阶段提交的信息未发生变更;若发生变更,必须及时进行重新认证后方可继续签约,避免产生严重法律风险与经济损失;<br>3、您确认,本次签约为本系统实名认证主体操作,如若不是实名认证主体操作,授权委托人需本系统进行授权认证后签约;<br>4、您确认,您与其他签约方均系在平等、自愿的基础上完成本次签约;<br>5、你确认,此次签约内容符合《中华人民共和国电子签名法》《民法典》生效保护范畴;<br>6、如因法律法规、监管部门政策更新等原因,系统提示您重新进行实名认证、证书续期等重要提示时,请您必须在签约前完成,否则造成任何损失,平台不承担任何责任;以上签约条件您均已经知悉并确认,否则您有权拒绝本次签约。`,
|
||
'提示', {
|
||
confirmButtonText: '知悉同意',
|
||
showCancelButton: false,
|
||
type: 'warning',
|
||
dangerouslyUseHTMLString: true
|
||
})
|
||
} catch (e) {
|
||
return
|
||
}
|
||
vk.callFunction({
|
||
url: 'client/contract/kh/extSign',
|
||
title: '请求中...',
|
||
data: {
|
||
contractId: contract._id,
|
||
isSignOnly: true,
|
||
},
|
||
success: (res) => {
|
||
window.open(res.data)
|
||
}
|
||
});
|
||
},
|
||
showStopSign(contract) {
|
||
vk.callFunction({
|
||
url: 'client/contract/kh/extSign',
|
||
title: '请求中...',
|
||
data: {
|
||
contractId: contract._id
|
||
},
|
||
success: (res) => {
|
||
window.open(res.data)
|
||
}
|
||
});
|
||
},
|
||
viewContract(contract) {
|
||
vk.callFunction({
|
||
url: 'client/contract/kh/viewContract',
|
||
title: '请求中...',
|
||
data: {
|
||
contractId: contract._id
|
||
},
|
||
success: (res) => {
|
||
const userAgent = window.navigator.userAgent.toLowerCase();
|
||
// if (userAgent.match(/micromessenger/i)) {
|
||
// window.open(`https://www.jinjiuxinzg.cn/pdfjs/web/viewer.html?file=${encodeURIComponent(res.data)}`)
|
||
// } else {
|
||
window.open(res.data)
|
||
// }
|
||
// window.open(res.data)
|
||
this.query(1)
|
||
}
|
||
});
|
||
},
|
||
navTo(url) {
|
||
vk.navigateTo({
|
||
url: url
|
||
})
|
||
},
|
||
showParty(userContract) {
|
||
// let content = ''
|
||
// userContract.contract.partyList.forEach(party => {
|
||
// // if (party.userId !== userContract.userId) partyUser = party.user
|
||
// const partyUser = party.user
|
||
// if (partyUser) {
|
||
// const name = partyUser.nickname
|
||
// let role = party.role === 'buyer' ? '购买方' : '销售方'
|
||
// if (party.role === 'third') role = '第三方'
|
||
// content += `<view class="flex flex-col justify-start items-start py-10 border-bottom">
|
||
// <view>身份:${role}</view>
|
||
// <view>联系人:${name}</view>
|
||
// <view>联系方式:${partyUser.mobile}</view></view>`
|
||
// }
|
||
// })
|
||
console.log(123123)
|
||
let content = ''
|
||
const buyer = userContract.contract.partyList.find(item => item.role !== 'third' && userContract.contract
|
||
.userId === item.userId)
|
||
const seller = userContract.contract.partyList.find(item => item.role !== 'third' && userContract.contract
|
||
.userId !== item.userId)
|
||
const thirdList = userContract.contract.partyList.filter(item => item.role === 'third')
|
||
content += `<view class="flex flex-col justify-start items-start py-10 border-bottom">
|
||
<view>身份:发起方</view>
|
||
<view>联系人:${(buyer && buyer.user) ? buyer.user.nickname : ''}</view>
|
||
<view>联系方式:${(buyer && buyer.user) ? buyer.user.mobile : ''}</view></view>`
|
||
content += `<view class="flex flex-col justify-start items-start py-10 border-bottom">
|
||
<view>身份:接收方</view>
|
||
<view>联系人:${(seller && seller.user) ? seller.user.nickname : ''}</view>
|
||
<view>联系方式:${(seller && seller.user) ? seller.user.mobile : ''}</view></view>`
|
||
thirdList.forEach((item, index) => {
|
||
content += `<view class="flex flex-col justify-start items-start py-10 border-bottom">
|
||
<view>身份:第${this.numToChForThird[index]}方</view>
|
||
<view>联系人:${item.user ? item.user.nickname : ''}</view>
|
||
<view>联系方式:${item.user ? item.user.mobile : ''}</view></view>`
|
||
})
|
||
this.$confirm(content, '', {
|
||
cancelButtonText: '关闭',
|
||
showConfirmButton: false,
|
||
dangerouslyUseHTMLString: true
|
||
})
|
||
},
|
||
changeTradeMethod(tradeMethod) {
|
||
this.formData.tradeMethod = tradeMethod
|
||
this.formData.type = null
|
||
this.query(1)
|
||
},
|
||
changeType(type) {
|
||
this.formData.tradeMethod = null
|
||
this.formData.type = type
|
||
// if (type === 'sign' && this.userInfo.qybNum === 0) {
|
||
// this.$confirm(`金久信签约宝次数已用完,请先购买`, '', {
|
||
// confirmButtonText: '去购买',
|
||
// })
|
||
// }
|
||
this.query(1)
|
||
},
|
||
openDownload(otherFile) {
|
||
for (let file in otherFile) {
|
||
window.open(otherFile)
|
||
}
|
||
},
|
||
async getCommonContractUrl() {
|
||
const res = await vk.callFunction({
|
||
url: 'client/dict/pub/getDetail',
|
||
data: {
|
||
id: '657d36befe975f63af1f24e7'
|
||
},
|
||
})
|
||
this.contractTemplate = res.content
|
||
},
|
||
openPreview(url) {
|
||
let openUrl = url
|
||
if (Array.isArray(url)) openUrl = url[url.length - 1]
|
||
if (!openUrl) return
|
||
window.open(`https://www.jinjiuxinzg.cn/pdfjs/web/viewer.html?file=${encodeURIComponent(openUrl)}`,
|
||
'_blank')
|
||
},
|
||
downloadContractTemplate() {
|
||
window.open(this.contractTemplate)
|
||
},
|
||
async delContract(contract) {
|
||
this.$confirm('请您确认文件无用或已经完成履约后再删除文件,删除后平台将无法为您提供相关数据,您将无法查询已经删除文件。请您谨慎操作。', '温馨提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(async () => {
|
||
await vk.callFunction({
|
||
url: 'client/contract/kh/del',
|
||
data: {
|
||
id: contract._id
|
||
},
|
||
success: (res) => {
|
||
this.query(1)
|
||
}
|
||
})
|
||
this.getUserInfo()
|
||
})
|
||
},
|
||
async payDownloadContract(contract) {
|
||
const amountRes = await vk.callFunction({
|
||
url: 'client/dict/pub/getDetail',
|
||
data: {
|
||
id: 'ded3e568662e5817000026633aad1e69'
|
||
},
|
||
})
|
||
if (!contract.canDownloadUidList || !contract.canDownloadUidList.includes(this.userInfo.uid)) {
|
||
if (contract.certificate && !contract.certificate.paidConfirm) {
|
||
if (contract.certificate.paymentMethod === 'online') {
|
||
return vk.navigateTo({
|
||
url: `/pages/index/shop/pay?id=${contract.certificate.orderId}`
|
||
})
|
||
} else return vk.toast('请先等待后台确认支付')
|
||
}
|
||
vk.navigateTo({
|
||
url: `/pages/contract/certificate?contractId=${contract._id}&transactionId=${contract.transactionId}&amount=${amountRes.content}`
|
||
})
|
||
} else {
|
||
this.$confirm(`您提交申请后,平台将在两个工作日内向您提供合法生效文件,客服电话:0771-3923001`, '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(async () => {
|
||
// let loadingInstance1 = Loading.service({fullscreen: true, text: '加载中...'});
|
||
// const res = await vk.callFunction({
|
||
// url: 'client/contract/kh/report',
|
||
// data: {
|
||
// contractNum: contract.transactionId
|
||
// }
|
||
// });
|
||
// loadingInstance1.close()
|
||
// if (res) {
|
||
// window.open(res.data.downloadUrl)
|
||
// }
|
||
if (contract.certificate) {
|
||
if (contract.certificate.deliveryMethods.paper) {
|
||
vk.toast('您的司法调证文件已发货,请注意查收')
|
||
} else {
|
||
if (contract.certificate.downloadUrl) window.open(contract.certificate
|
||
.downloadUrl)
|
||
else vk.toast('文件生成中,请耐心等待')
|
||
}
|
||
} else vk.toast('操作失败,请联系客服')
|
||
})
|
||
|
||
}
|
||
},
|
||
async downloadContract(contract_id) {
|
||
let loadingInstance1 = Loading.service({
|
||
fullscreen: true,
|
||
text: '加载中...'
|
||
});
|
||
const res = await vk.callFunction({
|
||
url: 'client/contract/pub/getDownload',
|
||
data: {
|
||
contract_id
|
||
}
|
||
});
|
||
loadingInstance1.close()
|
||
if (res) {
|
||
window.open(
|
||
`https://textapi.fadada.com/api2/downLoadContract.api?app_id=502704×tamp=${res.data.timestamp}&v=2.0&msg_digest=${res.data.msg_digest}&contract_id=${contract_id}`
|
||
)
|
||
}
|
||
},
|
||
getUserInfo() {
|
||
vk.userCenter.getCurrentUserInfo({
|
||
success: ({
|
||
userInfo
|
||
}) => {
|
||
this.userInfo = userInfo
|
||
if (!this.userInfo.mobile) {
|
||
this.$confirm('请先绑定手机号', '提示', {
|
||
confirmButtonText: '确定',
|
||
showCancelButton: false,
|
||
type: 'warning'
|
||
}).then(() => {
|
||
vk.redirectTo(`/pages/auth/login/wechat?state=jjx_` + this.userInfo
|
||
.accountType)
|
||
}).catch(() => {
|
||
vk.redirectTo(`/pages/auth/login/wechat?state=jjx_` + this.userInfo
|
||
.accountType)
|
||
})
|
||
return
|
||
}
|
||
if (!this.userInfo.fadadaConfirm) {
|
||
this.$confirm('您尚未认证, 是否前往个人中心认证?', '提示', {
|
||
confirmButtonText: '确定',
|
||
showCancelButton: false,
|
||
type: 'warning',
|
||
showClose: false,
|
||
closeOnClickModal: false,
|
||
closeOnPressEscape: false
|
||
}).then(() => {
|
||
vk.navigateTo({
|
||
url: '/pages/user/user?menu=fadada'
|
||
});
|
||
})
|
||
}
|
||
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
|
||
}
|
||
}
|
||
});
|
||
},
|
||
async getQybPackage() {
|
||
const {
|
||
rows
|
||
} = await vk.callFunction({
|
||
url: 'client/qyb/list/pub/getList',
|
||
data: {
|
||
pageIndex: 1,
|
||
pageSize: 500
|
||
}
|
||
});
|
||
this.qybPackageList = rows
|
||
},
|
||
async doBuyQyb() {
|
||
const {
|
||
data
|
||
} = await vk.callFunction({
|
||
url: 'client/qyb/list/kh/buy',
|
||
title: '请求中...',
|
||
data: {
|
||
qybId: this.selectedQyb,
|
||
}
|
||
})
|
||
this.dialogVisible = false
|
||
vk.navigateTo({
|
||
url: `/pages/index/shop/pay?id=${data}`
|
||
})
|
||
},
|
||
addThird() {
|
||
if (this.form2.data.thirdList.length < 8) this.form2.data.thirdList.push({
|
||
phone: '',
|
||
signKeyword: '',
|
||
})
|
||
},
|
||
removeThird(index) {
|
||
if (this.form2.data.thirdList.length === 1) return
|
||
this.form2.data.thirdList.splice(index, 1)
|
||
},
|
||
thirdMoveUp(index) {
|
||
let tmpList = JSON.parse(JSON.stringify(this.form2.data.thirdList));
|
||
[tmpList[index], tmpList[index - 1]] = [tmpList[index - 1], tmpList[index]];
|
||
this.form2.data.thirdList = tmpList
|
||
this.$forceUpdate()
|
||
},
|
||
thirdMoveDown(index) {
|
||
let tmpList = JSON.parse(JSON.stringify(this.form2.data.thirdList));
|
||
[tmpList[index], tmpList[index + 1]] = [tmpList[index + 1], tmpList[index]];
|
||
this.form2.data.thirdList = tmpList
|
||
this.$forceUpdate()
|
||
},
|
||
addThird1() {
|
||
if (this.form1.data.thirdList.length < 8) this.form1.data.thirdList.push({
|
||
phone: '',
|
||
signKeyword: '',
|
||
})
|
||
},
|
||
removeThird1(index) {
|
||
if (this.form1.data.thirdList.length === 1) return
|
||
this.form1.data.thirdList.splice(index, 1)
|
||
},
|
||
thirdMoveUp1(index) {
|
||
let tmpList = JSON.parse(JSON.stringify(this.form1.data.thirdList));
|
||
[tmpList[index], tmpList[index - 1]] = [tmpList[index - 1], tmpList[index]];
|
||
this.form1.data.thirdList = tmpList
|
||
this.$forceUpdate()
|
||
},
|
||
thirdMoveDown1(index) {
|
||
let tmpList = JSON.parse(JSON.stringify(this.form1.data.thirdList));
|
||
[tmpList[index], tmpList[index + 1]] = [tmpList[index + 1], tmpList[index]];
|
||
this.form1.data.thirdList = tmpList
|
||
this.$forceUpdate()
|
||
},
|
||
openInvoice(id) {
|
||
this.invoiceForm.id = id
|
||
this.showInvoice = true
|
||
},
|
||
async doInvoice() {
|
||
if (!this.invoiceForm.name) return this.$message.error('请输入发票抬头')
|
||
if (!this.invoiceForm.code) return this.$message.error('请输入纳税人识别号')
|
||
if (!this.invoiceForm.phone) return this.$message.error('请输入手机号码')
|
||
if (!this.invoiceForm.email) return this.$message.error('请输入邮箱')
|
||
if (!this.invoiceForm.address) return this.$message.error('请输入地址')
|
||
await vk.callFunction({
|
||
url: 'client/order/kh/changeInvoice',
|
||
data: {
|
||
...this.invoiceForm,
|
||
isInvoice: 1
|
||
}
|
||
})
|
||
this.showInvoice = false
|
||
this.$message.success('申请成功')
|
||
await this.getSignBuyLog(1)
|
||
},
|
||
copyInvoice(row) {
|
||
// const url = row.invoiceUrl[0]
|
||
// document.oncopy = function (e) {
|
||
// e.clipboardData.setData('text', url);
|
||
// e.preventDefault();
|
||
// document.oncopy = null;
|
||
// }
|
||
// document.execCommand('Copy');
|
||
// this.$message({
|
||
// type: 'success',
|
||
// message: '已复制'
|
||
// })
|
||
let openUrl = row.invoiceUrl
|
||
if (Array.isArray(openUrl)) openUrl = openUrl[openUrl.length - 1]
|
||
const userAgent = window.navigator.userAgent.toLowerCase();
|
||
// 检测 userAgent 中是否包含 'micromessenger' 字符串
|
||
if (userAgent.match(/micromessenger/i)) {
|
||
window.open(`https://www.jinjiuxinzg.cn/pdfjs/web/viewer.html?file=${encodeURIComponent(openUrl)}`)
|
||
} else {
|
||
window.open(openUrl)
|
||
}
|
||
},
|
||
openPayVoucher(row) {
|
||
const userAgent = window.navigator.userAgent.toLowerCase();
|
||
// 检测 userAgent 中是否包含 'micromessenger' 字符串
|
||
if (userAgent.match(/micromessenger/i)) {
|
||
this.payVoucher = row.payVoucher
|
||
this.showPayVoucher = true
|
||
} else {
|
||
window.open(row.payVoucher)
|
||
}
|
||
// window.open(row.payVoucher)
|
||
},
|
||
beforeUpload(file) {
|
||
if ((new RegExp(/\s+/g).test(file.name))) {
|
||
vk.toast('文件名不能有空格')
|
||
return false
|
||
}
|
||
},
|
||
uploadSuccess(response, file, fileList) {
|
||
console.log(response, file, fileList)
|
||
this.openPreview(response.url)
|
||
this.$refs.uploader.clearFiles();
|
||
},
|
||
uploadSuccess1(response, file, fileList) {
|
||
console.log(response, file, fileList)
|
||
this.openPreview(response.url)
|
||
this.$refs.uploader1.clearFiles();
|
||
},
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.el-picker-panel.currentDatePickerClass {
|
||
.el-button--text.el-picker-panel__link-btn {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
.currentDatePickerClass>>>.el-picker-panel__link-btn {
|
||
display: none
|
||
}
|
||
</style>
|
||
|
||
<style scoped lang="scss">
|
||
.w-1200 {
|
||
width: 1200px;
|
||
margin: 20px auto;
|
||
}
|
||
|
||
.contract-list {
|
||
background-color: #FFFFFF;
|
||
padding: 10px 20px 20px;
|
||
}
|
||
|
||
.paginatio {
|
||
margin: 20px auto;
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
|
||
.contract-box {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
border-bottom: solid 1px #e8e8e8;
|
||
padding: 10rpx 0;
|
||
}
|
||
|
||
.contract-right {
|
||
text-align: right;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.trade-method {
|
||
display: flex;
|
||
margin-bottom: 20px;
|
||
border-bottom: solid 2px #f4f4f4;
|
||
}
|
||
|
||
.trade-method-item {
|
||
cursor: pointer;
|
||
font-size: 18px;
|
||
margin-right: 15px;
|
||
height: 40px;
|
||
line-height: 40px;
|
||
}
|
||
|
||
.trade-method-item.active {
|
||
color: #A92924;
|
||
border-bottom: solid 2px #A92924;
|
||
}
|
||
|
||
.contract-left-top {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
}
|
||
|
||
.contract-product {
|
||
margin: 0 10px;
|
||
}
|
||
|
||
.contract-left-center {
|
||
//height: 40px;
|
||
display: flex;
|
||
justify-content: flex-start;
|
||
align-items: flex-start;
|
||
color: #999999;
|
||
font-size: 12px;
|
||
flex-direction: column;
|
||
margin-top: 5px;
|
||
}
|
||
|
||
.contract-left-bottom {
|
||
display: flex;
|
||
}
|
||
|
||
.contract-status {
|
||
display: flex;
|
||
}
|
||
|
||
.contract-status-tag {
|
||
margin-left: 2px;
|
||
}
|
||
|
||
.sign {
|
||
padding: 20px 20px 20px 25px;
|
||
font-size: 14px;
|
||
|
||
.icon {
|
||
line-height: 18px;
|
||
}
|
||
}
|
||
|
||
.buy-quote-mode-active {
|
||
border: 1px solid #a92924;
|
||
background-color: #f1c7c7;
|
||
color: #333;
|
||
}
|
||
|
||
/* ===== 右侧悬浮工具栏 ===== */
|
||
.float-toolbar {
|
||
position: fixed;
|
||
right: 20px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
background: #fff;
|
||
border-radius: 12px;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
|
||
padding: 12px 0;
|
||
z-index: 100;
|
||
width: 72px;
|
||
}
|
||
|
||
.float-toolbar .tool-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 10px 8px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.float-toolbar .tool-item:hover {
|
||
background: #f6eae9;
|
||
}
|
||
|
||
.float-toolbar .tool-item .tool-icon {
|
||
font-size: 22px;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.float-toolbar .tool-item .tool-label {
|
||
font-size: 10px;
|
||
color: #666;
|
||
text-align: center;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.float-toolbar .tool-divider {
|
||
height: 1px;
|
||
background: #eee;
|
||
margin: 4px 12px;
|
||
}
|
||
|
||
.float-toolbar .tool-item.ai-hot {
|
||
position: relative;
|
||
}
|
||
|
||
.float-toolbar .tool-item.ai-hot::after {
|
||
content: "AI";
|
||
position: absolute;
|
||
top: 4px;
|
||
right: 8px;
|
||
background: #a92924;
|
||
color: #fff;
|
||
font-size: 8px;
|
||
padding: 1px 4px;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
/* ===== 新增AI功能菜单组 ===== */
|
||
.menu-group-title {
|
||
padding: 8px 20px 4px;
|
||
font-size: 11px;
|
||
color: #999;
|
||
letter-spacing: 2px;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
/* AI 客服嵌入样式 */
|
||
.ai-chat-embed {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: calc(100vh - 260px);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.chat-header-bar {
|
||
padding: 14px 20px;
|
||
border-bottom: 1px solid #eee;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.chat-title {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.chat-subtitle {
|
||
font-size: 12px;
|
||
color: #999;
|
||
}
|
||
|
||
.chat-msg-list {
|
||
flex: 1;
|
||
padding: 16px 20px;
|
||
background: #f7f8fa;
|
||
}
|
||
|
||
.chat-row {
|
||
display: flex;
|
||
gap: 10px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.chat-row.right {
|
||
flex-direction: row-reverse;
|
||
}
|
||
|
||
.chat-avatar {
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, #a92924, #d4574e);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #fff;
|
||
font-size: 14px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.chat-bubble {
|
||
max-width: 65%;
|
||
padding: 10px 14px;
|
||
border-radius: 12px;
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.chat-row:not(.right) .chat-bubble {
|
||
background: #fff;
|
||
border: 1px solid #eee;
|
||
border-top-left-radius: 4px;
|
||
}
|
||
|
||
.chat-row.right .chat-bubble {
|
||
background: #a92924;
|
||
color: #fff;
|
||
border-top-right-radius: 4px;
|
||
}
|
||
|
||
.chat-bubble.typing {
|
||
color: #999;
|
||
}
|
||
|
||
.quick-btns {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 6px;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
.quick-btn {
|
||
padding: 5px 12px;
|
||
border-radius: 14px;
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
border: 1px solid #a92924;
|
||
color: #a92924;
|
||
background: #fff;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.quick-btn:hover {
|
||
background: #a92924;
|
||
color: #fff;
|
||
}
|
||
|
||
.chat-input-bar {
|
||
padding: 12px 20px;
|
||
border-top: 1px solid #eee;
|
||
display: flex;
|
||
gap: 10px;
|
||
background: #fff;
|
||
}
|
||
|
||
.chat-input-bar input {
|
||
flex: 1;
|
||
height: 38px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 8px;
|
||
padding: 0 12px;
|
||
font-size: 13px;
|
||
outline: none;
|
||
}
|
||
|
||
.chat-input-bar input:focus {
|
||
border-color: #a92924;
|
||
}
|
||
|
||
.chat-send-btn {
|
||
padding: 0 20px;
|
||
height: 38px;
|
||
line-height: 38px;
|
||
background: #a92924;
|
||
color: #fff;
|
||
border-radius: 8px;
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.chat-send-btn:hover {
|
||
background: #982520;
|
||
}
|
||
|
||
/* 合同审查样式 */
|
||
.review-container {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
padding: 24px;
|
||
}
|
||
|
||
.review-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
margin-bottom: 20px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; transform: scale(1); }
|
||
50% { opacity: 0.5; transform: scale(1.2); }
|
||
}
|
||
.disclaimer-notice {
|
||
display: flex; align-items: flex-start; gap: 8px;
|
||
background: #fff8e1; border: 1px solid #ffe082; border-radius: 8px;
|
||
padding: 12px 16px; margin-bottom: 20px; font-size: 12px; color: #e6a23c;
|
||
}
|
||
.dn-icon { font-size: 16px; flex-shrink: 0; margin-top: 1px; }
|
||
.dn-text { line-height: 1.6; }
|
||
.points-notice {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
background: #fff8e1;
|
||
border: 1px solid #ffe082;
|
||
border-radius: 8px;
|
||
padding: 14px 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.pn-icon {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.pn-text {
|
||
flex: 1;
|
||
}
|
||
|
||
.pn-text .pn-title {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.pn-text .pn-desc {
|
||
font-size: 12px;
|
||
color: #666;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.pn-balance {
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: #a92924;
|
||
}
|
||
|
||
.upload-area {
|
||
border: 2px dashed #d9d9d9;
|
||
border-radius: 12px;
|
||
padding: 48px;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.upload-area:hover {
|
||
border-color: #a92924;
|
||
background: #fafafa;
|
||
}
|
||
|
||
.upload-area .upload-icon {
|
||
font-size: 48px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.upload-area .upload-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.upload-area .upload-desc {
|
||
font-size: 13px;
|
||
color: #999;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.upload-area .upload-btn {
|
||
display: inline-block;
|
||
padding: 10px 32px;
|
||
background: #a92924;
|
||
color: #fff;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.file-info {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
background: #f5f5f5;
|
||
border-radius: 8px;
|
||
padding: 12px 16px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.file-info .fi-icon {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.file-info .fi-name {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.file-info .fi-size {
|
||
font-size: 11px;
|
||
color: #999;
|
||
}
|
||
|
||
.file-info .fi-remove {
|
||
margin-left: auto;
|
||
color: #f56c6c;
|
||
cursor: pointer;
|
||
font-size: 18px;
|
||
}
|
||
|
||
.review-opts {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 16px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.review-opt {
|
||
border: 1px solid #eee;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.review-opt:hover {
|
||
border-color: #a92924;
|
||
}
|
||
|
||
.review-opt.selected {
|
||
border-color: #a92924;
|
||
background: #fef8f7;
|
||
}
|
||
|
||
.review-opt .opt-icon {
|
||
font-size: 28px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.review-opt .opt-title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.review-opt .opt-desc {
|
||
font-size: 11px;
|
||
color: #999;
|
||
}
|
||
|
||
.review-opt .opt-points {
|
||
font-size: 12px;
|
||
color: #a92924;
|
||
font-weight: 600;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.result-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.result-header h3 {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.result-status {
|
||
padding: 4px 12px;
|
||
border-radius: 12px;
|
||
font-size: 12px;
|
||
background: #e8f5e9;
|
||
color: #67c23a;
|
||
}
|
||
|
||
.result-body {
|
||
background: #f9f9f9;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
font-size: 13px;
|
||
line-height: 1.8;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.result-footer {
|
||
text-align: center;
|
||
}
|
||
|
||
/* 积分管理样式 */
|
||
.points-summary {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 16px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.ps-card {
|
||
background: linear-gradient(135deg, #a92924, #d4574e);
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
color: #fff;
|
||
text-align: center;
|
||
}
|
||
|
||
.ps-card.secondary {
|
||
background: linear-gradient(135deg, #333, #555);
|
||
}
|
||
|
||
.ps-card .ps-label {
|
||
font-size: 12px;
|
||
opacity: 0.8;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.ps-card .ps-value {
|
||
font-size: 32px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* 充值卡片样式 */
|
||
.recharge-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 12px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.recharge-card {
|
||
border: 2px solid #eee;
|
||
border-radius: 8px;
|
||
padding: 16px;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.recharge-card:hover {
|
||
border-color: #a92924;
|
||
}
|
||
|
||
.recharge-card.selected {
|
||
border-color: #a92924;
|
||
background: #fef8f7;
|
||
}
|
||
|
||
.recharge-card .rc-points {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: #a92924;
|
||
}
|
||
|
||
.recharge-card .rc-price {
|
||
font-size: 13px;
|
||
color: #666;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.recharge-card .rc-bonus {
|
||
font-size: 11px;
|
||
color: #fff;
|
||
background: #e6a23c;
|
||
padding: 2px 8px;
|
||
border-radius: 10px;
|
||
margin-top: 8px;
|
||
display: inline-block;
|
||
}
|
||
|
||
/* 我的报告样式 */
|
||
.report-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 14px 0;
|
||
border-bottom: 1px solid #f5f5f5;
|
||
}
|
||
|
||
.report-item .report-name {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.report-item .report-time {
|
||
font-size: 11px;
|
||
color: #999;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.report-item .report-info {
|
||
flex: 1;
|
||
}
|
||
</style> |