category toglle 기능 복구
icon 저장 기능 오류 수정 검색 기능 추가
This commit is contained in:
2
Form1.Designer.cs
generated
2
Form1.Designer.cs
generated
@@ -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);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace VNCServerList.Services
|
|||||||
using (var connection = new SqlConnection(_connectionString))
|
using (var connection = new SqlConnection(_connectionString))
|
||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
|
|
||||||
// VNC_ServerList 테이블이 존재하는지 확인
|
// VNC_ServerList 테이블이 존재하는지 확인
|
||||||
string checkTableSql = @"
|
string checkTableSql = @"
|
||||||
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='VNC_ServerList' AND xtype='U')
|
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='VNC_ServerList' AND xtype='U')
|
||||||
@@ -64,12 +64,12 @@ namespace VNCServerList.Services
|
|||||||
public List<VNCServer> GetAllServers()
|
public List<VNCServer> GetAllServers()
|
||||||
{
|
{
|
||||||
var servers = new List<VNCServer>();
|
var servers = new List<VNCServer>();
|
||||||
|
|
||||||
using (var connection = new SqlConnection(_connectionString))
|
using (var connection = new SqlConnection(_connectionString))
|
||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
string sql = "SELECT * FROM VNC_ServerList ORDER BY [User]";
|
string sql = "SELECT * FROM VNC_ServerList ORDER BY [User]";
|
||||||
|
|
||||||
using (var command = new SqlCommand(sql, connection))
|
using (var command = new SqlCommand(sql, connection))
|
||||||
using (var reader = command.ExecuteReader())
|
using (var reader = command.ExecuteReader())
|
||||||
{
|
{
|
||||||
@@ -89,7 +89,7 @@ namespace VNCServerList.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return servers;
|
return servers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,12 +99,12 @@ namespace VNCServerList.Services
|
|||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
string sql = "SELECT * FROM VNC_ServerList WHERE [User] = @User AND [IP] = @IP";
|
string sql = "SELECT * FROM VNC_ServerList WHERE [User] = @User AND [IP] = @IP";
|
||||||
|
|
||||||
using (var command = new SqlCommand(sql, connection))
|
using (var command = new SqlCommand(sql, connection))
|
||||||
{
|
{
|
||||||
command.Parameters.AddWithValue("@User", user);
|
command.Parameters.AddWithValue("@User", user);
|
||||||
command.Parameters.AddWithValue("@IP", ip);
|
command.Parameters.AddWithValue("@IP", ip);
|
||||||
|
|
||||||
using (var reader = command.ExecuteReader())
|
using (var reader = command.ExecuteReader())
|
||||||
{
|
{
|
||||||
if (reader.Read())
|
if (reader.Read())
|
||||||
@@ -124,7 +124,7 @@ namespace VNCServerList.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ namespace VNCServerList.Services
|
|||||||
string sql = @"
|
string sql = @"
|
||||||
INSERT INTO VNC_ServerList ([User], [IP], [Category], [Title],[Description], [Password], [Argument], [Icon])
|
INSERT INTO VNC_ServerList ([User], [IP], [Category], [Title],[Description], [Password], [Argument], [Icon])
|
||||||
VALUES (@User, @IP, @Category, @Title,@Description, @Password, @Argument, @Icon)";
|
VALUES (@User, @IP, @Category, @Title,@Description, @Password, @Argument, @Icon)";
|
||||||
|
|
||||||
using (var command = new SqlCommand(sql, connection))
|
using (var command = new SqlCommand(sql, connection))
|
||||||
{
|
{
|
||||||
command.Parameters.AddWithValue("@User", server.User);
|
command.Parameters.AddWithValue("@User", server.User);
|
||||||
@@ -146,8 +146,8 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +163,7 @@ namespace VNCServerList.Services
|
|||||||
SET [Category] = @Category, [Title] = @Title, [Description] = @Description,
|
SET [Category] = @Category, [Title] = @Title, [Description] = @Description,
|
||||||
[Password] = @Password, [Argument] = @Argument, [Icon] = @Icon
|
[Password] = @Password, [Argument] = @Argument, [Icon] = @Icon
|
||||||
WHERE [User] = @User AND [IP] = @IP";
|
WHERE [User] = @User AND [IP] = @IP";
|
||||||
|
|
||||||
using (var command = new SqlCommand(sql, connection))
|
using (var command = new SqlCommand(sql, connection))
|
||||||
{
|
{
|
||||||
command.Parameters.AddWithValue("@User", server.User);
|
command.Parameters.AddWithValue("@User", server.User);
|
||||||
@@ -174,7 +174,7 @@ namespace VNCServerList.Services
|
|||||||
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.AddWithValue("@Icon", (object)server.Icon ?? DBNull.Value);
|
||||||
|
|
||||||
return command.ExecuteNonQuery() > 0;
|
return command.ExecuteNonQuery() > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,7 +190,7 @@ namespace VNCServerList.Services
|
|||||||
SET [IP] = @NewIP, [Category] = @Category, [Title] = @Title, [Description] = @Description,
|
SET [IP] = @NewIP, [Category] = @Category, [Title] = @Title, [Description] = @Description,
|
||||||
[Password] = @Password, [Argument] = @Argument, [Icon] = @Icon
|
[Password] = @Password, [Argument] = @Argument, [Icon] = @Icon
|
||||||
WHERE [User] = @User AND [IP] = @OriginalIP";
|
WHERE [User] = @User AND [IP] = @OriginalIP";
|
||||||
|
|
||||||
using (var command = new SqlCommand(sql, connection))
|
using (var command = new SqlCommand(sql, connection))
|
||||||
{
|
{
|
||||||
command.Parameters.AddWithValue("@User", originalUser);
|
command.Parameters.AddWithValue("@User", originalUser);
|
||||||
@@ -201,8 +201,8 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,7 +214,7 @@ namespace VNCServerList.Services
|
|||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
string sql = "DELETE FROM VNC_ServerList WHERE [User] = @User AND [IP] = @IP";
|
string sql = "DELETE FROM VNC_ServerList WHERE [User] = @User AND [IP] = @IP";
|
||||||
|
|
||||||
using (var command = new SqlCommand(sql, connection))
|
using (var command = new SqlCommand(sql, connection))
|
||||||
{
|
{
|
||||||
command.Parameters.AddWithValue("@User", user);
|
command.Parameters.AddWithValue("@User", user);
|
||||||
@@ -224,4 +224,4 @@ namespace VNCServerList.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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">
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user