mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 02:22:26 +08:00
🎨 將工作區的圖片設定轉移到設定頁籤
This commit is contained in:
parent
7dc9aa510a
commit
e8d4068229
@ -320,6 +320,7 @@
|
||||
"settings": {
|
||||
"title": "Image Settings",
|
||||
"sizeLimit": "Image Size Limit",
|
||||
"sizeLimitDesc": "Set the maximum file size limit for uploaded images",
|
||||
"sizeLimitOptions": {
|
||||
"unlimited": "Unlimited",
|
||||
"1mb": "1MB",
|
||||
|
@ -320,6 +320,7 @@
|
||||
"settings": {
|
||||
"title": "图片设置",
|
||||
"sizeLimit": "图片大小限制",
|
||||
"sizeLimitDesc": "设定上传图片的最大文件大小限制",
|
||||
"sizeLimitOptions": {
|
||||
"unlimited": "无限制",
|
||||
"1mb": "1MB",
|
||||
|
@ -325,6 +325,7 @@
|
||||
"settings": {
|
||||
"title": "圖片設定",
|
||||
"sizeLimit": "圖片大小限制",
|
||||
"sizeLimitDesc": "設定上傳圖片的最大檔案大小限制",
|
||||
"sizeLimitOptions": {
|
||||
"unlimited": "無限制",
|
||||
"1mb": "1MB",
|
||||
|
@ -554,6 +554,100 @@ body {
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.settings-card .setting-warning {
|
||||
font-size: 12px;
|
||||
color: var(--warning-color);
|
||||
margin-top: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 圖片大小限制選擇器樣式 */
|
||||
.image-size-limit-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.image-size-limit-select {
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
color: var(--text-primary);
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
min-width: 120px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.image-size-limit-select:hover {
|
||||
border-color: var(--accent-color);
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.image-size-limit-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent-color);
|
||||
box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.2);
|
||||
}
|
||||
|
||||
/* Base64 切換器容器樣式 */
|
||||
.base64-toggle-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 現代化切換開關樣式 */
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toggle-input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--border-color);
|
||||
border-radius: 24px;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.toggle-slider:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
left: 3px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.toggle-input:checked + .toggle-slider {
|
||||
background: var(--accent-color);
|
||||
}
|
||||
|
||||
.toggle-input:checked + .toggle-slider:before {
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
.toggle-switch:hover .toggle-slider {
|
||||
box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.2);
|
||||
}
|
||||
|
||||
/* 佈局模式選擇器 */
|
||||
.layout-mode-selector {
|
||||
display: flex;
|
||||
@ -1102,106 +1196,9 @@ body {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* 圖片設定區域樣式 */
|
||||
.image-settings-details {
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 8px;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.image-settings-details:hover {
|
||||
border-color: rgba(0, 122, 204, 0.5);
|
||||
box-shadow: 0 2px 8px rgba(0, 122, 204, 0.1);
|
||||
}
|
||||
|
||||
.image-settings-summary {
|
||||
padding: 8px 12px;
|
||||
background: var(--bg-secondary);
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
border: none;
|
||||
outline: none;
|
||||
transition: all 0.3s ease;
|
||||
list-style: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.image-settings-summary:hover {
|
||||
background: var(--surface-color);
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.image-settings-summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 箭頭指示器 */
|
||||
.image-settings-summary::after {
|
||||
content: '▼';
|
||||
font-size: 10px;
|
||||
color: var(--text-secondary);
|
||||
transition: all 0.3s ease;
|
||||
transform-origin: center;
|
||||
display: inline-block;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.image-settings-summary:hover::after {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
/* 展開狀態的箭頭 */
|
||||
.image-settings-details[open] .image-settings-summary::after {
|
||||
transform: rotate(180deg);
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
/* 展開狀態的 summary 樣式 */
|
||||
.image-settings-details[open] .image-settings-summary {
|
||||
background: var(--surface-color);
|
||||
color: var(--accent-color);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.image-settings-content {
|
||||
padding: 12px;
|
||||
background: var(--bg-tertiary);
|
||||
animation: slideDown 0.3s ease-out;
|
||||
}
|
||||
|
||||
/* 展開內容的滑入動畫 */
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 圖片設定行 */
|
||||
.image-setting-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.image-setting-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.image-setting-label {
|
||||
font-size: 12px;
|
||||
|
@ -0,0 +1,543 @@
|
||||
/**
|
||||
* 現代化檔案上傳管理器
|
||||
* 使用事件委託模式,避免重複事件監聽器問題
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// 確保命名空間存在
|
||||
if (!window.MCPFeedback) {
|
||||
window.MCPFeedback = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 檔案上傳管理器建構函數
|
||||
*/
|
||||
function FileUploadManager(options) {
|
||||
options = options || {};
|
||||
|
||||
// 配置選項
|
||||
this.maxFileSize = options.maxFileSize || 0; // 0 表示無限制
|
||||
this.enableBase64Detail = options.enableBase64Detail || false;
|
||||
this.acceptedTypes = options.acceptedTypes || 'image/*';
|
||||
this.maxFiles = options.maxFiles || 10;
|
||||
|
||||
// 狀態管理
|
||||
this.files = [];
|
||||
this.isInitialized = false;
|
||||
this.debounceTimeout = null;
|
||||
this.lastClickTime = 0;
|
||||
this.isProcessingClick = false;
|
||||
this.lastClickTime = 0;
|
||||
|
||||
// 事件回調
|
||||
this.onFileAdd = options.onFileAdd || null;
|
||||
this.onFileRemove = options.onFileRemove || null;
|
||||
this.onSettingsChange = options.onSettingsChange || null;
|
||||
|
||||
// 綁定方法上下文
|
||||
this.handleDelegatedEvent = this.handleDelegatedEvent.bind(this);
|
||||
this.handleGlobalPaste = this.handleGlobalPaste.bind(this);
|
||||
|
||||
console.log('📁 FileUploadManager 初始化完成');
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化檔案上傳管理器
|
||||
*/
|
||||
FileUploadManager.prototype.initialize = function() {
|
||||
if (this.isInitialized) {
|
||||
console.warn('⚠️ FileUploadManager 已經初始化過了');
|
||||
return;
|
||||
}
|
||||
|
||||
this.setupEventDelegation();
|
||||
this.setupGlobalPasteHandler();
|
||||
this.isInitialized = true;
|
||||
|
||||
console.log('✅ FileUploadManager 事件委託設置完成');
|
||||
};
|
||||
|
||||
/**
|
||||
* 設置事件委託
|
||||
* 使用單一事件監聽器處理所有檔案上傳相關事件
|
||||
*/
|
||||
FileUploadManager.prototype.setupEventDelegation = function() {
|
||||
// 移除舊的事件監聽器
|
||||
document.removeEventListener('click', this.handleDelegatedEvent);
|
||||
document.removeEventListener('dragover', this.handleDelegatedEvent);
|
||||
document.removeEventListener('dragleave', this.handleDelegatedEvent);
|
||||
document.removeEventListener('drop', this.handleDelegatedEvent);
|
||||
document.removeEventListener('change', this.handleDelegatedEvent);
|
||||
|
||||
// 設置新的事件委託
|
||||
document.addEventListener('click', this.handleDelegatedEvent);
|
||||
document.addEventListener('dragover', this.handleDelegatedEvent);
|
||||
document.addEventListener('dragleave', this.handleDelegatedEvent);
|
||||
document.addEventListener('drop', this.handleDelegatedEvent);
|
||||
document.addEventListener('change', this.handleDelegatedEvent);
|
||||
};
|
||||
|
||||
/**
|
||||
* 處理委託事件
|
||||
*/
|
||||
FileUploadManager.prototype.handleDelegatedEvent = function(event) {
|
||||
const target = event.target;
|
||||
|
||||
// 處理檔案移除按鈕點擊
|
||||
const removeBtn = target.closest('.image-remove-btn');
|
||||
if (removeBtn) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.handleRemoveFile(removeBtn);
|
||||
return;
|
||||
}
|
||||
|
||||
// 處理檔案輸入變更
|
||||
if (target.type === 'file' && event.type === 'change') {
|
||||
this.handleFileInputChange(target, event);
|
||||
return;
|
||||
}
|
||||
|
||||
// 處理上傳區域事件 - 只處理直接點擊上傳區域的情況
|
||||
const uploadArea = target.closest('.image-upload-area');
|
||||
if (uploadArea && event.type === 'click') {
|
||||
// 確保不是點擊 input 元素本身
|
||||
if (target.type === 'file') {
|
||||
return;
|
||||
}
|
||||
|
||||
// 確保不是點擊預覽圖片或移除按鈕
|
||||
if (target.closest('.image-preview-item') || target.closest('.image-remove-btn')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.handleUploadAreaClick(uploadArea, event);
|
||||
return;
|
||||
}
|
||||
|
||||
// 處理拖放事件
|
||||
if (uploadArea && (event.type === 'dragover' || event.type === 'dragleave' || event.type === 'drop')) {
|
||||
switch (event.type) {
|
||||
case 'dragover':
|
||||
this.handleDragOver(uploadArea, event);
|
||||
break;
|
||||
case 'dragleave':
|
||||
this.handleDragLeave(uploadArea, event);
|
||||
break;
|
||||
case 'drop':
|
||||
this.handleDrop(uploadArea, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 處理上傳區域點擊(使用防抖機制)
|
||||
*/
|
||||
FileUploadManager.prototype.handleUploadAreaClick = function(uploadArea, event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// 強力防抖機制 - 防止無限循環
|
||||
const now = Date.now();
|
||||
if (this.lastClickTime && (now - this.lastClickTime) < 500) {
|
||||
console.log('🚫 防抖:忽略重複點擊,間隔:', now - this.lastClickTime, 'ms');
|
||||
return;
|
||||
}
|
||||
this.lastClickTime = now;
|
||||
|
||||
// 如果已經有待處理的點擊,忽略新的點擊
|
||||
if (this.isProcessingClick) {
|
||||
console.log('🚫 正在處理點擊,忽略新的點擊');
|
||||
return;
|
||||
}
|
||||
|
||||
this.isProcessingClick = true;
|
||||
|
||||
const fileInput = uploadArea.querySelector('input[type="file"]');
|
||||
if (fileInput) {
|
||||
console.log('🖱️ 觸發檔案選擇:', fileInput.id);
|
||||
|
||||
// 重置 input 值以確保可以重複選擇同一檔案
|
||||
fileInput.value = '';
|
||||
|
||||
// 使用 setTimeout 確保在下一個事件循環中執行,避免事件冒泡問題
|
||||
const self = this;
|
||||
setTimeout(function() {
|
||||
try {
|
||||
fileInput.click();
|
||||
console.log('✅ 檔案選擇對話框已觸發');
|
||||
} catch (error) {
|
||||
console.error('❌ 檔案選擇對話框觸發失敗:', error);
|
||||
} finally {
|
||||
// 重置處理狀態
|
||||
setTimeout(function() {
|
||||
self.isProcessingClick = false;
|
||||
}, 100);
|
||||
}
|
||||
}, 50);
|
||||
} else {
|
||||
this.isProcessingClick = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 處理檔案輸入變更
|
||||
*/
|
||||
FileUploadManager.prototype.handleFileInputChange = function(fileInput, event) {
|
||||
const files = event.target.files;
|
||||
if (files && files.length > 0) {
|
||||
console.log('📁 檔案選擇變更:', files.length, '個檔案');
|
||||
this.processFiles(Array.from(files), fileInput);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 處理拖放事件
|
||||
*/
|
||||
FileUploadManager.prototype.handleDragOver = function(uploadArea, event) {
|
||||
event.preventDefault();
|
||||
uploadArea.classList.add('dragover');
|
||||
};
|
||||
|
||||
FileUploadManager.prototype.handleDragLeave = function(uploadArea, event) {
|
||||
event.preventDefault();
|
||||
// 只有當滑鼠真正離開上傳區域時才移除樣式
|
||||
if (!uploadArea.contains(event.relatedTarget)) {
|
||||
uploadArea.classList.remove('dragover');
|
||||
}
|
||||
};
|
||||
|
||||
FileUploadManager.prototype.handleDrop = function(uploadArea, event) {
|
||||
event.preventDefault();
|
||||
uploadArea.classList.remove('dragover');
|
||||
|
||||
const files = event.dataTransfer.files;
|
||||
if (files && files.length > 0) {
|
||||
console.log('📁 拖放檔案:', files.length, '個檔案');
|
||||
this.processFiles(Array.from(files), uploadArea.querySelector('input[type="file"]'));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 處理檔案移除
|
||||
*/
|
||||
FileUploadManager.prototype.handleRemoveFile = function(removeBtn) {
|
||||
const index = parseInt(removeBtn.dataset.index);
|
||||
if (!isNaN(index) && index >= 0 && index < this.files.length) {
|
||||
const removedFile = this.files.splice(index, 1)[0];
|
||||
console.log('🗑️ 移除檔案:', removedFile.name);
|
||||
|
||||
this.updateAllPreviews();
|
||||
|
||||
if (this.onFileRemove) {
|
||||
this.onFileRemove(removedFile, index);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 設置全域剪貼板貼上處理
|
||||
*/
|
||||
FileUploadManager.prototype.setupGlobalPasteHandler = function() {
|
||||
document.removeEventListener('paste', this.handleGlobalPaste);
|
||||
document.addEventListener('paste', this.handleGlobalPaste);
|
||||
};
|
||||
|
||||
/**
|
||||
* 處理全域剪貼板貼上
|
||||
*/
|
||||
FileUploadManager.prototype.handleGlobalPaste = function(event) {
|
||||
const items = event.clipboardData.items;
|
||||
const imageFiles = [];
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
if (item.type.indexOf('image') !== -1) {
|
||||
const file = item.getAsFile();
|
||||
if (file) {
|
||||
imageFiles.push(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (imageFiles.length > 0) {
|
||||
event.preventDefault();
|
||||
console.log('📋 剪貼板貼上圖片:', imageFiles.length, '個檔案');
|
||||
this.processFiles(imageFiles);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 處理檔案
|
||||
*/
|
||||
FileUploadManager.prototype.processFiles = function(files, sourceInput) {
|
||||
const validFiles = [];
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
|
||||
// 檢查檔案類型
|
||||
if (!file.type.startsWith('image/')) {
|
||||
console.warn('⚠️ 跳過非圖片檔案:', file.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 檢查檔案大小
|
||||
if (this.maxFileSize > 0 && file.size > this.maxFileSize) {
|
||||
const sizeLimit = this.formatFileSize(this.maxFileSize);
|
||||
console.warn('⚠️ 檔案過大:', file.name, '超過限制', sizeLimit);
|
||||
this.showMessage('圖片大小超過限制 (' + sizeLimit + '): ' + file.name, 'warning');
|
||||
continue;
|
||||
}
|
||||
|
||||
// 檢查檔案數量限制
|
||||
if (this.files.length + validFiles.length >= this.maxFiles) {
|
||||
console.warn('⚠️ 檔案數量超過限制:', this.maxFiles);
|
||||
this.showMessage('最多只能上傳 ' + this.maxFiles + ' 個檔案', 'warning');
|
||||
break;
|
||||
}
|
||||
|
||||
validFiles.push(file);
|
||||
}
|
||||
|
||||
// 處理有效檔案
|
||||
if (validFiles.length > 0) {
|
||||
this.addFiles(validFiles);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 添加檔案到列表
|
||||
*/
|
||||
FileUploadManager.prototype.addFiles = function(files) {
|
||||
const promises = files.map(file => this.fileToBase64(file));
|
||||
|
||||
const self = this;
|
||||
Promise.all(promises)
|
||||
.then(function(base64Results) {
|
||||
base64Results.forEach(function(base64, index) {
|
||||
const file = files[index];
|
||||
const fileData = {
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
data: base64,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
|
||||
self.files.push(fileData);
|
||||
console.log('✅ 檔案已添加:', file.name);
|
||||
|
||||
if (self.onFileAdd) {
|
||||
self.onFileAdd(fileData);
|
||||
}
|
||||
});
|
||||
|
||||
self.updateAllPreviews();
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('❌ 檔案處理失敗:', error);
|
||||
self.showMessage('檔案處理失敗,請重試', 'error');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 將檔案轉換為 Base64
|
||||
*/
|
||||
FileUploadManager.prototype.fileToBase64 = function(file) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
resolve(reader.result.split(',')[1]);
|
||||
};
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新所有預覽容器
|
||||
*/
|
||||
FileUploadManager.prototype.updateAllPreviews = function() {
|
||||
const previewContainers = document.querySelectorAll('.image-preview-container');
|
||||
const self = this;
|
||||
|
||||
previewContainers.forEach(function(container) {
|
||||
self.updatePreviewContainer(container);
|
||||
});
|
||||
|
||||
this.updateFileCount();
|
||||
console.log('🖼️ 已更新', previewContainers.length, '個預覽容器');
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新單個預覽容器
|
||||
*/
|
||||
FileUploadManager.prototype.updatePreviewContainer = function(container) {
|
||||
container.innerHTML = '';
|
||||
|
||||
const self = this;
|
||||
this.files.forEach(function(file, index) {
|
||||
const previewElement = self.createPreviewElement(file, index);
|
||||
container.appendChild(previewElement);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 創建預覽元素
|
||||
*/
|
||||
FileUploadManager.prototype.createPreviewElement = function(file, index) {
|
||||
const preview = document.createElement('div');
|
||||
preview.className = 'image-preview-item';
|
||||
|
||||
// 圖片元素
|
||||
const img = document.createElement('img');
|
||||
img.src = 'data:' + file.type + ';base64,' + file.data;
|
||||
img.alt = file.name;
|
||||
img.title = file.name + ' (' + this.formatFileSize(file.size) + ')';
|
||||
|
||||
// 檔案資訊
|
||||
const info = document.createElement('div');
|
||||
info.className = 'image-info';
|
||||
|
||||
const name = document.createElement('div');
|
||||
name.className = 'image-name';
|
||||
name.textContent = file.name;
|
||||
|
||||
const size = document.createElement('div');
|
||||
size.className = 'image-size';
|
||||
size.textContent = this.formatFileSize(file.size);
|
||||
|
||||
// 移除按鈕
|
||||
const removeBtn = document.createElement('button');
|
||||
removeBtn.className = 'image-remove-btn';
|
||||
removeBtn.textContent = '×';
|
||||
removeBtn.title = '移除圖片';
|
||||
removeBtn.dataset.index = index;
|
||||
removeBtn.setAttribute('aria-label', '移除圖片 ' + file.name);
|
||||
|
||||
// 組裝元素
|
||||
info.appendChild(name);
|
||||
info.appendChild(size);
|
||||
preview.appendChild(img);
|
||||
preview.appendChild(info);
|
||||
preview.appendChild(removeBtn);
|
||||
|
||||
return preview;
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新檔案計數顯示
|
||||
*/
|
||||
FileUploadManager.prototype.updateFileCount = function() {
|
||||
const count = this.files.length;
|
||||
const countElements = document.querySelectorAll('.image-count');
|
||||
|
||||
countElements.forEach(function(element) {
|
||||
element.textContent = count > 0 ? '(' + count + ')' : '';
|
||||
});
|
||||
|
||||
// 更新上傳區域狀態
|
||||
const uploadAreas = document.querySelectorAll('.image-upload-area');
|
||||
uploadAreas.forEach(function(area) {
|
||||
if (count > 0) {
|
||||
area.classList.add('has-images');
|
||||
} else {
|
||||
area.classList.remove('has-images');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 格式化檔案大小
|
||||
*/
|
||||
FileUploadManager.prototype.formatFileSize = function(bytes) {
|
||||
if (bytes === 0) 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];
|
||||
};
|
||||
|
||||
/**
|
||||
* 顯示訊息
|
||||
*/
|
||||
FileUploadManager.prototype.showMessage = function(message, type) {
|
||||
// 使用現有的 Utils.showMessage 如果可用
|
||||
if (window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.showMessage) {
|
||||
const messageType = type === 'warning' ? window.MCPFeedback.Utils.CONSTANTS.MESSAGE_WARNING :
|
||||
type === 'error' ? window.MCPFeedback.Utils.CONSTANTS.MESSAGE_ERROR :
|
||||
window.MCPFeedback.Utils.CONSTANTS.MESSAGE_INFO;
|
||||
window.MCPFeedback.Utils.showMessage(message, messageType);
|
||||
} else {
|
||||
// 後備方案
|
||||
console.log('[' + type.toUpperCase() + ']', message);
|
||||
alert(message);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新設定
|
||||
*/
|
||||
FileUploadManager.prototype.updateSettings = function(settings) {
|
||||
this.maxFileSize = settings.imageSizeLimit || 0;
|
||||
this.enableBase64Detail = settings.enableBase64Detail || false;
|
||||
|
||||
console.log('⚙️ FileUploadManager 設定已更新:', {
|
||||
maxFileSize: this.maxFileSize,
|
||||
enableBase64Detail: this.enableBase64Detail
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 獲取檔案列表
|
||||
*/
|
||||
FileUploadManager.prototype.getFiles = function() {
|
||||
return this.files.slice(); // 返回副本
|
||||
};
|
||||
|
||||
/**
|
||||
* 清空所有檔案
|
||||
*/
|
||||
FileUploadManager.prototype.clearFiles = function() {
|
||||
this.files = [];
|
||||
this.updateAllPreviews();
|
||||
console.log('🗑️ 已清空所有檔案');
|
||||
};
|
||||
|
||||
/**
|
||||
* 清理資源
|
||||
*/
|
||||
FileUploadManager.prototype.cleanup = function() {
|
||||
// 移除事件監聽器
|
||||
document.removeEventListener('click', this.handleDelegatedEvent);
|
||||
document.removeEventListener('dragover', this.handleDelegatedEvent);
|
||||
document.removeEventListener('dragleave', this.handleDelegatedEvent);
|
||||
document.removeEventListener('drop', this.handleDelegatedEvent);
|
||||
document.removeEventListener('change', this.handleDelegatedEvent);
|
||||
document.removeEventListener('paste', this.handleGlobalPaste);
|
||||
|
||||
// 清理防抖計時器
|
||||
if (this.debounceTimeout) {
|
||||
clearTimeout(this.debounceTimeout);
|
||||
this.debounceTimeout = null;
|
||||
}
|
||||
|
||||
// 清空檔案
|
||||
this.clearFiles();
|
||||
|
||||
this.isInitialized = false;
|
||||
console.log('🧹 FileUploadManager 資源已清理');
|
||||
};
|
||||
|
||||
// 將 FileUploadManager 加入命名空間
|
||||
window.MCPFeedback.FileUploadManager = FileUploadManager;
|
||||
|
||||
console.log('✅ FileUploadManager 模組載入完成');
|
||||
|
||||
})();
|
@ -17,30 +17,38 @@
|
||||
*/
|
||||
function ImageHandler(options) {
|
||||
options = options || {};
|
||||
|
||||
this.images = [];
|
||||
|
||||
this.imageSizeLimit = options.imageSizeLimit || 0;
|
||||
this.enableBase64Detail = options.enableBase64Detail || false;
|
||||
this.layoutMode = options.layoutMode || 'combined-vertical';
|
||||
this.currentImagePrefix = '';
|
||||
|
||||
// UI 元素
|
||||
this.imageInput = null;
|
||||
this.imageUploadArea = null;
|
||||
this.imagePreviewContainer = null;
|
||||
|
||||
// UI 元素(保留用於設定同步)
|
||||
this.imageSizeLimitSelect = null;
|
||||
this.enableBase64DetailCheckbox = null;
|
||||
|
||||
// 事件處理器
|
||||
this.imageChangeHandler = null;
|
||||
this.imageClickHandler = null;
|
||||
this.imageDragOverHandler = null;
|
||||
this.imageDragLeaveHandler = null;
|
||||
this.imageDropHandler = null;
|
||||
this.pasteHandler = null;
|
||||
|
||||
|
||||
// 回調函數
|
||||
this.onSettingsChange = options.onSettingsChange || null;
|
||||
|
||||
// 創建檔案上傳管理器
|
||||
const self = this;
|
||||
this.fileUploadManager = new window.MCPFeedback.FileUploadManager({
|
||||
maxFileSize: this.imageSizeLimit,
|
||||
enableBase64Detail: this.enableBase64Detail,
|
||||
onFileAdd: function(fileData) {
|
||||
console.log('📁 檔案已添加:', fileData.name);
|
||||
},
|
||||
onFileRemove: function(fileData, index) {
|
||||
console.log('🗑️ 檔案已移除:', fileData.name);
|
||||
},
|
||||
onSettingsChange: function() {
|
||||
if (self.onSettingsChange) {
|
||||
self.onSettingsChange();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('🖼️ ImageHandler 建構函數初始化完成');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,148 +56,46 @@
|
||||
*/
|
||||
ImageHandler.prototype.init = function() {
|
||||
console.log('🖼️ 開始初始化圖片處理功能...');
|
||||
|
||||
this.initImageElements();
|
||||
this.setupImageEventListeners();
|
||||
this.setupGlobalPasteHandler();
|
||||
|
||||
|
||||
// 初始化設定元素
|
||||
this.initImageSettingsElements();
|
||||
|
||||
// 初始化檔案上傳管理器
|
||||
this.fileUploadManager.initialize();
|
||||
|
||||
console.log('✅ 圖片處理功能初始化完成');
|
||||
};
|
||||
|
||||
/**
|
||||
* 動態初始化圖片相關元素
|
||||
*/
|
||||
ImageHandler.prototype.initImageElements = function() {
|
||||
const prefix = this.layoutMode && this.layoutMode.startsWith('combined') ? 'combined' : 'feedback';
|
||||
|
||||
console.log('🖼️ 初始化圖片元素,使用前綴: ' + prefix);
|
||||
|
||||
this.imageInput = Utils.safeQuerySelector('#' + prefix + 'ImageInput') ||
|
||||
Utils.safeQuerySelector('#imageInput');
|
||||
this.imageUploadArea = Utils.safeQuerySelector('#' + prefix + 'ImageUploadArea') ||
|
||||
Utils.safeQuerySelector('#imageUploadArea');
|
||||
this.imagePreviewContainer = Utils.safeQuerySelector('#' + prefix + 'ImagePreviewContainer') ||
|
||||
Utils.safeQuerySelector('#imagePreviewContainer');
|
||||
this.imageSizeLimitSelect = Utils.safeQuerySelector('#' + prefix + 'ImageSizeLimit') ||
|
||||
Utils.safeQuerySelector('#imageSizeLimit');
|
||||
this.enableBase64DetailCheckbox = Utils.safeQuerySelector('#' + prefix + 'EnableBase64Detail') ||
|
||||
Utils.safeQuerySelector('#enableBase64Detail');
|
||||
|
||||
this.currentImagePrefix = prefix;
|
||||
|
||||
if (!this.imageInput || !this.imageUploadArea) {
|
||||
console.warn('⚠️ 圖片元素初始化失敗 - imageInput: ' + !!this.imageInput + ', imageUploadArea: ' + !!this.imageUploadArea);
|
||||
} else {
|
||||
console.log('✅ 圖片元素初始化成功 - 前綴: ' + prefix);
|
||||
}
|
||||
};
|
||||
ImageHandler.prototype.initImageSettingsElements = function() {
|
||||
// 查找設定頁籤中的圖片設定元素
|
||||
this.imageSizeLimitSelect = Utils.safeQuerySelector('#settingsImageSizeLimit');
|
||||
this.enableBase64DetailCheckbox = Utils.safeQuerySelector('#settingsEnableBase64Detail');
|
||||
|
||||
/**
|
||||
* 設置圖片事件監聽器
|
||||
*/
|
||||
ImageHandler.prototype.setupImageEventListeners = function() {
|
||||
if (!this.imageInput || !this.imageUploadArea) {
|
||||
console.warn('⚠️ 缺少必要的圖片元素,跳過事件監聽器設置');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('🖼️ 設置圖片事件監聽器 - imageInput: ' + this.imageInput.id + ', imageUploadArea: ' + this.imageUploadArea.id);
|
||||
|
||||
// 移除舊的事件監聽器
|
||||
this.removeImageEventListeners();
|
||||
|
||||
const self = this;
|
||||
|
||||
// 文件選擇事件
|
||||
this.imageChangeHandler = function(e) {
|
||||
console.log('📁 文件選擇事件觸發 - input: ' + e.target.id + ', files: ' + e.target.files.length);
|
||||
self.handleFileSelect(e.target.files);
|
||||
};
|
||||
this.imageInput.addEventListener('change', this.imageChangeHandler);
|
||||
|
||||
// 點擊上傳區域
|
||||
this.imageClickHandler = function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (self.imageInput) {
|
||||
console.log('🖱️ 點擊上傳區域 - 觸發 input: ' + self.imageInput.id);
|
||||
self.imageInput.click();
|
||||
}
|
||||
};
|
||||
this.imageUploadArea.addEventListener('click', this.imageClickHandler);
|
||||
|
||||
// 拖放事件
|
||||
this.imageDragOverHandler = function(e) {
|
||||
e.preventDefault();
|
||||
self.imageUploadArea.classList.add('dragover');
|
||||
};
|
||||
this.imageUploadArea.addEventListener('dragover', this.imageDragOverHandler);
|
||||
|
||||
this.imageDragLeaveHandler = function(e) {
|
||||
e.preventDefault();
|
||||
self.imageUploadArea.classList.remove('dragover');
|
||||
};
|
||||
this.imageUploadArea.addEventListener('dragleave', this.imageDragLeaveHandler);
|
||||
|
||||
this.imageDropHandler = function(e) {
|
||||
e.preventDefault();
|
||||
self.imageUploadArea.classList.remove('dragover');
|
||||
self.handleFileSelect(e.dataTransfer.files);
|
||||
};
|
||||
this.imageUploadArea.addEventListener('drop', this.imageDropHandler);
|
||||
|
||||
// 初始化圖片設定事件
|
||||
// 初始化設定事件監聽器
|
||||
this.initImageSettings();
|
||||
|
||||
console.log('✅ 圖片設定元素初始化完成');
|
||||
};
|
||||
|
||||
/**
|
||||
* 設置全域剪貼板貼上事件
|
||||
*/
|
||||
ImageHandler.prototype.setupGlobalPasteHandler = function() {
|
||||
if (this.pasteHandler) {
|
||||
return; // 已經設置過了
|
||||
}
|
||||
|
||||
const self = this;
|
||||
this.pasteHandler = function(e) {
|
||||
const items = e.clipboardData.items;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
if (item.type.indexOf('image') !== -1) {
|
||||
e.preventDefault();
|
||||
const file = item.getAsFile();
|
||||
self.handleFileSelect([file]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('paste', this.pasteHandler);
|
||||
console.log('✅ 全域剪貼板貼上事件已設置');
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 移除圖片事件監聽器
|
||||
* 移除圖片設定事件監聽器
|
||||
*/
|
||||
ImageHandler.prototype.removeImageEventListeners = function() {
|
||||
if (this.imageInput && this.imageChangeHandler) {
|
||||
this.imageInput.removeEventListener('change', this.imageChangeHandler);
|
||||
ImageHandler.prototype.removeImageSettingsListeners = function() {
|
||||
if (this.imageSizeLimitSelect && this.imageSizeLimitChangeHandler) {
|
||||
this.imageSizeLimitSelect.removeEventListener('change', this.imageSizeLimitChangeHandler);
|
||||
this.imageSizeLimitChangeHandler = null;
|
||||
}
|
||||
|
||||
if (this.imageUploadArea) {
|
||||
if (this.imageClickHandler) {
|
||||
this.imageUploadArea.removeEventListener('click', this.imageClickHandler);
|
||||
}
|
||||
if (this.imageDragOverHandler) {
|
||||
this.imageUploadArea.removeEventListener('dragover', this.imageDragOverHandler);
|
||||
}
|
||||
if (this.imageDragLeaveHandler) {
|
||||
this.imageUploadArea.removeEventListener('dragleave', this.imageDragLeaveHandler);
|
||||
}
|
||||
if (this.imageDropHandler) {
|
||||
this.imageUploadArea.removeEventListener('drop', this.imageDropHandler);
|
||||
}
|
||||
|
||||
if (this.enableBase64DetailCheckbox && this.enableBase64DetailChangeHandler) {
|
||||
this.enableBase64DetailCheckbox.removeEventListener('change', this.enableBase64DetailChangeHandler);
|
||||
this.enableBase64DetailChangeHandler = null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -198,238 +104,47 @@
|
||||
*/
|
||||
ImageHandler.prototype.initImageSettings = function() {
|
||||
const self = this;
|
||||
|
||||
|
||||
// 移除舊的設定事件監聽器
|
||||
this.removeImageSettingsListeners();
|
||||
|
||||
if (this.imageSizeLimitSelect) {
|
||||
this.imageSizeLimitSelect.addEventListener('change', function(e) {
|
||||
this.imageSizeLimitChangeHandler = function(e) {
|
||||
self.imageSizeLimit = parseInt(e.target.value);
|
||||
if (self.onSettingsChange) {
|
||||
self.onSettingsChange();
|
||||
}
|
||||
});
|
||||
};
|
||||
this.imageSizeLimitSelect.addEventListener('change', this.imageSizeLimitChangeHandler);
|
||||
}
|
||||
|
||||
if (this.enableBase64DetailCheckbox) {
|
||||
this.enableBase64DetailCheckbox.addEventListener('change', function(e) {
|
||||
this.enableBase64DetailChangeHandler = function(e) {
|
||||
self.enableBase64Detail = e.target.checked;
|
||||
if (self.onSettingsChange) {
|
||||
self.onSettingsChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 處理文件選擇
|
||||
*/
|
||||
ImageHandler.prototype.handleFileSelect = function(files) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
if (file.type.startsWith('image/')) {
|
||||
this.addImage(file);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 添加圖片
|
||||
*/
|
||||
ImageHandler.prototype.addImage = function(file) {
|
||||
// 檢查文件大小
|
||||
if (this.imageSizeLimit > 0 && file.size > this.imageSizeLimit) {
|
||||
Utils.showMessage('圖片大小超過限制 (' + Utils.formatFileSize(this.imageSizeLimit) + ')', Utils.CONSTANTS.MESSAGE_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
const self = this;
|
||||
this.fileToBase64(file)
|
||||
.then(function(base64) {
|
||||
const imageData = {
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
data: base64
|
||||
};
|
||||
|
||||
self.images.push(imageData);
|
||||
self.updateImagePreview();
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('圖片處理失敗:', error);
|
||||
Utils.showMessage('圖片處理失敗,請重試', Utils.CONSTANTS.MESSAGE_ERROR);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 將文件轉換為 Base64
|
||||
*/
|
||||
ImageHandler.prototype.fileToBase64 = function(file) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
resolve(reader.result.split(',')[1]);
|
||||
};
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新圖片預覽
|
||||
*/
|
||||
ImageHandler.prototype.updateImagePreview = function() {
|
||||
const previewContainers = [
|
||||
Utils.safeQuerySelector('#feedbackImagePreviewContainer'),
|
||||
Utils.safeQuerySelector('#combinedImagePreviewContainer'),
|
||||
this.imagePreviewContainer
|
||||
].filter(function(container) {
|
||||
return container !== null;
|
||||
});
|
||||
|
||||
if (previewContainers.length === 0) {
|
||||
console.warn('⚠️ 沒有找到圖片預覽容器');
|
||||
return;
|
||||
this.enableBase64DetailCheckbox.addEventListener('change', this.enableBase64DetailChangeHandler);
|
||||
}
|
||||
|
||||
console.log('🖼️ 更新 ' + previewContainers.length + ' 個圖片預覽容器');
|
||||
|
||||
const self = this;
|
||||
previewContainers.forEach(function(container) {
|
||||
container.innerHTML = '';
|
||||
|
||||
self.images.forEach(function(image, index) {
|
||||
const preview = self.createImagePreviewElement(image, index);
|
||||
container.appendChild(preview);
|
||||
});
|
||||
});
|
||||
|
||||
this.updateImageCount();
|
||||
};
|
||||
|
||||
/**
|
||||
* 創建圖片預覽元素
|
||||
*/
|
||||
ImageHandler.prototype.createImagePreviewElement = function(image, index) {
|
||||
const self = this;
|
||||
|
||||
// 創建圖片預覽項目容器
|
||||
const preview = document.createElement('div');
|
||||
preview.className = 'image-preview-item';
|
||||
preview.style.cssText = 'position: relative; display: inline-block;';
|
||||
|
||||
// 創建圖片元素
|
||||
const img = document.createElement('img');
|
||||
img.src = 'data:' + image.type + ';base64,' + image.data;
|
||||
img.alt = image.name;
|
||||
img.style.cssText = 'width: 80px; height: 80px; object-fit: cover; display: block; border-radius: 6px;';
|
||||
|
||||
// 創建圖片信息容器
|
||||
const imageInfo = document.createElement('div');
|
||||
imageInfo.className = 'image-info';
|
||||
imageInfo.style.cssText = `
|
||||
position: absolute; bottom: 0; left: 0; right: 0;
|
||||
background: rgba(0, 0, 0, 0.7); color: white; padding: 4px;
|
||||
font-size: 10px; line-height: 1.2;
|
||||
`;
|
||||
|
||||
const imageName = document.createElement('div');
|
||||
imageName.className = 'image-name';
|
||||
imageName.textContent = image.name;
|
||||
imageName.style.cssText = 'font-weight: bold; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;';
|
||||
|
||||
const imageSize = document.createElement('div');
|
||||
imageSize.className = 'image-size';
|
||||
imageSize.textContent = Utils.formatFileSize(image.size);
|
||||
imageSize.style.cssText = 'font-size: 9px; opacity: 0.8;';
|
||||
|
||||
// 創建刪除按鈕
|
||||
const removeBtn = document.createElement('button');
|
||||
removeBtn.className = 'image-remove-btn';
|
||||
removeBtn.textContent = '×';
|
||||
removeBtn.title = '移除圖片';
|
||||
removeBtn.style.cssText = `
|
||||
position: absolute; top: -8px; right: -8px; width: 20px; height: 20px;
|
||||
border-radius: 50%; background: #f44336; color: white; border: none;
|
||||
cursor: pointer; font-size: 12px; font-weight: bold;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); transition: all 0.3s ease; z-index: 10;
|
||||
`;
|
||||
|
||||
// 添加刪除按鈕懸停效果
|
||||
removeBtn.addEventListener('mouseenter', function() {
|
||||
removeBtn.style.background = '#d32f2f';
|
||||
removeBtn.style.transform = 'scale(1.1)';
|
||||
});
|
||||
removeBtn.addEventListener('mouseleave', function() {
|
||||
removeBtn.style.background = '#f44336';
|
||||
removeBtn.style.transform = 'scale(1)';
|
||||
});
|
||||
|
||||
// 添加刪除功能
|
||||
removeBtn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
self.removeImage(index);
|
||||
});
|
||||
|
||||
// 組裝元素
|
||||
imageInfo.appendChild(imageName);
|
||||
imageInfo.appendChild(imageSize);
|
||||
preview.appendChild(img);
|
||||
preview.appendChild(imageInfo);
|
||||
preview.appendChild(removeBtn);
|
||||
|
||||
return preview;
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新圖片計數顯示
|
||||
* 獲取圖片數據
|
||||
*/
|
||||
ImageHandler.prototype.updateImageCount = function() {
|
||||
const count = this.images.length;
|
||||
const countElements = document.querySelectorAll('.image-count');
|
||||
|
||||
countElements.forEach(function(element) {
|
||||
element.textContent = count > 0 ? '(' + count + ')' : '';
|
||||
});
|
||||
|
||||
// 更新上傳區域的顯示狀態
|
||||
const uploadAreas = [
|
||||
Utils.safeQuerySelector('#feedbackImageUploadArea'),
|
||||
Utils.safeQuerySelector('#combinedImageUploadArea')
|
||||
].filter(function(area) {
|
||||
return area !== null;
|
||||
});
|
||||
|
||||
uploadAreas.forEach(function(area) {
|
||||
if (count > 0) {
|
||||
area.classList.add('has-images');
|
||||
} else {
|
||||
area.classList.remove('has-images');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 移除圖片
|
||||
*/
|
||||
ImageHandler.prototype.removeImage = function(index) {
|
||||
this.images.splice(index, 1);
|
||||
this.updateImagePreview();
|
||||
ImageHandler.prototype.getImages = function() {
|
||||
return this.fileUploadManager.getFiles();
|
||||
};
|
||||
|
||||
/**
|
||||
* 清空所有圖片
|
||||
*/
|
||||
ImageHandler.prototype.clearImages = function() {
|
||||
this.images = [];
|
||||
this.updateImagePreview();
|
||||
};
|
||||
|
||||
/**
|
||||
* 獲取圖片數據
|
||||
*/
|
||||
ImageHandler.prototype.getImages = function() {
|
||||
return Utils.deepClone(this.images);
|
||||
this.fileUploadManager.clearFiles();
|
||||
};
|
||||
|
||||
/**
|
||||
@ -437,19 +152,13 @@
|
||||
*/
|
||||
ImageHandler.prototype.reinitialize = function(layoutMode) {
|
||||
console.log('🔄 重新初始化圖片處理功能...');
|
||||
|
||||
|
||||
this.layoutMode = layoutMode;
|
||||
this.removeImageEventListeners();
|
||||
this.initImageElements();
|
||||
|
||||
if (this.imageUploadArea && this.imageInput) {
|
||||
this.setupImageEventListeners();
|
||||
console.log('✅ 圖片處理功能重新初始化完成');
|
||||
} else {
|
||||
console.warn('⚠️ 圖片處理重新初始化失敗 - 缺少必要元素');
|
||||
}
|
||||
|
||||
this.updateImagePreview();
|
||||
|
||||
// 重新初始化設定元素
|
||||
this.initImageSettingsElements();
|
||||
|
||||
console.log('✅ 圖片處理功能重新初始化完成');
|
||||
};
|
||||
|
||||
/**
|
||||
@ -458,7 +167,13 @@
|
||||
ImageHandler.prototype.updateSettings = function(settings) {
|
||||
this.imageSizeLimit = settings.imageSizeLimit || 0;
|
||||
this.enableBase64Detail = settings.enableBase64Detail || false;
|
||||
|
||||
|
||||
// 更新檔案上傳管理器設定
|
||||
this.fileUploadManager.updateSettings({
|
||||
imageSizeLimit: this.imageSizeLimit,
|
||||
enableBase64Detail: this.enableBase64Detail
|
||||
});
|
||||
|
||||
// 同步到 UI 元素
|
||||
if (this.imageSizeLimitSelect) {
|
||||
this.imageSizeLimitSelect.value = this.imageSizeLimit.toString();
|
||||
@ -472,14 +187,8 @@
|
||||
* 清理資源
|
||||
*/
|
||||
ImageHandler.prototype.cleanup = function() {
|
||||
this.removeImageEventListeners();
|
||||
|
||||
if (this.pasteHandler) {
|
||||
document.removeEventListener('paste', this.pasteHandler);
|
||||
this.pasteHandler = null;
|
||||
}
|
||||
|
||||
this.clearImages();
|
||||
this.removeImageSettingsListeners();
|
||||
this.fileUploadManager.cleanup();
|
||||
};
|
||||
|
||||
// 將 ImageHandler 加入命名空間
|
||||
|
@ -369,15 +369,22 @@
|
||||
* 應用圖片設定
|
||||
*/
|
||||
SettingsManager.prototype.applyImageSettings = function() {
|
||||
// 更新所有圖片大小限制選擇器(包括設定頁籤中的)
|
||||
const imageSizeLimitSelects = document.querySelectorAll('[id$="ImageSizeLimit"]');
|
||||
imageSizeLimitSelects.forEach(function(select) {
|
||||
select.value = this.currentSettings.imageSizeLimit.toString();
|
||||
}.bind(this));
|
||||
|
||||
// 更新所有 Base64 相容模式複選框(包括設定頁籤中的)
|
||||
const enableBase64DetailCheckboxes = document.querySelectorAll('[id$="EnableBase64Detail"]');
|
||||
enableBase64DetailCheckboxes.forEach(function(checkbox) {
|
||||
checkbox.checked = this.currentSettings.enableBase64Detail;
|
||||
}.bind(this));
|
||||
|
||||
console.log('圖片設定已應用到 UI:', {
|
||||
imageSizeLimit: this.currentSettings.imageSizeLimit,
|
||||
enableBase64Detail: this.currentSettings.enableBase64Detail
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -424,6 +431,26 @@
|
||||
});
|
||||
});
|
||||
|
||||
// 圖片設定 - 大小限制選擇器
|
||||
const settingsImageSizeLimit = Utils.safeQuerySelector('#settingsImageSizeLimit');
|
||||
if (settingsImageSizeLimit) {
|
||||
settingsImageSizeLimit.addEventListener('change', function(e) {
|
||||
const value = parseInt(e.target.value);
|
||||
self.set('imageSizeLimit', value);
|
||||
console.log('圖片大小限制已更新:', value);
|
||||
});
|
||||
}
|
||||
|
||||
// 圖片設定 - Base64 相容模式切換器
|
||||
const settingsEnableBase64Detail = Utils.safeQuerySelector('#settingsEnableBase64Detail');
|
||||
if (settingsEnableBase64Detail) {
|
||||
settingsEnableBase64Detail.addEventListener('change', function(e) {
|
||||
const value = e.target.checked;
|
||||
self.set('enableBase64Detail', value);
|
||||
console.log('Base64 相容模式已更新:', value);
|
||||
});
|
||||
}
|
||||
|
||||
// 重置設定
|
||||
const resetBtn = Utils.safeQuerySelector('#resetSettingsBtn');
|
||||
if (resetBtn) {
|
||||
@ -435,7 +462,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
// 將 SettingsManager 加入命名空間
|
||||
|
@ -19,34 +19,7 @@
|
||||
{% set min_height = min_height or "120px" %}
|
||||
{% set upload_text = upload_text or "📎 點擊選擇圖片或拖放圖片到此處<br><small>支援 PNG、JPG、JPEG、GIF、BMP、WebP 等格式</small>" %}
|
||||
|
||||
<!-- 圖片設定區域 -->
|
||||
<div class="input-group" style="margin-bottom: 12px;">
|
||||
<details class="image-settings-details">
|
||||
<summary class="image-settings-summary" data-i18n="images.settings.title">⚙️ 圖片設定</summary>
|
||||
<div class="image-settings-content">
|
||||
<div class="image-setting-row">
|
||||
<label class="image-setting-label" data-i18n="images.settings.sizeLimit">圖片大小限制:</label>
|
||||
<select id="{{ id_prefix }}ImageSizeLimit" class="image-setting-select">
|
||||
<option value="0" data-i18n="images.settings.sizeLimitOptions.unlimited">無限制</option>
|
||||
<option value="1048576" data-i18n="images.settings.sizeLimitOptions.1mb">1MB</option>
|
||||
<option value="3145728" data-i18n="images.settings.sizeLimitOptions.3mb">3MB</option>
|
||||
<option value="5242880" data-i18n="images.settings.sizeLimitOptions.5mb">5MB</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="image-setting-row">
|
||||
<label class="image-setting-checkbox-container">
|
||||
<input type="checkbox" id="{{ id_prefix }}EnableBase64Detail" class="image-setting-checkbox">
|
||||
<span class="image-setting-checkmark"></span>
|
||||
<span class="image-setting-label" data-i18n="images.settings.base64Detail">Base64 相容模式</span>
|
||||
</label>
|
||||
<small class="image-setting-help" data-i18n="images.settings.base64Warning">⚠️ 會增加傳輸量</small>
|
||||
</div>
|
||||
<div class="image-setting-help-text" data-i18n="images.settings.base64DetailHelp">
|
||||
啟用後會在文字中包含完整的 Base64 圖片資料,提升部分 AI 模型的相容性
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="input-group">
|
||||
<label class="input-label" data-i18n="feedback.imageLabel">{{ label_text }}</label>
|
||||
|
@ -743,6 +743,46 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 圖片設定卡片 -->
|
||||
<div class="settings-card">
|
||||
<div class="settings-card-header">
|
||||
<h3 class="settings-card-title" data-i18n="images.settings.title">🖼️ 圖片設定</h3>
|
||||
</div>
|
||||
<div class="settings-card-body">
|
||||
<div class="setting-item">
|
||||
<div class="setting-info">
|
||||
<div class="setting-label" data-i18n="images.settings.sizeLimit">圖片大小限制</div>
|
||||
<div class="setting-description" data-i18n="images.settings.sizeLimitDesc">
|
||||
設定上傳圖片的最大檔案大小限制
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-size-limit-selector">
|
||||
<select id="settingsImageSizeLimit" class="image-size-limit-select">
|
||||
<option value="0" data-i18n="images.settings.sizeLimitOptions.unlimited">無限制</option>
|
||||
<option value="1048576" data-i18n="images.settings.sizeLimitOptions.1mb">1MB</option>
|
||||
<option value="3145728" data-i18n="images.settings.sizeLimitOptions.3mb">3MB</option>
|
||||
<option value="5242880" data-i18n="images.settings.sizeLimitOptions.5mb">5MB</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="setting-info">
|
||||
<div class="setting-label" data-i18n="images.settings.base64Detail">Base64 相容模式</div>
|
||||
<div class="setting-description" data-i18n="images.settings.base64DetailHelp">
|
||||
啟用後會在文字中包含完整的 Base64 圖片資料,提升與某些 AI 模型的相容性
|
||||
</div>
|
||||
<div class="setting-warning" data-i18n="images.settings.base64Warning">⚠️ 會增加傳輸量</div>
|
||||
</div>
|
||||
<div class="base64-toggle-container">
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="settingsEnableBase64Detail" class="toggle-input">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示詞管理卡片 -->
|
||||
<div class="settings-card">
|
||||
<div class="settings-card-header">
|
||||
@ -894,6 +934,7 @@
|
||||
<script src="/static/js/modules/websocket-manager.js?v=2025010510"></script>
|
||||
<script src="/static/js/modules/connection-monitor.js?v=2025010510"></script>
|
||||
<script src="/static/js/modules/session-manager.js?v=2025010510"></script>
|
||||
<script src="/static/js/modules/file-upload-manager.js?v=2025010510"></script>
|
||||
<script src="/static/js/modules/image-handler.js?v=2025010510"></script>
|
||||
<script src="/static/js/modules/settings-manager.js?v=2025010510"></script>
|
||||
<script src="/static/js/modules/ui-manager.js?v=2025010510"></script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user