bonus-ui/src/views/material/materialStation/authorize/index.vue

1666 lines
55 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="申请时间" prop="startTime">
<el-date-picker
v-model="queryParams.startTime"
type="date"
placeholder="开始日期"
clearable
value-format="yyyy-MM-dd"
format="yyyy-MM-dd"
style="width: 130px"
/>
</el-form-item>
<el-form-item label="">-</el-form-item>
<el-form-item label="" prop="endTime">
<el-date-picker
v-model="queryParams.endTime"
type="date"
placeholder="结束日期"
clearable
value-format="yyyy-MM-dd"
format="yyyy-MM-dd"
style="width: 130px"
/>
</el-form-item>
<el-form-item label="关键字" prop="keyWord">
<el-input
v-model="queryParams.keyWord"
placeholder="请输入关键字"
clearable
maxlength="50"
style="width: 240px"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="授权状态" prop="bindStatus">
<el-select v-model="queryParams.taskStatus" placeholder="请选择状态" clearable filterable style="width: 240px">
<el-option label="全部" value=""></el-option>
<el-option label="未授权" value="0"></el-option>
<el-option label="已授权" value="1"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">查询</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
<el-button
type="success"
icon="el-icon-plus"
size="mini"
@click="handleAdd"
:disabled="selectList.length === 0"
>
批量授权
<span v-if="selectList.length > 0" style="margin-left: 5px">({{ selectList.length }})</span>
</el-button>
<span v-if="selectList.length > 0" style="margin-left: 10px; color: #67c23a">
已选择 {{ selectList.length }} 条未授权记录
</span>
</el-form-item>
</el-form>
<el-table
border
:data="tableList"
v-loading="loading"
ref="multipleTable"
@selection-change="handleSelectionChange"
row-key="id"
:max-height="650"
>
<el-table-column type="selection" align="center" :selectable="selectable" />
<el-table-column label="序号" align="center" type="index">
<template slot-scope="scope">
<span>{{ (queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column label="申请时间" align="center" prop="createTime" width="100" />
<el-table-column label="申请人" align="center" prop="createBy" show-overflow-tooltip />
<el-table-column label="领用单位" align="center" prop="leaseUnit" width="200" />
<el-table-column label="领用工程" align="center" prop="leaseProject" width="200" />
<el-table-column label="领用物资类型" align="center" prop="maTypeNames" show-overflow-tooltip />
<el-table-column label="业务单号" align="center" prop="code" show-overflow-tooltip />
<el-table-column label="领料人" align="center" prop="leasePerson" show-overflow-tooltip />
<el-table-column label="领料人电话" align="center" prop="phone" width="110" />
<el-table-column label="授权状态" align="center" prop="status" show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="scope.row.authId == null">未授权</span>
<span v-if="scope.row.authId != null">已授权</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="270">
<template slot-scope="{ row }">
<el-button v-if="row.authId === null" size="mini" type="danger" @click="handleAuth(row)">授权</el-button>
<el-button size="mini" v-if="row.authId !== null" type="success" @click="handleView(row)">查看</el-button>
<el-button size="mini" v-if="row.authId !== null" type="warning" @click="handleEdit(row)">编辑</el-button>
<el-button size="mini" v-if="row.authId !== null" type="primary" @click="handleAuthorize(row)">
项目部委托书
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
:total="total"
v-show="total > 0"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<el-dialog :title="dialogTitle" :visible.sync="authDialogVisible" width="60%" append-to-body>
<el-form :model="authForm" ref="authFormRef" label-width="110px" class="auth-dialog-form" :rules="authFormRules">
<el-form-item label="选择单位:" prop="teamId">
<el-popover
placement="bottom-start"
width="400"
trigger="click"
v-model="teamSelectVisible"
>
<div class="tree-container">
<el-input
v-model="treeFilterText"
placeholder="搜索单位"
size="small"
clearable
style="margin-bottom: 10px"
/>
<el-tree
:data="teamTreeData"
:props="treeProps"
node-key="id"
:highlight-current="true"
:expand-on-click-node="false"
:filter-node-method="filterTeamNode"
:default-expand-all="false"
:show-checkbox="false"
ref="teamTree"
@node-click="handleTeamNodeClick"
class="team-tree"
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<span
:class="{
'selectable-node': isSelectableNode(data),
'disabled-node': !isSelectableNode(data)
}"
>
{{ data.name }}
<!-- <span v-if="data.level" class="node-level">({{ getLevelText(data.level) }})</span>-->
</span>
</span>
</el-tree>
</div>
<el-input
slot="reference"
v-model="authForm.teamName"
placeholder="请选择单位"
readonly
clearable
style="width: 50%"
:disabled="isEditMode"
@clear="handleTeamClear"
>
<i slot="suffix" class="el-input__icon el-icon-arrow-down"></i>
</el-input>
</el-popover>
</el-form-item>
<el-form-item label="授权委托书:" prop="authorizationFile" required>
<div style="display: inline-flex; align-items: center; gap: 10px;">
<el-upload
:action="uploadParseUrl"
:headers="upload.headers"
:file-list="fileList"
:on-success="handleParseSuccess"
:on-error="handleError"
:on-remove="handleFileRemove"
accept=".docx, .pdf, image/*"
:before-upload="beforeUploadDocx"
:disabled="isEditMode"
style="margin-right: 10px"
>
<el-button size="small" type="primary" :disabled="isEditMode">点击上传</el-button>
</el-upload>
<el-button v-if="dialogTitle!='编辑授权'" size="small" type="warning" @click="downloadTemplate">下载委托书模版</el-button>
<el-button
v-if="fileList.length > 0 && isEditMode"
size="small"
type="text"
@click="previewFile(authForm.authorizationFile)"
>
</el-button>
</div>
</el-form-item>
<el-form-item label="领料人信息:" required>
<el-table :data="materialReceivers" border style="width: 100%">
<!-- <template>-->
<!-- <div class="id-card-upload-demo">-->
<!-- &lt;!&ndash; 身份证上传表格部分 &ndash;&gt;-->
<!-- <el-table :data="materialReceivers" border style="width: 100%">-->
<el-table-column label="序号" type="index" width="60" align="center"></el-table-column>
<el-table-column label="姓名" prop="name" align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.name" placeholder="请上传身份证识别" maxlength="30"></el-input>
</template>
</el-table-column>
<el-table-column label="身份证号码" prop="idNumber" align="center">
<template slot-scope="scope">
<el-input
v-model="scope.row.idNumber"
placeholder="请上传身份证识别"
maxlength="18"
@blur="handleIdCard(scope.row)"
></el-input>
</template>
</el-table-column>
<el-table-column label="手机号" prop="phone" align="center">
<template slot-scope="scope">
<div style="display: flex">
<!-- <span style="color: red">*</span>-->
<el-input
v-model="scope.row.phone"
placeholder="请输入手机号"
maxlength="11"
@blur="handlePhone(scope.row)"
></el-input>
</div>
</template>
</el-table-column>
<el-table-column label="身份证照片" prop="idPhotos" align="center" width="280">
<template slot-scope="scope">
<div style="display:flex; align-items:center; justify-content:center; gap:10px">
<!-- 正面 -->
<el-upload
:action="uploadUrl"
:headers="uploadHeaders"
:data="{ type: 1 }"
:show-file-list="false"
accept="image/*"
:on-success="(res, file) => handleIdPhotoSuccess(res, file, scope.$index, 'front')"
>
<img
v-if="scope.row.idFrontPhoto"
:src="scope.row.idFrontPhoto"
style="width:120px; height:80px; cursor:pointer; border:1px solid #eee; border-radius:4px; object-fit:cover"
/>
<div v-else style="width:120px; height:80px; border:1px dashed #d9d9d9; display:flex; justify-content:center; align-items:center; cursor:pointer">
<i class="el-icon-plus"></i>
</div>
</el-upload>
<!-- 背面 -->
<el-upload
:action="uploadUrl"
:headers="uploadHeaders"
:data="{ type: 2 }"
:show-file-list="false"
accept="image/*"
:on-success="(res, file) => handleIdPhotoSuccess(res, file, scope.$index, 'back')"
>
<img
v-if="scope.row.idBackPhoto"
:src="scope.row.idBackPhoto"
style="width:120px; height:80px; cursor:pointer; border:1px solid #eee; border-radius:4px; object-fit:cover"
/>
<div v-else style="width:120px; height:80px; border:1px dashed #d9d9d9; display:flex; justify-content:center; align-items:center; cursor:pointer">
<i class="el-icon-plus"></i>
</div>
</el-upload>
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center">
<template slot-scope="scope">
<el-button size="mini" type="danger" @click="removeReceiver(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 10px">
<el-button type="primary" @click="addReceiver">新增领料人</el-button>
</div>
</el-form-item>
<!-- 批量授权时才显示的已选申请单 -->
<el-form-item v-if="isBatchMode" label="已选申请单:">
<el-tag
v-for="item in selectedItems"
:key="item.id"
closable
@close="removeSelectedItem(item)"
style="margin-right: 10px; margin-bottom: 5px"
>
{{ item.code }} ({{ item.leasePerson }})
</el-tag>
<div v-if="selectedItems.length === 0" style="color: #999">无已选申请单</div>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="authDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitAuthorization">{{ isBatchMode ? '批量授权' : '保存' }}</el-button>
</div>
</el-dialog>
<!-- 查看对话框 -->
<el-dialog title="授权详情" :visible.sync="viewDialogVisible" width="800px" append-to-body>
<el-form :model="viewForm" label-width="110px" class="view-dialog-form">
<el-form-item label="班组名称:">
<el-input v-model="viewForm.teamName" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="授权委托书:">
<div v-if="viewForm.authorizationFile">
<el-button type="text" @click="previewFile(viewForm.authorizationFile)">
{{ viewForm.authorizationFile.name }}
</el-button>
<span class="file-size" v-if="viewForm.authorizationFile.size">
({{ formatFileSize(viewForm.authorizationFile.size) }})
</span>
</div>
<span v-else>无</span>
</el-form-item>
<el-form-item label="领料人信息:">
<el-table :data="viewForm.detailsList" border style="width: 100%">
<el-table-column label="序号" type="index" width="60" align="center"></el-table-column>
<el-table-column label="姓名" prop="name" align="center"></el-table-column>
<el-table-column label="身份证号" prop="idNumber" align="center"></el-table-column>
<el-table-column label="身份证照片" align="center">
<template slot-scope="scope">
<div class="id-photo-view">
<el-image
v-if="scope.row.frontUrl"
:src="scope.row.frontUrl"
:preview-src-list="getPreviewList(scope.row)"
style="width: 100px; height: 60px; margin-right: 10px"
></el-image>
<el-image
v-if="scope.row.backUrl"
:src="scope.row.backUrl"
:preview-src-list="getPreviewList(scope.row)"
style="width: 100px; height: 60px"
></el-image>
</div>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="viewDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
<el-dialog
:title="previewTitle"
:visible.sync="previewVisible"
width="90%"
top="5vh"
class="file-preview-dialog"
>
<div v-if="previewType === 'pdf'" class="preview-content">
<iframe :src="previewUrl" width="100%" height="600px" frameborder="0"></iframe>
</div>
<div v-else-if="previewType === 'office'" class="preview-content">
<iframe :src="previewUrl" width="100%" height="600px" frameborder="0"></iframe>
</div>
<div v-else-if="previewType === 'image'" class="preview-content">
<img :src="previewUrl" style="max-width: 100%; max-height: 70vh; display: block; margin: 0 auto;" />
</div>
<div slot="footer">
<el-button @click="previewVisible = false">关闭</el-button>
<el-button type="primary" @click="downloadFile(previewUrl)">下载</el-button>
</div>
</el-dialog>
<!-- 项目部授权委托书 -->
<el-dialog :visible.sync="authorizeVisible" width="50%" append-to-body>
<div style="font-size: 16px; padding: 0 20px">
<vue-easy-print tableShow ref="remarksPrintRef" class="print">
<div style="line-height: 1.9">
<h2 style="text-align: center">委托书</h2>
<div style="text-indent: 2em">
滋有
<span style="text-decoration: underline">{{ authorizeData.projectDept }}</span>
授权委托:
<span v-for="(item, index) in authorizeData.detailsList" :key="index">
<span style="text-decoration: underline">{{authorizeData.teamName }}{{ item.name }}</span>
身份证号
<span style="text-decoration: underline">{{ item.idNumber }}</span>
</span>
前往贵单位办理
<span style="text-decoration: underline">&nbsp;{{ authorizeData.leaseProject }}&nbsp;</span>
工器具、安全文明用品领退料事宜。委托人在贵单位领、退过程中所签署的一切文件和处理与之相关的一切事务,我均予以认可。委托人不得再次委托他人。
</div>
<div>特此委托证明!</div>
<div>
<span>受托人:</span>
<span v-if="authorizeData.detailsList && authorizeData.detailsList.length > 0">
<img
v-if="authorizeData.detailsList[0].signName"
:src="authorizeData.detailsList[0].signName"
alt=""
width="120px"
height="120px"
style="vertical-align: middle;"
:style="{ transform: authorizeData.detailsList[0].signType == 0 ? 'rotate(-90deg)' : 'none' }"
/>
</span>
</div>
<div style="margin-left: 62%; margin-bottom: 20px">
<div>委托单位:{{ authorizeData.leaseUnit }}</div>
<div>授权人:{{ authorizeData.userName }}</div>
<div>授权时间:{{ authorizeData.createTime }}</div>
</div>
<div
v-for="(item, index) in authorizeData.detailsList"
:key="index"
style="display: flex; justify-content: space-around; margin-bottom: 20px"
>
<img :src="item.frontUrl" alt="" width="300" height="150px;" />
<img :src="item.backUrl" alt="" width="300" height="150px;" />
</div>
</div>
<div id="print-content" style="padding: 30px 30px">
<span>附件:业务联系单</span>
<!-- 标题 -->
<div style="text-align: center; font-size: 16px">
<h3 style="font-size: 25px">{{ dialogForm.impUnitName }}</h3>
<h2 style="font-size: 35px; margin: 0 20%; display: flex; justify-content: space-around; align-items: center">
<span>业</span>
<span>务</span>
<span>联</span>
<span>系</span>
<span>单</span>
</h2>
</div>
<div
style="display: flex; justify-content: space-between; align-items: center; line-height: 1.9; padding: 0 15px"
>
<span style="text-decoration: underline">{{ dialogForm.leaseProject }}</span>
<span>
<span style="text-decoration: underline">{{ dialogForm.code }}</span>
</span>
</div>
<!-- 内容 -->
<div style="border: 3px solid #000; min-height: 800px; line-height: 1.9; position: relative">
<div style="font-weight: 800; border-bottom: 1px solid #888; display: flex">
<div style="width: 50%; padding-left: 10px">主送单位:{{ '技术质量科' }}</div>
<div style="width: 50%; padding-left: 10px; border-left: 1px solid #888">
抄送单位:{{ '物资管理中心' }}
</div>
</div>
<!-- 内容 -->
<div style="padding-left: 10px; min-height: 300px">
<div>联系内容:</div>
<div style="text-indent: 2em">
因在
<span style="font-weight: 800">
{{ dialogForm.leaseProject }}
</span>
进行架线施工需要,现需领用以下工具器,详见附件一:
<!-- dialogForm.maTypeNames -->
<div>{{ dialogForm.maTypeNames }}</div>
</div>
</div>
<!-- 说明 -->
<div style="padding-left: 10px; min-height: 200px">
<div>说明:</div>
<div>领料单位:{{ dialogForm.leaseUnit }}</div>
<!-- <div>项目部材料站站长联系人:{{ '徐青松' }} {{ '15234213324' }}</div> -->
<div>项目部技术联系人:{{ dialogForm.createBy }}{{ dialogForm.phone }}</div>
<div>领取人员:{{ dialogForm.leasePerson }}{{ dialogForm.phone }}</div>
</div>
<div
style="
width: 100%;
display: flex;
border-top: 1px solid #888;
border-bottom: 1px solid #888;
position: absolute;
bottom: 90px;
right: 0;
"
>
<div style="width: 35%; padding-left: 10px">主管:{{ '' }}</div>
<div style="width: 43%; padding-left: 10px; border-left: 1px solid #888">
联系:{{ dialogForm.leasePerson }} {{ dialogForm.phone }}
</div>
<div style="width: 22%; padding-left: 10px; border-left: 1px solid #888">
{{ handleTimeFormat(dialogForm.supplierTime) }}
</div>
</div>
<div
style="
width: 100%;
display: flex;
border-top: 1px solid #888;
border-bottom: 1px solid #888;
position: absolute;
bottom: 0;
right: 0;
"
>
<div style="width: 50%; padding-left: 10px">签复:</div>
<div
style="
width: 50%;
padding-left: 100px;
border-left: 1px solid #888;
display: flex;
justify-content: space-around;
align-items: center;
"
>
<span>年</span>
<span>月</span>
<span>日</span>
</div>
</div>
</div>
<!-- 附件 -->
<div style="margin-top: 100px">
<div style="font-size: 20px; padding-left: 40px; line-height: 1.9">
附件一:{{ dialogForm.leaseProject }}
<span style="margin-left: 20px">{{ dialogForm.code }}</span>
号附件
</div>
<div></div>
</div>
<table border="1" style="width: 100%; border-collapse: collapse">
<thead>
<tr>
<th style="width: 55px; text-align: center">序号</th>
<th
v-for="(column, index) in dialogColumns"
:key="column.prop"
:style="{ width: column.width, textAlign: 'center' }"
>
{{ column.label }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in dialogList" :key="rowIndex">
<td style="text-align: center">{{ rowIndex + 1 }}</td>
<td v-for="(column, colIndex) in dialogColumns" :key="colIndex" :style="{ textAlign: 'center' }">
{{ row[column.prop] }}
</td>
</tr>
</tbody>
</table>
</div>
</vue-easy-print>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="authorizePrint">打印</el-button>
<el-button @click="authorizeVisible = false">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import {
getAuthDetail,
getLeasePublishAuthList,
getTeamList,
sumbitAuthData,
uploadIdCard,
getAuthInfo, getBusinessFormByCode
} from '@/api/materialsStation/auth'
import { getToken } from '@/utils/auth'
import { validateIdCard } from '@/utils/bonus'
import vueEasyPrint from 'vue-easy-print'
import {getLeaseTask} from "@/api/business";
export default {
components: { vueEasyPrint },
name: 'IdCardUploadComponent',
data() {
return {
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
statusList: [
{ id: '', name: '全部' },
{ id: '1', name: '已授权' },
{ id: '0', name: '未授权' }
],
total: 0, // 总条数
loading: false, // 遮罩层
showSearch: true, // 显示搜索条件
tableList: [], // 列表数据源
queryTime: [], // 日期数据源
selectList: [], // 列表选中数据
url: null,
fileName: null,
// 列表查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
startTime: null,
endTime: null,
keyWord: '',
taskStatus: ''
},
//新增弹窗
open: false,
previewVisible: false,
previewUrl: '',
previewTitle: '',
previewType: '', // 'image', 'pdf', 'office'
fileList: [],
uploadList: [],
// 上传参数
upload: {
// 是否显示弹出层(上传导入)
open: false,
// 弹出层标题(上传导入)
title: '上传文件',
// 设置上传的请求头部
headers: { Authorization: 'Bearer ' + getToken() },
// 上传的地址
url: process.env.VUE_APP_BASE_API + '/file/upload'
},
titles: '',
authDialogVisible: false,
teamList: [], // 班组列表
authFormRules: {
teamId: [{ required: true, message: '请选择班组', trigger: 'change' }]
},
authForm: {
teamId: '',
teamName: '',
authorizationFile: null,
materialReceivers: [] // 初始化为空,后面统一处理
},
currentRow: null, // 当前操作的行数据
materialReceivers: [],
// uploadUrl: process.env.VUE_APP_BASE_API + '/material/authorize/uploadPhoto',
uploadUrl: process.env.VUE_APP_BASE_API + '/file/upload',
uploadHeaders: {
Authorization: 'Bearer ' + getToken()
},
viewDialogVisible: false,
viewForm: {
teamName: '',
authorizationFile: null,
detailsList: []
},
pdfPreviewVisible: false,
pdfViewerUrl: '',
pdfJsPath: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js',
pdfWorkerPath: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js',
isEditMode: false, // 是否处于编辑模式
dialogTitle: '新增授权', // 弹窗标题
isBatchMode: false, // 是否批量模式
selectedItems: [], // 批量授权时选择的申请单
authorizeVisible: false, // 授权委托书
authorizeData: {
leaseUnit: '',
leaseProject: '',
detailsList: [],
userName: '',
createTime: ''
},
// 新增数据属性
teamSelectVisible: false,
teamTreeData: [],
treeFilterText: '', // 树形搜索文本
treeProps: {
label: 'name',
children: 'children'
},
uploadParseUrl: process.env.VUE_APP_BASE_API + '/material/authorize/parseWord',
//业务联系单
dialogForm: {
proName: '', // 项目名称
code: '' // 业务单号
},
dialogColumns: [
{ label: '名称', prop: 'maTypeName', width: '150px' },
{ label: '规格', prop: 'typeName', width: '150px' },
{ label: '单位', prop: 'unitName', width: '60px' },
{ label: '数量', prop: 'preNum', width: '60px' },
{ label: '备注', prop: 'remark', width: '' }
],
dialogList: []
}
},
created() {
this.getList()
},
watch: {
// 监听弹框显示状态
authDialogVisible(newVal) {
if (!newVal) {
// 弹框关闭时重置上传相关状态
this.resetUploadState()
this.$refs.authFormRef.resetFields() // 重置表单
}
},
// 监听搜索文本变化
treeFilterText(val) {
this.$refs.teamTree.filter(val)
}
},
methods: {
beforeUploadDocx(file) {
const isDocx = file.name.toLowerCase().endsWith('.docx')
const isWordMime =
file.type ===
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
// if (!isDocx || !isWordMime) {
// this.$message.error('只能上传.docx格式的文件')
// return false // 阻止上传
// }
// 限制大小,例如 10MB
const isLt10M = file.size / 1024 / 1024 < 10
if (!isLt10M) {
this.$message.error('文件大小不能超过 10MB')
return false
}
return true
},
// 判断节点是否可选择(只有第三级或没有层级的数据可选择)
isSelectableNode(node) {
// 第三级数据level为"3"
// 没有层级的数据没有parentId或parentId为null
return node.level === '3' || (!node.parentId || node.parentId === null)
},
// 获取层级文本
getLevelText(level) {
const levelMap = {
'1': '一级',
'2': '二级',
'3': '三级'
}
return levelMap[level] || ''
},
// 处理节点点击
handleTeamNodeClick(node) {
// 只有可选择的节点才能被选中
if (!this.isSelectableNode(node)) {
// this.$message.warning('只能选择具体的班组(第三级)或独立班组')
return
}
this.authForm.teamId = node.id
this.authForm.teamName = node.name
this.teamSelectVisible = false
// 清除表单验证错误
this.$refs.authFormRef.clearValidate(['teamId'])
},
// 清除选择
handleTeamClear() {
this.authForm.teamId = ''
this.authForm.teamName = ''
},
// 过滤节点方法
filterTeamNode(value, data) {
if (!value) return true
return data.name.toLowerCase().indexOf(value.toLowerCase()) !== -1
},
// 处理树形数据(适配已经是树形结构的数据)
processTreeData(data) {
if (!Array.isArray(data)) return []
return data.map(item => {
const processedItem = {
...item,
// 确保children存在
children: item.children ? this.processTreeData(item.children) : []
}
return processedItem
})
},
// 是否可用勾选框
selectable(row) {
return row.authId === null
},
// 身份证号码校验
handleIdCard(row) {
const idCard = row.idNumber
const isValid = validateIdCard(idCard)
if (!isValid) {
this.$message.error('身份证号码格式不正确')
}
},
// 手机号验证
handlePhone(row) {
// 正则校验手机号 /^(?:(?:\+|00)86)?1[3-9]\d{9}$/
const reg = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/
if (!reg.test(row.phone)) {
this.$message.error('手机号格式不正确')
row.phone = ''
}
},
// 上传前验证
beforeIdPhotoUpload(file) {
const isImage = file.type.startsWith('image/')
const isLt5M = file.size / 1024 / 1024 < 5
if (!isImage) {
this.$message.error('只能上传图片文件!')
return false
}
if (!isLt5M) {
this.$message.error('上传图片大小不能超过 5MB!')
return false
}
return true
},
// 身份证照片上传成功处理
handleIdPhotoSuccess(response, file, index, type) {
if (type === 'front') {
this.materialReceivers[index].frontUploading = false
} else {
this.materialReceivers[index].backUploading = false
}
if (response.code === 200) {
const data = response.data
if (type === 'front') {
this.materialReceivers[index].idFrontPhoto = data.url
// this.materialReceivers[index].idFrontPhoto = data.frontUrl;
// if (data.name) {
// this.materialReceivers[index].name = data.name;
// }
// if (data.idNumber) {
// this.materialReceivers[index].idNumber = data.idNumber;
// }
} else {
this.materialReceivers[index].idBackPhoto = data.url
// this.materialReceivers[index].idBackPhoto = data.backUrl;
}
// 同步到 authForm
this.authForm.materialReceivers = [...this.materialReceivers]
// this.$message.success('身份证上传识别成功');
} else {
this.$message.error(response.msg || '上传失败')
}
},
// 上传失败处理
handleUploadError(error, file, fileList) {
console.error('上传失败:', error)
this.$message.error('上传失败,请重试')
},
// 获取列表
getList() {
this.loading = true
const params = {
keyWord: this.queryParams.keyWord,
taskStatus: this.queryParams.taskStatus,
startTime: this.queryParams.startTime,
endTime: this.queryParams.endTime,
pageSize: this.queryParams.pageSize,
pageNum: this.queryParams.pageNum
}
getLeasePublishAuthList(params).then(response => {
this.tableList = response.data.rows
this.total = response.data.total
this.loading = false
// 重置选择状态 - 确保清理干净
this.$nextTick(() => {
this.selectList = []
this.selectedItems = [] // 确保清空
this.$refs.multipleTable.clearSelection()
})
})
},
// 搜索按钮
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
// 重置按钮
resetQuery() {
this.queryParams.time = []
this.resetForm('queryForm')
this.queryParams.keyWord = ''
this.queryParams.taskStatus = ''
this.handleQuery()
},
// 列表复选框
handleSelectionChange(validSelection) {
// 只存储未授权项目的ID
this.selectList = validSelection.filter(item => item.authId === null).map(item => item.id)
// 更新 selectedItems 为当前表格中匹配的未授权项
this.selectedItems = this.tableList.filter(item => this.selectList.includes(item.id) && item.authId === null)
this.single = validSelection.length !== 1
this.multiple = !validSelection.length
},
// 单条授权
handleAuth(row) {
this.currentRow = row
this.authDialogVisible = true
this.isEditMode = false
this.isBatchMode = false
this.dialogTitle = '新增授权'
this.resetAuthForm()
this.getTeamList(row.externalId)
// 初始化领料人数据
this.materialReceivers = [
{
name: '',
idNumber: '',
phone:'',
idFrontPhoto: null,
idBackPhoto: null,
frontUploading: false,
backUploading: false
}
]
this.authForm.materialReceivers = [...this.materialReceivers]
},
// 批量授权
handleAdd() {
if (this.selectList.length === 0) {
this.$message.warning('请至少选择一条未授权的记录')
return
}
// 修正只获取selectList中指定的项目
this.selectedItems = this.tableList.filter(item => this.selectList.includes(item.id))
if (this.selectedItems.length === 0) {
this.$message.warning('所选记录中没有未授权的申请单')
return
}
// 检查所有选中的申请单是否属于同一个班组
const firstTeamId = this.selectedItems[0].externalId
const allSameTeam = this.selectedItems.every(item => item.externalId === firstTeamId)
if (!allSameTeam) {
this.$message.error('批量授权只能选择相同班组的申请单')
return
}
this.authDialogVisible = true
this.isEditMode = false
this.isBatchMode = true
this.dialogTitle = `批量授权 (${this.selectedItems.length}条)`
this.resetAuthForm()
this.getTeamList(firstTeamId)
// 初始化领料人数据
this.materialReceivers = [
{
name: '',
idNumber: '',
phone:'',
idFrontPhoto: null,
idBackPhoto: null,
frontUploading: false,
backUploading: false
}
]
},
// 移除已选申请单
removeSelectedItem(item) {
this.selectedItems = this.selectedItems.filter(i => i.id !== item.id)
this.selectList = this.selectList.filter(id => id !== item.id)
this.dialogTitle = `批量授权 (${this.selectedItems.length}条)`
// 更新表格选中状态
this.$nextTick(() => {
this.tableList.forEach(row => {
if (row.id === item.id) {
this.$refs.multipleTable.toggleRowSelection(row, false)
}
})
})
},
// 重置授权表单
resetAuthForm() {
this.authForm = {
teamId: '',
teamName: '',
authorizationFile: null,
materialReceivers: []
}
this.materialReceivers = []
this.fileList = []
this.url = null
this.fileName = null
// 重置表单验证
if (this.$refs.authFormRef) {
this.$refs.authFormRef.clearValidate()
}
},
// 获取班组列表
getTeamList(externalId) {
const params = {
externalId: externalId
}
getTeamList(params).then(response => {
this.teamList = response.data
// 处理树形数据
this.teamTreeData = this.processTreeData(response.data)
})
},
// 重置上传状态
resetUploadState() {
this.fileList = []
this.uploadList = []
this.url = null
},
//文件上传-成功
handleSuccess(response, file, fileList) {
if (response.code == 200) {
console.log(response.data)
console.log('路径:', response.data.url)
this.url = response.data.url
this.fileName = response.data.name
this.$message.success('文件上传成功')
// 更新fileList显示
this.fileList = [
{
name: file.name,
url: response.data.url
}
]
} else {
this.$message.error(response.msg || '文件上传失败')
}
},
// 新增领料人
addReceiver() {
this.materialReceivers.push({
name: '',
idNumber: '',
phone:'',
idFrontPhoto: null,
idBackPhoto: null,
frontUploading: false,
backUploading: false
})
},
// 删除领料人
removeReceiver(index) {
if (this.materialReceivers.length <= 1) {
this.$message.warning('至少需要保留一个领料人')
return
}
this.materialReceivers.splice(index, 1)
},
// 表单验证(统一验证)
validateForm() {
if (!this.authForm.teamId) {
this.$message.error('请选择班组')
return false
}
if (!this.url) {
this.$message.error('请上传授权委托书')
return false
}
if (this.materialReceivers.length === 0) {
this.$message.error('请至少添加一个领料人')
return false
}
for (let i = 0; i < this.materialReceivers.length; i++) {
const receiver = this.materialReceivers[i]
if (!receiver.name || !receiver.idNumber) {
this.$message.error(`${i + 1} 个领料人身份证信息不完整`)
return false
}
if (!receiver.idFrontPhoto || !receiver.idBackPhoto) {
this.$message.error(`${i + 1} 个领料人请上传身份证正反面照片`)
return false
}
// 身份证号码格式验证
if (!validateIdCard(receiver.idNumber)) {
this.$message.error(`${i + 1} 个领料人身份证号码格式不正确`)
return false
}
}
// 批量模式下额外验证
if (this.isBatchMode && this.selectedItems.length === 0) {
this.$message.error('请至少选择一条申请单')
return false
}
return true
},
// 查看授权详情
handleView(row) {
this.viewDialogVisible = true
this.loading = true
// 这里替换为实际的API调用
const params = {
leaseId: row.id
}
getAuthDetail(params)
.then(response => {
console.log(response.data)
this.viewForm = response.data
this.viewForm = {
teamName: response.data.teamName,
authorizationFile: {
name: response.data.fileName,
url: response.data.url
},
detailsList: response.data.detailsList
}
this.loading = false
})
.catch(error => {
this.loading = false
this.$message.error('获取详情失败')
})
},
// 获取预览列表
getPreviewList(row) {
const list = []
if (row.frontUrl) list.push(row.frontUrl)
if (row.backUrl) list.push(row.backUrl)
return list
},
// 预览文件
previewFile(file) {
if (!file || !file.url) return;
const ext = file.name.split('.').pop().toLowerCase();
console.log(ext);
if (ext === 'pdf') {
this.previewPDF(file.url);
} else if (['jpg', 'jpeg', 'png', 'gif'].includes(ext)) {
// 图片预览
this.previewImage(file.url);
} else if (ext === 'docx') {
// 直接下载 DOCX 文件
this.downloadFile(file.url);
} else {
this.$message.warning('不支持预览此文件类型');
}
},
previewPDF(url) {
// 打开 PDF 文件
window.open(url, '_blank');
},
previewImage(url) {
// 创建新窗口
const imgWindow = window.open('', '_blank');
// 设置新窗口内容
imgWindow.document.write(`
<html>
<head>
<title>图片预览</title>
<style>
body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; }
img { max-width: 90%; max-height: 90%; object-fit: contain; }
</style>
</head>
<body>
<img src="${url}" alt="图片预览" />
</body>
</html>
`);
},
downloadFile(url) {
// 直接下载 DOCX 文件
const link = document.createElement('a');
link.href = url;
link.download = url.split('/').pop(); // 自动获取文件名
link.click();
},
// 格式化文件大小
formatFileSize(bytes) {
if (!bytes) return '0 Bytes'
const k = 1024
const sizes = ['Bytes', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i])
},
// 编辑授权
handleEdit(row) {
this.currentRow = row
this.authDialogVisible = true
this.isEditMode = true
this.isBatchMode = false
this.dialogTitle = '编辑授权'
this.getTeamList(row.externalId)
// 重置表单状态
this.resetAuthForm()
// 加载授权详情数据
this.loading = true
const params = {
leaseId: row.id
}
getAuthDetail(params)
.then(response => {
if (response.code === 200) {
const data = response.data
// 填充表单数据
this.authForm.teamId = data.teamId
this.authForm.teamName = data.teamName
this.url = data.url
this.fileName = data.fileName
// 设置文件列表
if (data.url && data.fileName) {
this.fileList = [
{
name: data.fileName,
url: data.url
}
]
this.authForm.authorizationFile = {
name: data.fileName,
url: data.url
}
}
// 设置领料人信息
this.materialReceivers = data.detailsList.map(item => ({
name: item.name,
idNumber: item.idNumber,
idFrontPhoto: item.frontUrl,
idBackPhoto: item.backUrl,
phone:item.phone,
frontUploading: false,
backUploading: false
}))
// 同步到authForm
this.authForm.materialReceivers = [...this.materialReceivers]
} else {
this.$message.error(response.msg || '获取授权详情失败')
}
})
.catch(error => {
console.error('获取授权详情失败:', error)
this.$message.error('获取授权详情失败')
})
.finally(() => {
this.loading = false
})
},
// 项目部委托书
async handleAuthorize(row) {
try {
const params = {
leaseId: row.id
}
const res = await getAuthInfo(params)
this.authorizeData = res.data
const date = new Date()
const time = `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}`
this.authorizeData.createTime = res.data.createTime ? this.formatTime(res.data.createTime) : time
const params2 = {
businessCode: row.code
}
const res2 = await getBusinessFormByCode(params2)
this.dialogForm = {
...res2.data.leaseApplyInfo,
}
this.dialogList = res2.data.leaseApplyDetailsList
this.authorizeVisible = true
} catch (error) {
console.log('🚀 ~ handleAuthorize ~ error:', error)
}
},
// 转换时间格式
formatTime(time) {
const date = new Date(time)
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
return `${year}${month}${day}`
},
// 打印
authorizePrint() {
this.$refs.remarksPrintRef.print()
},
// 提交授权(统一处理单条和批量)
async submitAuthorization() {
// 1. 验证身份证号码是否唯一
const idNumbers = this.materialReceivers.map(receiver => receiver.idNumber)
const uniqueIdNumbers = [...new Set(idNumbers)]
if (idNumbers.length !== uniqueIdNumbers.length) {
this.$message.error('存在重复的身份证号码,请确保每个领料人的身份证号码唯一')
return
}
if (!this.validateForm()) return
try {
this.$loading({
text: this.isBatchMode ? '批量授权中...' : '提交中...',
lock: true
})
// 获取选中的班组名称
// const selectedTeam = this.teamList.find(team => team.id === this.authForm.teamId);
// const teamName = selectedTeam ? selectedTeam.name : '';
const teamName = this.authForm.teamName;
console.log("teamName", teamName)
const requestData = {
teamId: this.authForm.teamId,
teamName: teamName,
url: this.url,
fileName: this.fileName,
detailsList: this.materialReceivers.map(receiver => ({
name: receiver.name,
idNumber: receiver.idNumber,
frontUrl: receiver.idFrontPhoto,
backUrl: receiver.idBackPhoto,
phone:receiver.phone
}))
}
// 根据模式添加不同字段
if (this.isBatchMode) {
requestData.leaseIds = this.selectedItems.map(item => item.id)
} else {
requestData.leaseIds = [this.currentRow.id]
// requestData.leaseId = this.currentRow.id;
requestData.authId = this.isEditMode ? this.currentRow.authId : null
}
const response = await sumbitAuthData(requestData)
if (response.code === 200) {
this.$message.success(
this.isBatchMode
? `成功批量授权 ${this.selectedItems.length} 条记录`
: `${this.isEditMode ? '编辑' : '授权'}成功`
)
// 刷新列表数据
await this.getList()
// 重置选择状态
if (this.isBatchMode) {
this.selectList = []
this.selectedItems = []
this.$refs.multipleTable.clearSelection()
}
this.authDialogVisible = false
} else {
throw new Error(response.msg || `${this.isEditMode ? '编辑' : '授权'}失败`)
}
} catch (error) {
console.error('提交失败:', error)
this.$message.error(error.message || `${this.isEditMode ? '编辑' : '授权'}失败`)
} finally {
this.$loading().close()
}
},
/** 下载模板方法 */
async downloadTemplate() {
try {
// 使用统一的下载方法,确保路径正确
await this.download("/material/authorize/downLoad", {}, "授权委托书模版.docx");
this.$message.success("模板下载成功");
} catch (error) {
console.error("模版下载失败:", error);
this.$message.error("模板下载失败,请稍后重试");
} finally {
}
},
handleParseSuccess(response, file, fileList) {
// 后端返回结构:{ code:200, data: { receivers: [...], filePath: "xxx" } }
const data = (response && response.code === 200) ? response.data : null;
const list = data ? data.receivers : null;
if (!list || list.length === 0) {
this.$message.warning('未识别到委托人信息,请手动填写');
// 设置 fileList 显示上传的文件名
this.fileList = [{ name: file.name, url: data ? data.filePath : '' }];
// 同时保存文件路径(即使没有识别成功)
this.authForm.filePath = data ? data.filePath : '';
this.url=data ? data.filePath : '';
this.fileName=data ? data.fileName : '';
return;
}
// 把识别到的结果赋值给 materialReceivers
this.materialReceivers = list.map(item => ({
name: item.name || '',
idNumber: item.idNumber || '',
phone: item.phone || '',
idFrontPhoto: item.frontUrl || item.idFrontPhoto || item.fronturl || null,
idBackPhoto: item.backUrl || item.idBackPhoto || item.backurl || null,
frontUploading: false,
backUploading: false
}));
// 同步到表单
this.authForm.materialReceivers = [...this.materialReceivers];
// 保存文件路径
this.authForm.filePath = data.filePath;
// 更新 fileList 显示
this.fileList = [{ name: file.name, url: data.filePath }];
this.url=data.filePath;
this.fileName=data.fileName;
this.$message.success(`识别成功,共提取 ${this.materialReceivers.length} 个委托人`);
},
handleError(err) {
console.error('上传/解析失败', err);
this.$message.error('文件上传或解析失败');
},
handleFileRemove(file, fileList) {
this.fileList = fileList;
// 清空识别结果(可选)
// this.materialReceivers = [];
},
// 处理时间格式 2021-09-01 转换为 2021年09月01日
handleTimeFormat(time) {
if (time) {
return time.replace(/-/g, '年').replace(/-/g, '月') + '日'
}
return ''
},
}
}
</script>
<style lang="scss" scoped>
/* 树形容器样式 */
.tree-container {
max-height: 300px;
overflow-y: auto;
}
/* 自定义树节点样式 */
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
/* 可选择节点样式 */
.selectable-node {
color: #333;
font-weight: 500;
cursor: pointer;
&:hover {
color: #409eff;
}
}
/* 不可选择节点样式 */
.disabled-node {
//color: #c0c4cc;
cursor: not-allowed;
font-weight: normal;
}
/* 层级标识样式 */
.node-level {
font-size: 12px;
//color: #909399;
margin-left: 5px;
}
/* 树形组件样式调整 */
.team-tree {
.el-tree-node__content {
height: 32px;
&:hover {
background-color: #f5f7fa;
}
}
.el-tree-node.is-current > .el-tree-node__content {
background-color: #e6f7ff;
color: #409eff;
}
}
/* 批量授权标签容器 */
.selected-items-container {
max-height: 150px;
overflow-y: auto;
border: 1px solid #ebeef5;
padding: 10px;
border-radius: 4px;
margin-top: 10px;
}
/* 批量授权标签 */
.selected-item-tag {
margin-right: 10px;
margin-bottom: 5px;
}
/* 批量授权标题 */
.batch-title {
font-size: 16px;
margin-bottom: 10px;
color: #333;
}
.el-upload.is-disabled {
opacity: 0.7;
cursor: not-allowed;
}
/* 弹窗标题样式 */
.el-dialog__header {
border-bottom: 1px solid #eee;
}
.el-dialog__title {
font-size: 18px;
font-weight: bold;
color: #333;
}
/* 查看对话框样式 */
.view-dialog-form {
margin-left: 0px;
}
.id-photo-view {
display: flex;
justify-content: center;
align-items: center;
}
.file-size {
color: #999;
font-size: 12px;
margin-left: 5px;
}
/* 图片预览模态框样式 */
.image-preview-modal {
width: 80%;
max-width: 800px;
}
.image-preview-modal .el-message-box__content {
display: flex;
justify-content: center;
padding: 20px;
}
/* PDF预览对话框样式 */
.pdf-preview-dialog .el-dialog__body {
padding: 10px;
}
/* 调整表单整体左移 */
.auth-dialog-form {
margin-left: 0px;
}
/* 调整领料人列表标签位置 */
.receiver-list-label {
margin-left: -10px;
}
/* 调整表格位置 */
.receiver-table-item {
margin-left: -30px;
}
/* 表格样式调整 */
.receiver-table {
margin-left: -30px;
width: calc(100% + 30px);
}
.id-photo-upload {
display: flex;
justify-content: space-around;
gap: 10px;
}
.upload-item {
display: flex;
align-items: center;
gap: 5px;
}
.upload-status {
color: #67c23a;
font-weight: bold;
font-size: 14px;
}
.uploadImg {
padding-top: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.boxCode {
margin-top: 10px;
padding-bottom: 20px;
font-size: 18px;
}
::v-deep.el-table .fixed-width .el-button--mini {
width: 60px !important;
margin-bottom: 10px;
}
</style>