| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- {% extends "base.html" %}
- {% block title %}{% if renew %}续签{% elif contract %}编辑{% else %}新建{% endif %}合同{% endblock %}
- {% block content %}
- <div class="d-flex justify-content-between align-items-center mb-4">
- <h2 class="fw-bold">{% if renew %}续签{% elif contract %}编辑{% else %}新建{% endif %}合同</h2>
- <a href="{{ url_for('contract_list') }}" class="btn btn-outline-secondary">返回列表</a>
- </div>
- <div class="card shadow-sm">
- <div class="card-body">
- <form method="POST" enctype="multipart/form-data">
- <div class="row g-3">
- <!-- 合同类型 -->
- <div class="col-md-6">
- <label class="form-label fw-semibold">合同类型</label>
- <select class="form-select" name="type_id" id="type-select" required>
- <option value="">选择合同类型</option>
- {% for type in contract_types %}
- <option value="{{ type.id }}"
- {% if contract and contract.type_id == type.id %}selected
- {% elif renew and original_contract is defined and original_contract.type_id == type.id %}selected{% endif %}>
- {{ type.name }}
- </option>
- {% endfor %}
- </select>
- </div>
- <!-- 合同编号 -->
- <div class="col-md-6">
- <label class="form-label fw-semibold">合同编号</label>
- {% if renew %}
- <input type="text" class="form-control" name="contract_number" id="contract-number" value="自动生成" readonly>
- <div class="form-text">续签时编号将在保存后自动生成</div>
- {% else %}
- <input type="text" class="form-control" name="contract_number" id="contract-number"
- value="{{ contract.contract_number if contract else default_number }}" required>
- <div class="form-text">系统已自动生成编号,您也可以自行修改</div>
- {% endif %}
- </div>
- <!-- 合同名称 -->
- <div class="col-md-6">
- <label class="form-label fw-semibold">合同名称</label>
- <input type="text" class="form-control" name="name"
- value="{{ contract.name if contract else '' }}" required>
- </div>
- <!-- 我方主体 -->
- <div class="col-md-6">
- <label class="form-label fw-semibold">我方主体</label>
- <select class="form-select" name="company_entity_id" required>
- <option value="">选择我方主体</option>
- {% for entity in company_entities %}
- <option value="{{ entity.id }}"
- {% if contract and contract.company_entity_id == entity.id %}selected
- {% elif renew and original_contract is defined and original_contract.company_entity_id == entity.id %}selected{% endif %}>
- {{ entity.name }}
- </option>
- {% endfor %}
- </select>
- </div>
- <!-- 开始日期 -->
- <div class="col-md-6">
- <label class="form-label fw-semibold">开始日期</label>
- <input type="date" class="form-control" id="start-date" name="start_date"
- value="{{ contract.start_date.strftime('%Y-%m-%d') if contract else '' }}" required>
- </div>
- <!-- 结束日期 -->
- <div class="col-md-6">
- <label class="form-label fw-semibold">结束日期</label>
- <input type="date" class="form-control" id="end-date" name="end_date"
- value="{{ contract.end_date.strftime('%Y-%m-%d') if contract else '' }}" required>
- </div>
- <!-- 签约方式 -->
- <div class="col-md-6">
- <label class="form-label fw-semibold">签约方式</label>
- {% set signing_method_value = contract.signing_method if contract else (original_contract.signing_method if renew and original_contract is defined else '纸质签') %}
- <select class="form-select" name="signing_method" required>
- <option value="纸质签" {% if signing_method_value == '纸质签' %}selected{% endif %}>纸质签</option>
- <option value="电子签" {% if signing_method_value == '电子签' %}selected{% endif %}>电子签</option>
- <option value="电子签+纸质签" {% if signing_method_value == '电子签+纸质签' %}selected{% endif %}>电子签+纸质签</option>
- </select>
- </div>
- <!-- 合同收回时间 -->
- <div class="col-md-6">
- <label class="form-label fw-semibold">合同收回时间</label>
- <input type="date" class="form-control" name="collected_date"
- value="{{ contract.collected_date.strftime('%Y-%m-%d') if contract and contract.collected_date else (original_contract.collected_date.strftime('%Y-%m-%d') if renew and original_contract and original_contract.collected_date else '') }}">
- </div>
- <!-- 存储盒名称 -->
- <div class="col-md-6">
- <label class="form-label fw-semibold">存储盒名称</label>
- <input type="text" class="form-control" name="storage_box"
- value="{{ contract.storage_box if contract else (original_contract.storage_box if renew and original_contract is defined else '') }}">
- </div>
- <!-- 对方主体 -->
- <div class="col-12">
- <label class="form-label fw-semibold">对方主体(可多个)</label>
- <div id="counterparties-container">
- {% set counterparties = contract.counterparties if contract else (original_contract.counterparties if renew and original_contract is defined else []) %}
- {% if counterparties %}
- {% for cp in counterparties %}
- <div class="input-group mb-2">
- <input type="text" class="form-control" name="counterparty_name" value="{{ cp.name }}">
- <button type="button" class="btn btn-outline-danger remove-counterparty">删除</button>
- </div>
- {% endfor %}
- {% else %}
- <div class="input-group mb-2">
- <input type="text" class="form-control" name="counterparty_name">
- <button type="button" class="btn btn-outline-danger remove-counterparty">删除</button>
- </div>
- {% endif %}
- </div>
- <button type="button" id="add-counterparty" class="btn btn-sm btn-outline-secondary mt-1">添加对方主体</button>
- </div>
- <!-- 提前提醒天数 -->
- <div class="col-md-6">
- <label class="form-label fw-semibold">到期前提醒(天)</label>
- <input type="number" class="form-control" id="remind-before" name="remind_before"
- value="{{ contract.remind_before if contract else 30 }}" min="1" max="365">
- </div>
- <!-- 备注 -->
- <div class="col-12">
- <label class="form-label fw-semibold">备注</label>
- <textarea class="form-control" name="notes" rows="3">{{ contract.notes if contract else '' }}</textarea>
- </div>
- <!-- 附件上传(已修复) -->
- <div class="col-12">
- <label class="form-label fw-semibold">附件</label>
- <input type="file" class="form-control" name="new_attachments" multiple
- accept=".pdf,.jpg,.jpeg,.png,.gif,.bmp,.tiff,.webp,.doc,.docx,.xls,.xlsx,.ppt,.pptx">
- <div class="form-text">仅支持 PDF、图片、Office 文件,单个不超过 5MB</div>
- {% if attachments %}
- <div class="mt-3">
- <label class="form-label fw-semibold">已上传附件</label>
- <ul class="list-group">
- {% for att in attachments %}
- <!-- 修改附件链接 -->
- <li class="list-group-item d-flex justify-content-between align-items-center">
- <div>
- <i class="bi bi-paperclip me-2"></i>
- <a href="#" onclick="previewAttachment('{{ url_for('download_file', attachment_id=att.id) }}', '{{ att.filename }}')">
- {{ att.filename }}
- </a>
- </div>
- <div>
- <input type="checkbox" name="delete_attachment" value="{{ att.id }}" class="form-check-input me-1">
- <label>删除</label>
- </div>
- </li>
- {% endfor %}
- </ul>
- </div>
- {% endif %}
- </div>
- </div>
- <!-- 提交按钮 -->
- <div class="d-grid gap-2 mt-4">
- <button type="submit" class="btn btn-primary btn-lg">{{ '续签合同' if renew else '保存合同' }}</button>
- </div>
- </form>
- </div>
- </div>
- <script>
- document.addEventListener('DOMContentLoaded', function() {
- const typeSelect = document.getElementById('type-select');
- const contractInput = document.getElementById('contract-number');
- const startDateInput = document.getElementById('start-date');
- const endDateInput = document.getElementById('end-date');
- const fileInput = document.querySelector('input[name="new_attachments"]');
- const addCounterpartyBtn = document.getElementById('add-counterparty');
- const counterpartiesContainer = document.getElementById('counterparties-container');
- const form = document.querySelector('form');
- // 文件校验(支持 PDF、图片、Word、Excel、PowerPoint)
- function validateFileType(input) {
- const validTypes = [
- 'application/pdf',
- 'image/jpeg',
- 'image/png',
- 'image/gif',
- 'image/bmp',
- 'image/tiff',
- 'image/webp',
- 'application/msword',
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
- 'application/vnd.ms-excel',
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
- 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
- ];
- const files = input.files;
- for (let i = 0; i < files.length; i++) {
- if (!validTypes.includes(files[i].type)) {
- alert(`文件 "${files[i].name}" 不是有效的 PDF、图片、Word、Excel 或 PowerPoint 文件`);
- input.value = '';
- return false;
- }
- if (files[i].size > 5 * 1024 * 1024) { // 最大 5MB
- alert(`文件 "${files[i].name}" 超过 5MB`);
- input.value = '';
- return false;
- }
- }
- return true;
- }
- form.addEventListener('submit', function(e) {
- if (fileInput.files.length > 0 && !validateFileType(fileInput)) e.preventDefault();
- });
- fileInput.addEventListener('change', () => validateFileType(fileInput));
- // 合同编号自动生成
- async function updateContractNumber(typeId) {
- if (!typeId || contractInput.readOnly) return;
- try {
- const res = await fetch(`/contract/generate_number/${typeId}`);
- const data = await res.json();
- if (!contractInput.dataset.userEdited) contractInput.value = data.number;
- } catch (err) {
- console.error('获取合同编号失败', err);
- }
- }
- typeSelect.addEventListener('change', function() { updateContractNumber(this.value); });
- contractInput.addEventListener('input', function(){ this.dataset.userEdited='true'; });
- // 自动结束日期 = 开始日期 +1年
- startDateInput.addEventListener('change', () => {
- if (!endDateInput.value) {
- const startDate = new Date(startDateInput.value);
- if (!isNaN(startDate)) {
- startDate.setFullYear(startDate.getFullYear() + 1);
- endDateInput.value = startDate.toISOString().split('T')[0];
- }
- }
- });
- // 对方主体增删
- function addCounterpartyField(value='') {
- const div = document.createElement('div');
- div.className = 'input-group mb-2';
- div.innerHTML = `<input type="text" class="form-control" name="counterparty_name" value="${value}"><button type="button" class="btn btn-outline-danger remove-counterparty">删除</button>`;
- counterpartiesContainer.appendChild(div);
- }
- addCounterpartyBtn.addEventListener('click', () => addCounterpartyField());
- counterpartiesContainer.addEventListener('click', function(e) {
- if (e.target.classList.contains('remove-counterparty')) e.target.parentElement.remove();
- });
- });
- </script>
- {% endblock %}
|