Files
TEMP_spec_system/templates/create_temp_spec.html
beabigegg d1d68e66a7 Ok
2025-07-29 20:24:40 +08:00

333 lines
14 KiB
HTML
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.

{% extends "base.html" %}
{% block title %}暫時規範建立 - 暫時規範系統{% endblock %}
{% block head %}
{{ super() }}
<link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor.min.css" />
<link rel="stylesheet" href="https://uicdn.toast.com/tui-image-editor/latest/tui-image-editor.css" />
<style>
#tui-image-editor-container {
width: 100%;
height: 80vh; /* 增加高度佔比 */
}
.modal-dialog.modal-xl {
max-width: 98vw;
height: 90vh;
}
.modal-content {
height: 100%;
}
.modal-body {
height: 100%;
padding: 0;
overflow: hidden;
}
#tui-image-editor-container .tui-image-editor {
height: 100% !important;
}
</style>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12">
<h2 class="mb-4">暫時規範建立</h2>
<div class="card">
<div class="card-body">
<form id="spec-form" method="post">
<div class="row">
<div class="col-md-6 mb-3">
<label for="serial_number" class="form-label">暫時規範編號</label>
<input type="text" class="form-control" id="serial_number" name="serial_number" value="{{ next_spec_code }}" readonly>
</div>
<div class="col-md-6 mb-3">
<label for="theme" class="form-label">主題/目的</label>
<input type="text" class="form-control" id="theme" name="theme" required>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="applicant" class="form-label">申請者</label>
<input type="text" class="form-control" id="applicant" name="applicant" required>
</div>
<div class="col-md-6 mb-3">
<label for="applicant_phone" class="form-label">電話(分機)</label>
<input type="text" class="form-control" id="applicant_phone" name="applicant_phone">
</div>
</div>
<div class="mb-3">
<label class="form-label">站別 (可多選)</label>
<div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="點測"> <label>點測</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="切割"> <label>切割</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="晶粒黏著"> <label>晶粒黏著</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="銲線黏著"> <label>銲線黏著</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="錫膏焊接"> <label>錫膏焊接</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="成型"> <label>成型</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="去膠"> <label>去膠</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="吹砂"> <label>吹砂</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="電鍍"> <label>電鍍</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="切彎腳"> <label>切彎腳</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="印字"> <label>印字</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="測試"> <label>測試</label></div>
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="其他" id="station_other_checkbox"> <label>其他</label></div>
</div>
<div class="mt-2" id="station_other_input_div" style="display: none;"><input type="text" class="form-control" name="station_other" placeholder="請輸入其他站別"></div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">TCCS</label>
<div class="input-group">
<select class="form-select" name="tccs_level"><option selected>請選擇 Level...</option><option value="Level 1">Level 1</option><option value="Level 2">Level 2</option><option value="Level 3">Level 3</option><option value="Level 4">Level 4</option></select>
<div class="input-group-text">
<div class="form-check form-check-inline mb-0"><input class="form-check-input" type="radio" name="tccs_4m" value="人"> <label></label></div>
<div class="form-check form-check-inline mb-0"><input class="form-check-input" type="radio" name="tccs_4m" value="機"> <label></label></div>
<div class="form-check form-check-inline mb-0"><input class="form-check-input" type="radio" name="tccs_4m" value="料"> <label></label></div>
<div class="form-check form-check-inline mb-0"><input class="form-check-input" type="radio" name="tccs_4m" value="法"> <label></label></div>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<label for="start_date" class="form-label">實施起始日</label>
<input type="date" class="form-control" id="start_date" name="start_date" required>
</div>
</div>
<div class="row">
<div class="col-md-4 mb-3"><label for="package" class="form-label">Package</label><input type="text" class="form-control" id="package" name="package"></div>
<div class="col-md-4 mb-3"><label for="lot_number" class="form-label">工單批號</label><input type="text" class="form-control" id="lot_number" name="lot_number"></div>
<div class="col-md-4 mb-3"><label for="equipment_type" class="form-label">設備型(編)號</label><input type="text" class="form-control" id="equipment_type" name="equipment_type"></div>
</div>
<div class="mb-3">
<label class="form-label">變更前內容</label>
<div id="editor-before"></div>
<textarea name="change_before" id="textarea-before" style="display: none;"></textarea>
</div>
<div class="mb-3">
<label class="form-label">變更後內容</label>
<div id="editor-after"></div>
<textarea name="change_after" id="textarea-after" style="display: none;"></textarea>
</div>
<div class="mb-3">
<label for="data_needs" class="form-label">資料收集需求</label>
<textarea class="form-control" id="data_needs" name="data_needs" rows="3" required></textarea>
</div>
<div class="d-flex justify-content-end">
<button type="button" id="preview-btn" class="btn btn-info me-2">預覽</button>
<button type="submit" class="btn btn-primary">建立暫時規範</button>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="modal fade" id="imageEditorModal" tabindex="-1" aria-labelledby="imageEditorModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered modal-fullscreen">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="imageEditorModalLabel">編輯圖片</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="tui-image-editor-container"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="save-image-btn">儲存變更</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
<script src="https://uicdn.toast.com/tui-image-editor/latest/tui-image-editor.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const imageEditorModal = new bootstrap.Modal(document.getElementById('imageEditorModal'));
const Editor = toastui.Editor;
const ImageEditor = tui.ImageEditor;
let activeEditorInstance = null;
let imageEditorInstance = null;
let targetImageElement = null;
// 上傳圖片至後端,回傳正式 URL
const uploadImage = async (blob) => {
const formData = new FormData();
formData.append('file', blob, 'edited-image.png');
try {
const response = await fetch("{{ url_for('upload.upload_image') }}", {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error(`Upload failed: ${response.statusText}`);
const result = await response.json();
return result.location;
} catch (error) {
console.error(error);
alert('圖片上傳失敗,請檢查網路或聯絡管理員。');
return null;
}
};
// 判斷圖片來源是否為 blob/base64
const isBlobOrBase64 = (url) => url.startsWith('data:') || url.startsWith('blob:');
// 啟動圖片編輯器(支援本地圖片自動轉正式 URL
const launchImageEditor = async (rawSrc) => {
let imageUrl = rawSrc;
if (isBlobOrBase64(rawSrc)) {
try {
const blob = await (await fetch(rawSrc)).blob();
const uploadedUrl = await uploadImage(blob);
if (!uploadedUrl) return;
imageUrl = uploadedUrl;
} catch (err) {
alert("圖片格式無法載入編輯器。");
return;
}
}
if (imageEditorInstance) imageEditorInstance.destroy();
imageEditorInstance = new ImageEditor('#tui-image-editor-container', {
includeUI: {
loadImage: {
path: imageUrl,
name: 'image'
},
menuBarPosition: 'bottom'
},
cssMaxWidth: 1200,
cssMaxHeight: 800,
usageStatistics: false
});
imageEditorModal.show();
};
// 建立 Markdown 編輯器並綁定圖片點擊
const createEditor = (containerId) => {
const editor = new Editor({
el: document.querySelector(containerId),
height: '300px',
initialEditType: 'wysiwyg',
previewStyle: 'vertical',
hooks: {
addImageBlobHook: async (blob, callback) => {
const newUrl = await uploadImage(blob);
if (newUrl) callback(newUrl, 'image');
}
}
});
// 綁定圖片點擊 → 進入編輯器
editor.getEditorElements().wwEditor.addEventListener('click', (event) => {
if (event.target.tagName === 'IMG') {
activeEditorInstance = editor;
targetImageElement = event.target;
launchImageEditor(event.target.src);
}
});
return editor;
};
const editorBefore = createEditor('#editor-before');
const editorAfter = createEditor('#editor-after');
// 儲存按鈕處理編輯後圖片
document.getElementById('save-image-btn').addEventListener('click', async () => {
if (!imageEditorInstance) return;
const dataURL = imageEditorInstance.toDataURL({ multiplier: 2 });
const blob = await (await fetch(dataURL)).blob();
const newUrl = await uploadImage(blob);
if (newUrl && activeEditorInstance && targetImageElement) {
targetImageElement.src = newUrl;
// 強制同步內容讓 Markdown 也更新
const updatedHtml = activeEditorInstance.getHTML();
activeEditorInstance.setHTML(updatedHtml);
}
imageEditorModal.hide();
});
// 表單送出前同步 Markdown 內容
document.getElementById('spec-form').addEventListener('submit', function () {
if (editorBefore) document.getElementById('textarea-before').value = editorBefore.getMarkdown();
if (editorAfter) document.getElementById('textarea-after').value = editorAfter.getMarkdown();
});
// 預覽產生邏輯
document.getElementById('preview-btn').addEventListener('click', async function () {
let isPreviewing = false;
if (isPreviewing) return;
isPreviewing = true;
this.disabled = true;
this.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> 產生中...';
const form = document.getElementById('spec-form');
const formData = new FormData(form);
if (editorBefore) formData.set('change_before', editorBefore.getMarkdown());
if (editorAfter) formData.set('change_after', editorAfter.getMarkdown());
let data = Object.fromEntries(formData.entries());
const stations = Array.from(form.querySelectorAll('input[name="station"]:checked')).map(el => el.value);
if (stations.includes('其他') && data.station_other) {
stations[stations.indexOf('其他')] = data.station_other;
}
data.station = stations.join(', ');
data.tccs_info = data.tccs_level ? `${data.tccs_level}${data.tccs_4m ? ' (' + data.tccs_4m + ')' : ''}` : '';
try {
const response = await fetch("{{ url_for('temp_spec.preview_spec') }}", {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!response.ok) throw new Error(`Server error: ${response.status}`);
const pdfBlob = await response.blob();
const pdfUrl = URL.createObjectURL(pdfBlob);
window.open(pdfUrl, '_blank');
} catch (error) {
console.error(error);
alert('預覽失敗,請檢查表單內容或網路。');
} finally {
isPreviewing = false;
this.disabled = false;
this.innerHTML = '預覽';
}
});
// 顯示其他站別欄位
document.getElementById('station_other_checkbox').addEventListener('change', function () {
document.getElementById('station_other_input_div').style.display = this.checked ? 'block' : 'none';
});
});
</script>
{% endblock %}