category toglle 기능 복구

icon 저장 기능 오류 수정
검색 기능 추가
This commit is contained in:
backuppc
2025-07-09 14:33:38 +09:00
parent 33a22e0ac7
commit 31fd7542b8
4 changed files with 58 additions and 37 deletions

2
Form1.Designer.cs generated
View File

@@ -34,7 +34,7 @@
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F); this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(584, 561); this.ClientSize = new System.Drawing.Size(616, 733);
this.Name = "Form1"; this.Name = "Form1";
this.Text = "Form1"; this.Text = "Form1";
this.ResumeLayout(false); this.ResumeLayout(false);

View File

@@ -146,7 +146,7 @@ namespace VNCServerList.Services
command.Parameters.AddWithValue("@Description", (object)server.Description ?? DBNull.Value); command.Parameters.AddWithValue("@Description", (object)server.Description ?? DBNull.Value);
command.Parameters.AddWithValue("@Password", (object)server.Password ?? DBNull.Value); command.Parameters.AddWithValue("@Password", (object)server.Password ?? DBNull.Value);
command.Parameters.AddWithValue("@Argument", (object)server.Argument ?? DBNull.Value); command.Parameters.AddWithValue("@Argument", (object)server.Argument ?? DBNull.Value);
command.Parameters.AddWithValue("@Icon", (object)server.Icon ?? DBNull.Value); command.Parameters.Add("@Icon", SqlDbType.Image).Value = (object)server.Icon ?? DBNull.Value;
return command.ExecuteNonQuery() > 0; return command.ExecuteNonQuery() > 0;
} }
@@ -201,7 +201,7 @@ namespace VNCServerList.Services
command.Parameters.AddWithValue("@Description", (object)newServerData.Description ?? DBNull.Value); command.Parameters.AddWithValue("@Description", (object)newServerData.Description ?? DBNull.Value);
command.Parameters.AddWithValue("@Password", (object)newServerData.Password ?? DBNull.Value); command.Parameters.AddWithValue("@Password", (object)newServerData.Password ?? DBNull.Value);
command.Parameters.AddWithValue("@Argument", (object)newServerData.Argument ?? DBNull.Value); command.Parameters.AddWithValue("@Argument", (object)newServerData.Argument ?? DBNull.Value);
command.Parameters.AddWithValue("@Icon", (object)newServerData.Icon ?? DBNull.Value); command.Parameters.Add("@Icon", SqlDbType.Image).Value = (object)newServerData.Icon ?? DBNull.Value;
return command.ExecuteNonQuery() > 0; return command.ExecuteNonQuery() > 0;
} }

View File

@@ -221,7 +221,7 @@
<!-- 숨겨진 템플릿들 --> <!-- 숨겨진 템플릿들 -->
<template id="serverItemTemplate"> <template id="serverItemTemplate">
<div class="server-item px-4 py-3 hover:bg-gray-50 transition duration-200 border-b border-gray-100 relative overflow-hidden group"> <div class="server-item px-4 py-3 hover:bg-gray-50 transition duration-200 border-b border-gray-100 relative overflow-hidden group {{marginClass}}">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex-1 cursor-pointer" onclick="app.connectToServer('{{userName}}', '{{serverIP}}')"> <div class="flex-1 cursor-pointer" onclick="app.connectToServer('{{userName}}', '{{serverIP}}')">
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">

View File

@@ -495,20 +495,33 @@ class VNCServerApp {
const iconFile = document.getElementById('iconFile'); const iconFile = document.getElementById('iconFile');
if (iconFile.files.length > 0) { if (iconFile.files.length > 0) {
const file = iconFile.files[0]; const file = iconFile.files[0];
const reader = new FileReader();
reader.onload = async (e) => { // 파일을 Base64로 변환하는 Promise
// Base64 데이터에서 헤더 제거 (data:image/png;base64, 부분) const base64Promise = new Promise((resolve, reject) => {
const base64Data = e.target.result.split(',')[1]; const reader = new FileReader();
reader.onload = (e) => {
try {
// Base64 데이터에서 헤더 제거 (data:image/png;base64, 부분)
const base64Data = e.target.result.split(',')[1];
resolve(base64Data);
} catch (error) {
reject(error);
}
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
try {
const base64Data = await base64Promise;
serverData.IconBase64 = base64Data; serverData.IconBase64 = base64Data;
} catch (error) {
await this.submitServerData(serverData, form); this.showError('아이콘 파일 처리 중 오류가 발생했습니다: ' + error.message);
}; return;
}
reader.readAsDataURL(file);
} else {
await this.submitServerData(serverData, form);
} }
await this.submitServerData(serverData, form);
} catch (error) { } catch (error) {
this.showError('서버 저장 중 오류가 발생했습니다: ' + error.message); this.showError('서버 저장 중 오류가 발생했습니다: ' + error.message);
} }
@@ -689,28 +702,30 @@ class VNCServerApp {
// 카테고리 헤더 // 카테고리 헤더
if (depth === 0) { if (depth === 0) {
html += `<div class="category-header bg-gray-100 px-4 py-2 border-b border-gray-200"> html += `<div class="category-header bg-gray-100 px-4 py-2 border-b border-gray-200">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between cursor-pointer" onclick="app.toggleCategory('${this.escapeHtml(categoryName)}', ${depth})">
<h3 class="text-lg font-semibold text-gray-800 flex items-center"> <h3 class="text-lg font-semibold text-gray-800 flex items-center">
<svg class="w-5 h-5 mr-2 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-5 h-5 mr-2 text-gray-600 category-icon transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path>
</svg> </svg>
${this.escapeHtml(categoryName)} ${this.escapeHtml(categoryName)}
</h3> </h3>
<span class="text-sm text-gray-500">${categoryData.servers.length}개 서버</span> <span class="text-sm text-gray-500">${categoryData.servers.length}개 서버</span>
</div> </div>
</div>`; </div>
<div id="category-${this.escapeHtml(categoryName)}-${depth}" class="category-content">`;
} else { } else {
html += `<div class="subcategory-header bg-gray-50 px-4 py-2 border-b border-gray-200 ml-${depth * 4}"> html += `<div class="subcategory-header bg-gray-50 px-4 py-2 border-b border-gray-200 ml-${depth * 4}">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between cursor-pointer" onclick="app.toggleCategory('${this.escapeHtml(categoryName)}', ${depth})">
<h4 class="text-md font-medium text-gray-700 flex items-center"> <h4 class="text-md font-medium text-gray-700 flex items-center">
<svg class="w-4 h-4 mr-2 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4 mr-2 text-gray-500 category-icon transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
</svg> </svg>
${this.escapeHtml(categoryName)} ${this.escapeHtml(categoryName)}
</h4> </h4>
<span class="text-sm text-gray-500">${categoryData.servers.length}개 서버</span> <span class="text-sm text-gray-500">${categoryData.servers.length}개 서버</span>
</div> </div>
</div>`; </div>
<div id="category-${this.escapeHtml(categoryName)}-${depth}" class="category-content">`;
} }
// 서버 목록 // 서버 목록
@@ -726,6 +741,9 @@ class VNCServerApp {
// 아이콘 URL 생성 // 아이콘 URL 생성
const iconUrl = `${this.apiBase}/icon/${encodeURIComponent(userName)}/${encodeURIComponent(serverIP)}`; const iconUrl = `${this.apiBase}/icon/${encodeURIComponent(userName)}/${encodeURIComponent(serverIP)}`;
// depth에 따라 margin-left 클래스 적용 (최대 ml-24)
const marginClass = `ml-${Math.min(depth * 4, 24)}`;
const serverData = { const serverData = {
userName: userName, userName: userName,
serverIP: serverIP, serverIP: serverIP,
@@ -733,7 +751,8 @@ class VNCServerApp {
serverCategory: this.escapeHtml(serverCategory), serverCategory: this.escapeHtml(serverCategory),
serverDescription: this.escapeHtml(serverDescription), serverDescription: this.escapeHtml(serverDescription),
serverArgument: this.escapeHtml(serverArgument), serverArgument: this.escapeHtml(serverArgument),
iconUrl: iconUrl iconUrl: iconUrl,
marginClass: marginClass
}; };
html += this.renderTemplate('serverItemTemplate', serverData); html += this.renderTemplate('serverItemTemplate', serverData);
@@ -746,6 +765,8 @@ class VNCServerApp {
html += this.renderCategoryNode(subcategory, categoryData.subcategories[subcategory], depth + 1); html += this.renderCategoryNode(subcategory, categoryData.subcategories[subcategory], depth + 1);
}); });
html += '</div>'; // category-content 닫기
return html; return html;
} }