2025-12-20 16:53:57 +08:00
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
|
<html lang="zh-CN">
|
|
|
|
|
|
|
|
|
|
|
|
<head>
|
|
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
|
<title>许可文件上传 - LawRisk</title>
|
|
|
|
|
|
<style>
|
|
|
|
|
|
* {
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
body {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
font-family: "Microsoft YaHei", "PingFang SC", -apple-system, BlinkMacSystemFont, sans-serif;
|
|
|
|
|
|
background: linear-gradient(135deg, #f7fafc 0%, #e2e8f0 100%);
|
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
|
color: #1f2937;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.page {
|
|
|
|
|
|
max-width: 1000px;
|
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
|
padding: 32px 20px 60px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.header {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 18px;
|
|
|
|
|
|
padding: 28px;
|
|
|
|
|
|
box-shadow: 0 20px 60px rgba(79, 70, 229, 0.12);
|
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.title {
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.title h1 {
|
|
|
|
|
|
font-size: 30px;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
color: #111827;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.title p {
|
|
|
|
|
|
margin: 6px 0 0;
|
|
|
|
|
|
color: #4b5563;
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.upload-section {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-radius: 18px;
|
|
|
|
|
|
padding: 28px;
|
|
|
|
|
|
box-shadow: 0 20px 60px rgba(79, 70, 229, 0.12);
|
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #374151;
|
|
|
|
|
|
margin: 0 0 20px;
|
|
|
|
|
|
padding-bottom: 12px;
|
|
|
|
|
|
border-bottom: 2px solid #f3f4f6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.upload-area {
|
|
|
|
|
|
border: 2px dashed #d1d5db;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
padding: 48px 20px;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
background: #f9fafb;
|
|
|
|
|
|
transition: all 0.3s;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.upload-area:hover {
|
|
|
|
|
|
border-color: #6366f1;
|
|
|
|
|
|
background: #f0f9ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.upload-area.dragover {
|
|
|
|
|
|
border-color: #6366f1;
|
|
|
|
|
|
background: #eef2ff;
|
|
|
|
|
|
transform: scale(1.02);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.upload-icon {
|
|
|
|
|
|
font-size: 48px;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
color: #6366f1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.upload-text {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
color: #4b5563;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.upload-hint {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #9ca3af;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.upload-input {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-list {
|
|
|
|
|
|
margin-top: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
background: #f9fafb;
|
|
|
|
|
|
border: 1px solid #e5e7eb;
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-info {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-icon {
|
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
|
color: #6366f1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-details {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-name {
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #374151;
|
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-size {
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: #9ca3af;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-status {
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
padding: 4px 12px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: #dbeafe;
|
|
|
|
|
|
color: #1e40af;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-status.uploading {
|
|
|
|
|
|
background: #fef3c7;
|
|
|
|
|
|
color: #92400e;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-status.success {
|
|
|
|
|
|
background: #d1fae5;
|
|
|
|
|
|
color: #065f46;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-status.error {
|
|
|
|
|
|
background: #fee2e2;
|
|
|
|
|
|
color: #991b1b;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn {
|
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn-small {
|
|
|
|
|
|
padding: 6px 12px;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn-danger {
|
|
|
|
|
|
background: #fee2e2;
|
|
|
|
|
|
color: #991b1b;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn-danger:hover {
|
|
|
|
|
|
background: #fecaca;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn-primary {
|
|
|
|
|
|
background: #6366f1;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn-primary:hover {
|
|
|
|
|
|
background: #4f46e5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn:disabled {
|
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-group {
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-label {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #374151;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hint-icon {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 18px;
|
|
|
|
|
|
height: 18px;
|
|
|
|
|
|
margin-left: 6px;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
border: 1px solid #9ca3af;
|
|
|
|
|
|
color: #374151;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
background: #f9fafb;
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-select,
|
|
|
|
|
|
.form-input,
|
|
|
|
|
|
.form-textarea {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
|
border: 1px solid #d1d5db;
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-select:focus,
|
|
|
|
|
|
.form-input:focus,
|
|
|
|
|
|
.form-textarea:focus {
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
border-color: #6366f1;
|
|
|
|
|
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-textarea {
|
|
|
|
|
|
resize: vertical;
|
|
|
|
|
|
min-height: 80px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-help {
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: #6b7280;
|
|
|
|
|
|
margin-top: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.binding-options {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
|
|
|
|
gap: 20px;
|
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.binding-card {
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
border: 2px solid #e5e7eb;
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.binding-card:hover {
|
|
|
|
|
|
border-color: #6366f1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.binding-card.selected {
|
|
|
|
|
|
border-color: #6366f1;
|
|
|
|
|
|
background: #eef2ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.binding-radio {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.binding-title {
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #374151;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.binding-desc {
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: #6b7280;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.submit-section {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
margin-top: 24px;
|
|
|
|
|
|
padding-top: 24px;
|
|
|
|
|
|
border-top: 2px solid #f3f4f6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loading {
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: 40px 20px;
|
|
|
|
|
|
color: #6b7280;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loading-spinner {
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
border: 4px solid #f3f4f6;
|
|
|
|
|
|
border-top-color: #6366f1;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
animation: spin 0.8s linear infinite;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@keyframes spin {
|
|
|
|
|
|
to {
|
|
|
|
|
|
transform: rotate(360deg);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.error-message {
|
|
|
|
|
|
background: #fee2e2;
|
|
|
|
|
|
border: 1px solid #fecaca;
|
|
|
|
|
|
color: #991b1b;
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.success-message {
|
|
|
|
|
|
background: #d1fae5;
|
|
|
|
|
|
border: 1px solid #a7f3d0;
|
|
|
|
|
|
color: #065f46;
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
|
|
</head>
|
|
|
|
|
|
|
|
|
|
|
|
<body>
|
|
|
|
|
|
<div class="page">
|
|
|
|
|
|
<!-- 页面头部 -->
|
|
|
|
|
|
<div class="header">
|
|
|
|
|
|
<div class="title">
|
|
|
|
|
|
<h1>许可文件上传</h1>
|
|
|
|
|
|
<p>上传Excel格式的许可文件,可选择绑定到特定单位</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 上传区域 -->
|
|
|
|
|
|
<div class="upload-section">
|
|
|
|
|
|
<h2 class="section-title">1. 选择文件</h2>
|
|
|
|
|
|
|
|
|
|
|
|
<div id="uploadArea" class="upload-area">
|
|
|
|
|
|
<div class="upload-icon">📤</div>
|
|
|
|
|
|
<div class="upload-text">点击或拖拽文件到此处上传</div>
|
|
|
|
|
|
<div class="upload-hint">支持 .xlsx 格式,建议文件大小不超过 50MB</div>
|
|
|
|
|
|
<input type="file" id="fileInput" class="upload-input" accept=".xlsx,.xls" multiple />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div id="fileList" class="file-list" style="display: none;"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 绑定设置 -->
|
|
|
|
|
|
<div class="upload-section">
|
|
|
|
|
|
<h2 class="section-title">2. 绑定设置(可选)</h2>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label">
|
|
|
|
|
|
绑定单位
|
|
|
|
|
|
<span class="hint-icon" title="自动绑定到当前账号所属单位,该单位及下级可见,上级可下钻查看,横向同级不可见。">i</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<div class="binding-card selected"
|
|
|
|
|
|
style="display:flex; align-items:center; justify-content:space-between;">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div class="binding-title" id="boundDeptLabel">加载中...</div>
|
|
|
|
|
|
<div class="binding-desc">绑定到本单位,权限范围由组织层级决定</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="pill" id="boundDeptHint">自动绑定</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
|
<label class="form-label">上传说明(可选)</label>
|
|
|
|
|
|
<textarea id="uploadNote" class="form-textarea" placeholder="输入本次上传的说明信息..."></textarea>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 提交区域 -->
|
|
|
|
|
|
<div class="upload-section">
|
|
|
|
|
|
<div class="submit-section">
|
|
|
|
|
|
<button id="cancelBtn" class="btn" style="background: #e5e7eb; color: #374151;">取消</button>
|
|
|
|
|
|
<button id="submitBtn" class="btn btn-primary" disabled>开始上传</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 状态消息 -->
|
|
|
|
|
|
<div id="errorMessage" class="error-message" style="display: none;"></div>
|
|
|
|
|
|
<div id="successMessage" class="success-message" style="display: none;"></div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 上传进度 -->
|
|
|
|
|
|
<div id="uploadProgress" class="upload-section" style="display: none;">
|
|
|
|
|
|
<h2 class="section-title">上传进度</h2>
|
|
|
|
|
|
<div id="progressContent"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
// 全局变量
|
|
|
|
|
|
let selectedFiles = [];
|
|
|
|
|
|
let currentDepartment = null;
|
|
|
|
|
|
|
|
|
|
|
|
// DOM元素
|
|
|
|
|
|
const uploadArea = document.getElementById('uploadArea');
|
|
|
|
|
|
const fileInput = document.getElementById('fileInput');
|
|
|
|
|
|
const fileList = document.getElementById('fileList');
|
|
|
|
|
|
const submitBtn = document.getElementById('submitBtn');
|
|
|
|
|
|
const cancelBtn = document.getElementById('cancelBtn');
|
|
|
|
|
|
const errorMessage = document.getElementById('errorMessage');
|
|
|
|
|
|
const successMessage = document.getElementById('successMessage');
|
|
|
|
|
|
const boundDeptLabel = document.getElementById('boundDeptLabel');
|
|
|
|
|
|
const uploadProgress = document.getElementById('uploadProgress');
|
|
|
|
|
|
const progressContent = document.getElementById('progressContent');
|
|
|
|
|
|
|
|
|
|
|
|
// 页面初始化
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
|
|
|
|
loadCurrentUserDept();
|
|
|
|
|
|
bindEvents();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 绑定事件
|
|
|
|
|
|
function bindEvents() {
|
|
|
|
|
|
// 文件选择
|
|
|
|
|
|
fileInput.addEventListener('change', handleFileSelect);
|
|
|
|
|
|
|
|
|
|
|
|
// 拖拽上传
|
|
|
|
|
|
uploadArea.addEventListener('dragover', handleDragOver);
|
|
|
|
|
|
uploadArea.addEventListener('dragleave', handleDragLeave);
|
|
|
|
|
|
uploadArea.addEventListener('drop', handleDrop);
|
|
|
|
|
|
|
|
|
|
|
|
// 提交/取消
|
|
|
|
|
|
submitBtn.addEventListener('click', handleSubmit);
|
|
|
|
|
|
cancelBtn.addEventListener('click', handleCancel);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理文件选择
|
|
|
|
|
|
function handleFileSelect(e) {
|
|
|
|
|
|
const files = Array.from(e.target.files);
|
|
|
|
|
|
addFiles(files);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理拖拽悬停
|
|
|
|
|
|
function handleDragOver(e) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
uploadArea.classList.add('dragover');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理拖拽离开
|
|
|
|
|
|
function handleDragLeave(e) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
uploadArea.classList.remove('dragover');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理拖拽放下
|
|
|
|
|
|
function handleDrop(e) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
uploadArea.classList.remove('dragover');
|
|
|
|
|
|
|
|
|
|
|
|
const files = Array.from(e.dataTransfer.files);
|
|
|
|
|
|
addFiles(files);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加文件
|
|
|
|
|
|
function addFiles(files) {
|
|
|
|
|
|
// 过滤文件类型
|
|
|
|
|
|
const validFiles = files.filter(file => {
|
|
|
|
|
|
const validTypes = ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel'];
|
|
|
|
|
|
const validExt = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
|
|
|
|
|
|
|
|
|
|
|
|
if (!validTypes.includes(file.type) && !validExt) {
|
|
|
|
|
|
showError(`文件 "${file.name}" 不是有效的Excel格式`);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (file.size > 50 * 1024 * 1024) {
|
|
|
|
|
|
showError(`文件 "${file.name}" 超过50MB大小限制`);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
validFiles.forEach(file => {
|
|
|
|
|
|
selectedFiles.push({
|
|
|
|
|
|
file: file,
|
|
|
|
|
|
id: Date.now() + Math.random(),
|
|
|
|
|
|
status: 'pending'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
renderFileList();
|
|
|
|
|
|
updateSubmitButton();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 渲染文件列表
|
|
|
|
|
|
function renderFileList() {
|
|
|
|
|
|
if (selectedFiles.length === 0) {
|
|
|
|
|
|
fileList.style.display = 'none';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fileList.style.display = 'block';
|
|
|
|
|
|
fileList.innerHTML = selectedFiles.map(item => `
|
|
|
|
|
|
<div class="file-item" data-id="${item.id}">
|
|
|
|
|
|
<div class="file-info">
|
|
|
|
|
|
<div class="file-icon">📊</div>
|
|
|
|
|
|
<div class="file-details">
|
|
|
|
|
|
<div class="file-name">${escapeHtml(item.file.name)}</div>
|
|
|
|
|
|
<div class="file-size">${formatFileSize(item.file.size)}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="file-status ${item.status}">${getStatusText(item.status)}</div>
|
|
|
|
|
|
<div class="file-actions">
|
|
|
|
|
|
<button class="btn btn-small btn-danger" onclick="removeFile('${item.id}')">移除</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
`).join('');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 移除文件
|
|
|
|
|
|
function removeFile(id) {
|
|
|
|
|
|
selectedFiles = selectedFiles.filter(item => item.id !== id);
|
|
|
|
|
|
renderFileList();
|
|
|
|
|
|
updateSubmitButton();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function loadCurrentUserDept() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const resp = await fetch('/fs-ai-asistant/api/workflow/lawrisk/auth/me');
|
|
|
|
|
|
const data = await resp.json();
|
|
|
|
|
|
if (!data.authenticated) {
|
|
|
|
|
|
throw new Error('未登录');
|
|
|
|
|
|
}
|
|
|
|
|
|
const dept = data.user && data.user.department;
|
|
|
|
|
|
currentDepartment = dept;
|
|
|
|
|
|
if (dept && dept.name) {
|
|
|
|
|
|
boundDeptLabel.textContent = `${dept.name}${dept.code ? ' · ' + dept.code : ''}`;
|
|
|
|
|
|
boundDeptHint.textContent = '自动绑定';
|
|
|
|
|
|
submitBtn.disabled = false;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
boundDeptLabel.textContent = '未绑定单位';
|
|
|
|
|
|
boundDeptHint.textContent = '请先绑定';
|
|
|
|
|
|
submitBtn.disabled = true;
|
|
|
|
|
|
showError('当前账号未绑定单位,无法上传文件。');
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载当前用户失败:', error);
|
|
|
|
|
|
boundDeptLabel.textContent = '加载失败';
|
|
|
|
|
|
boundDeptHint.textContent = '请重新登录';
|
|
|
|
|
|
submitBtn.disabled = true;
|
|
|
|
|
|
showError('无法获取当前用户信息,请重新登录。');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理提交
|
|
|
|
|
|
async function handleSubmit() {
|
|
|
|
|
|
if (selectedFiles.length === 0) {
|
|
|
|
|
|
showError('请先选择要上传的文件');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const boundDepartmentId = currentDepartment ? currentDepartment.id : null;
|
|
|
|
|
|
const bindingMode = 'department';
|
|
|
|
|
|
|
|
|
|
|
|
if (!boundDepartmentId) {
|
|
|
|
|
|
showError('当前账号未绑定单位,请先绑定后再上传。');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 开始上传第一个文件
|
|
|
|
|
|
await uploadFile(0, boundDepartmentId, bindingMode);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 上传文件
|
|
|
|
|
|
async function uploadFile(index, boundDepartmentId, bindingMode) {
|
|
|
|
|
|
if (index >= selectedFiles.length) {
|
|
|
|
|
|
// 所有文件上传完成
|
|
|
|
|
|
showSuccess(`成功上传 ${selectedFiles.length} 个文件`);
|
|
|
|
|
|
resetForm();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const item = selectedFiles[index];
|
|
|
|
|
|
item.status = 'uploading';
|
|
|
|
|
|
renderFileList();
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
|
formData.append('file', item.file);
|
|
|
|
|
|
|
|
|
|
|
|
// 添加绑定信息
|
|
|
|
|
|
if (boundDepartmentId) {
|
|
|
|
|
|
formData.append('bound_department_id', boundDepartmentId);
|
|
|
|
|
|
}
|
|
|
|
|
|
formData.append('binding_mode', bindingMode || 'auto');
|
|
|
|
|
|
|
|
|
|
|
|
const response = await fetch('/fs-ai-asistant/api/workflow/lawrisk/admin/permit-import/upload', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
body: formData
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (!data.success) {
|
|
|
|
|
|
throw new Error(data.message || '上传失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
item.status = 'success';
|
|
|
|
|
|
renderFileList();
|
|
|
|
|
|
|
|
|
|
|
|
// 显示上传进度
|
|
|
|
|
|
showProgress(index + 1, selectedFiles.length, item.file.name);
|
|
|
|
|
|
|
|
|
|
|
|
// 继续上传下一个文件
|
|
|
|
|
|
setTimeout(() => uploadFile(index + 1, boundDepartmentId, bindingMode), 1000);
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('上传失败:', error);
|
|
|
|
|
|
item.status = 'error';
|
|
|
|
|
|
renderFileList();
|
|
|
|
|
|
showError(`文件 "${item.file.name}" 上传失败: ` + error.message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 显示上传进度
|
|
|
|
|
|
function showProgress(current, total, filename) {
|
|
|
|
|
|
uploadProgress.style.display = 'block';
|
|
|
|
|
|
progressContent.innerHTML = `
|
|
|
|
|
|
<div style="margin-bottom: 12px;">正在上传: ${escapeHtml(filename)} (${current}/${total})</div>
|
|
|
|
|
|
<div style="width: 100%; height: 8px; background: #f3f4f6; border-radius: 4px; overflow: hidden;">
|
|
|
|
|
|
<div style="width: ${(current / total) * 100}%; height: 100%; background: #6366f1; transition: width 0.3s;"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理取消
|
|
|
|
|
|
function handleCancel() {
|
|
|
|
|
|
if (confirm('确定要取消上传吗?已选择的文件将被清除。')) {
|
|
|
|
|
|
resetForm();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重置表单
|
|
|
|
|
|
function resetForm() {
|
|
|
|
|
|
selectedFiles = [];
|
|
|
|
|
|
fileInput.value = '';
|
|
|
|
|
|
fileList.style.display = 'none';
|
|
|
|
|
|
uploadProgress.style.display = 'none';
|
|
|
|
|
|
submitBtn.disabled = true;
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById('uploadNote').value = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新提交按钮状态
|
|
|
|
|
|
function updateSubmitButton() {
|
|
|
|
|
|
submitBtn.disabled = selectedFiles.length === 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取状态文本
|
|
|
|
|
|
function getStatusText(status) {
|
|
|
|
|
|
const statusMap = {
|
|
|
|
|
|
'pending': '等待上传',
|
|
|
|
|
|
'uploading': '上传中...',
|
|
|
|
|
|
'success': '上传成功',
|
|
|
|
|
|
'error': '上传失败'
|
|
|
|
|
|
};
|
|
|
|
|
|
return statusMap[status] || status;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 格式化文件大小
|
|
|
|
|
|
function 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';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// HTML转义
|
|
|
|
|
|
function escapeHtml(text) {
|
|
|
|
|
|
if (!text) return '';
|
|
|
|
|
|
const div = document.createElement('div');
|
|
|
|
|
|
div.textContent = text;
|
|
|
|
|
|
return div.innerHTML;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 显示错误信息
|
|
|
|
|
|
function showError(message) {
|
|
|
|
|
|
errorMessage.textContent = message;
|
|
|
|
|
|
errorMessage.style.display = 'block';
|
|
|
|
|
|
setTimeout(() => errorMessage.style.display = 'none', 5000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 显示成功信息
|
|
|
|
|
|
function showSuccess(message) {
|
|
|
|
|
|
successMessage.textContent = message;
|
|
|
|
|
|
successMessage.style.display = 'block';
|
|
|
|
|
|
setTimeout(() => successMessage.style.display = 'none', 5000);
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
</body>
|
|
|
|
|
|
|
2025-12-20 11:25:34 +08:00
|
|
|
|
</html>
|