feat: MachineBridge 추가 및 fetch API를 HostObject 호출로 전환
- WebView2 HostObject 기반 MachineBridge 브릿지 클래스 추가 - MachineBridge.cs (메인), Login, Dashboard, Todo, Common, Jobreport, Kuntae, Project 모듈 - WebSocketServer.cs 추가 (실시간 통신용) - fDashboardNew 다이얼로그 추가 - Jobreport/index.html, Project/index.html의 fetch API를 machine HostObject 호출로 전환 - DashBoardController.cs의 gcode null 처리 추가 - 사용하지 않는 파일 삭제 (navigation.html, common-nav.js, navigation.js, _add_to_project.py, _project_updater.js) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
63
Project/Dialog/fDashboardNew.Designer.cs
generated
Normal file
63
Project/Dialog/fDashboardNew.Designer.cs
generated
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
namespace Project.Dialog
|
||||||
|
{
|
||||||
|
partial class fDashboardNew
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.label1 = new System.Windows.Forms.Label();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// label1
|
||||||
|
//
|
||||||
|
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.label1.Font = new System.Drawing.Font("맑은 고딕", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
|
||||||
|
this.label1.ForeColor = System.Drawing.Color.DimGray;
|
||||||
|
this.label1.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.label1.Name = "label1";
|
||||||
|
this.label1.Size = new System.Drawing.Size(1063, 567);
|
||||||
|
this.label1.TabIndex = 0;
|
||||||
|
this.label1.Text = "요약 화면 구성 중 입니다.\r\n\r\n업무일지는 \"관리->업무일지->목록\" 을 사용하세요";
|
||||||
|
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
|
this.label1.Click += new System.EventHandler(this.label1_Click);
|
||||||
|
//
|
||||||
|
// fDashboard
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(1063, 567);
|
||||||
|
this.Controls.Add(this.label1);
|
||||||
|
this.Name = "fDashboard";
|
||||||
|
this.Text = "요약";
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label label1;
|
||||||
|
}
|
||||||
|
}
|
||||||
119
Project/Dialog/fDashboardNew.cs
Normal file
119
Project/Dialog/fDashboardNew.cs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
using FCM0000.Mail;
|
||||||
|
using FCOMMON;
|
||||||
|
using Microsoft.Web.WebView2.Core;
|
||||||
|
using Microsoft.Web.WebView2.WinForms;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Data;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace Project.Dialog
|
||||||
|
{
|
||||||
|
public partial class fDashboardNew : fBase
|
||||||
|
{
|
||||||
|
private Web.WebSocketServer _wsServer;
|
||||||
|
private WebView2 webView;
|
||||||
|
public fDashboardNew()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
|
||||||
|
InitializeWebView2();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_wsServer = new Web.WebSocketServer("http://localhost:8081/", this);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Failed to start WebSocket Server (Port 8081). Run as Admin or allow port.\n" + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool loadok = false;
|
||||||
|
public void RefreshView()
|
||||||
|
{
|
||||||
|
if (loadok)
|
||||||
|
webView.Reload();
|
||||||
|
}
|
||||||
|
private void InitializeWebView2()
|
||||||
|
{
|
||||||
|
// 수동으로 WebView2 컨트롤 생성
|
||||||
|
this.webView = new WebView2();
|
||||||
|
|
||||||
|
// 기본 속성 설정
|
||||||
|
this.webView.CreationProperties = null;
|
||||||
|
this.webView.DefaultBackgroundColor = Color.White;
|
||||||
|
this.webView.Dock = DockStyle.Fill;
|
||||||
|
this.webView.Location = new Point(0, 0);
|
||||||
|
this.webView.Name = "webView21";
|
||||||
|
this.webView.Size = new Size(800, 600);
|
||||||
|
this.webView.TabIndex = 0;
|
||||||
|
this.webView.ZoomFactor = 1D;
|
||||||
|
|
||||||
|
// 폼에 추가
|
||||||
|
this.Controls.Add(this.webView);
|
||||||
|
|
||||||
|
// 비동기 초기화
|
||||||
|
InitializeAsync();
|
||||||
|
}
|
||||||
|
private async void InitializeAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Fixed Version 경로 설정
|
||||||
|
string runtimePath = Path.Combine(Application.StartupPath, "WebView2Runtime");
|
||||||
|
|
||||||
|
if (Directory.Exists(runtimePath))
|
||||||
|
{
|
||||||
|
var env = await CoreWebView2Environment.CreateAsync(runtimePath);
|
||||||
|
await this.webView.EnsureCoreWebView2Async(env);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 시스템에 설치된 WebView2 사용
|
||||||
|
await this.webView.EnsureCoreWebView2Async();
|
||||||
|
}
|
||||||
|
|
||||||
|
var wwwroot = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Web", "wwwroot");
|
||||||
|
webView.CoreWebView2.SetVirtualHostNameToFolderMapping(
|
||||||
|
"hmi.local",
|
||||||
|
wwwroot,
|
||||||
|
CoreWebView2HostResourceAccessKind.Allow);
|
||||||
|
|
||||||
|
|
||||||
|
// 2. Inject Native Object
|
||||||
|
webView.CoreWebView2.AddHostObjectToScript("machine", new Web.MachineBridge(this));
|
||||||
|
|
||||||
|
Pub.WebServiceURL = "http://hmi.local";
|
||||||
|
|
||||||
|
// OWIN 서버의 DashBoard 페이지로 연결
|
||||||
|
if (FCOMMON.info.Login.no.isEmpty())
|
||||||
|
webView.Source = new Uri($"{Pub.WebServiceURL}/login.html");
|
||||||
|
else
|
||||||
|
webView.Source = new Uri($"{Pub.WebServiceURL}/DashBoard");
|
||||||
|
|
||||||
|
label1.Visible = false;
|
||||||
|
loadok = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"WebView2 초기화 실패: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected override void OnLoad(EventArgs e)
|
||||||
|
{
|
||||||
|
base.OnLoad(e);
|
||||||
|
EnsureVisibleAndUsableSize();
|
||||||
|
}
|
||||||
|
private void label1_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
120
Project/Dialog/fDashboardNew.resx
Normal file
120
Project/Dialog/fDashboardNew.resx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
@@ -259,6 +259,12 @@
|
|||||||
<Compile Include="Dialog\fCommon.Designer.cs">
|
<Compile Include="Dialog\fCommon.Designer.cs">
|
||||||
<DependentUpon>fCommon.cs</DependentUpon>
|
<DependentUpon>fCommon.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Dialog\fDashboardNew.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Dialog\fDashboardNew.Designer.cs">
|
||||||
|
<DependentUpon>fDashboardNew.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Dialog\fHolyday.cs">
|
<Compile Include="Dialog\fHolyday.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -424,6 +430,14 @@
|
|||||||
<Compile Include="Web\Controllers\ResultController.cs" />
|
<Compile Include="Web\Controllers\ResultController.cs" />
|
||||||
<Compile Include="Web\Controllers\SettingController.cs" />
|
<Compile Include="Web\Controllers\SettingController.cs" />
|
||||||
<Compile Include="Web\Controllers\TodoController.cs" />
|
<Compile Include="Web\Controllers\TodoController.cs" />
|
||||||
|
<Compile Include="Web\MachineBridge\MachineBridge.cs" />
|
||||||
|
<Compile Include="Web\MachineBridge\MachineBridge.Login.cs" />
|
||||||
|
<Compile Include="Web\MachineBridge\MachineBridge.Dashboard.cs" />
|
||||||
|
<Compile Include="Web\MachineBridge\MachineBridge.Todo.cs" />
|
||||||
|
<Compile Include="Web\MachineBridge\MachineBridge.Common.cs" />
|
||||||
|
<Compile Include="Web\MachineBridge\MachineBridge.Jobreport.cs" />
|
||||||
|
<Compile Include="Web\MachineBridge\MachineBridge.Kuntae.cs" />
|
||||||
|
<Compile Include="Web\MachineBridge\MachineBridge.Project.cs" />
|
||||||
<Compile Include="Web\Model\PageModel.cs" />
|
<Compile Include="Web\Model\PageModel.cs" />
|
||||||
<Compile Include="Web\Model\ProjectModel.cs" />
|
<Compile Include="Web\Model\ProjectModel.cs" />
|
||||||
<Compile Include="Web\Model\TodoModel.cs" />
|
<Compile Include="Web\Model\TodoModel.cs" />
|
||||||
@@ -432,6 +446,7 @@
|
|||||||
<Compile Include="Settings.cs" />
|
<Compile Include="Settings.cs" />
|
||||||
<Compile Include="SqlServerTypes\Loader.cs" />
|
<Compile Include="SqlServerTypes\Loader.cs" />
|
||||||
<Compile Include="StateMachine\ReportUserData.cs" />
|
<Compile Include="StateMachine\ReportUserData.cs" />
|
||||||
|
<Compile Include="Web\WebSocketServer.cs" />
|
||||||
<Compile Include="_Common\fADSUserList.cs">
|
<Compile Include="_Common\fADSUserList.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -495,6 +510,9 @@
|
|||||||
<EmbeddedResource Include="Dialog\fCommon.resx">
|
<EmbeddedResource Include="Dialog\fCommon.resx">
|
||||||
<DependentUpon>fCommon.cs</DependentUpon>
|
<DependentUpon>fCommon.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Dialog\fDashboardNew.resx">
|
||||||
|
<DependentUpon>fDashboardNew.cs</DependentUpon>
|
||||||
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Dialog\fHolyday.resx">
|
<EmbeddedResource Include="Dialog\fHolyday.resx">
|
||||||
<DependentUpon>fHolyday.cs</DependentUpon>
|
<DependentUpon>fHolyday.cs</DependentUpon>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
@@ -686,9 +704,6 @@
|
|||||||
<None Include="Web\wwwroot\js\common-navigation.js">
|
<None Include="Web\wwwroot\js\common-navigation.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Include="Web\wwwroot\js\navigation.js">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Include="Web\wwwroot\js\tailwind-config.js">
|
<None Include="Web\wwwroot\js\tailwind-config.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
@@ -707,9 +722,6 @@
|
|||||||
<None Include="Web\wwwroot\Jobreport\index.html">
|
<None Include="Web\wwwroot\Jobreport\index.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Include="Web\wwwroot\js\common-nav.js">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Include="Web\wwwroot\Kuntae\index.html">
|
<None Include="Web\wwwroot\Kuntae\index.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ namespace Project.Web.Controllers
|
|||||||
var cs = Properties.Settings.Default.gwcs;// "Data Source=K4FASQL.kr.ds.amkor.com,50150;Initial Catalog=EE;Persist Security Info=True;User ID=eeadm;Password=uJnU8a8q&DJ+ug-D!";
|
var cs = Properties.Settings.Default.gwcs;// "Data Source=K4FASQL.kr.ds.amkor.com,50150;Initial Catalog=EE;Persist Security Info=True;User ID=eeadm;Password=uJnU8a8q&DJ+ug-D!";
|
||||||
var cn = new System.Data.SqlClient.SqlConnection(cs);
|
var cn = new System.Data.SqlClient.SqlConnection(cs);
|
||||||
var cmd = new System.Data.SqlClient.SqlCommand(sql, cn);
|
var cmd = new System.Data.SqlClient.SqlCommand(sql, cn);
|
||||||
cmd.Parameters.AddWithValue("gcode", FCOMMON.info.Login.gcode);
|
cmd.Parameters.AddWithValue("gcode", FCOMMON.info.Login.gcode ?? string.Empty);
|
||||||
var da = new System.Data.SqlClient.SqlDataAdapter(cmd);
|
var da = new System.Data.SqlClient.SqlDataAdapter(cmd);
|
||||||
var dt = new System.Data.DataTable();
|
var dt = new System.Data.DataTable();
|
||||||
da.Fill(dt);
|
da.Fill(dt);
|
||||||
|
|||||||
233
Project/Web/MachineBridge/MachineBridge.Common.cs
Normal file
233
Project/Web/MachineBridge/MachineBridge.Common.cs
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using FCOMMON;
|
||||||
|
|
||||||
|
namespace Project.Web
|
||||||
|
{
|
||||||
|
public partial class MachineBridge
|
||||||
|
{
|
||||||
|
#region Common API
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 네비게이션 메뉴 조회
|
||||||
|
/// </summary>
|
||||||
|
public string GetNavigationMenu()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var menuItems = new[]
|
||||||
|
{
|
||||||
|
new { key = "dashboard", title = "대시보드", url = "/DashBoard/index.html", icon = "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z", isVisible = true, sortOrder = 1 },
|
||||||
|
new { key = "common", title = "공용코드", url = "/Common.html", icon = "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z", isVisible = true, sortOrder = 2 },
|
||||||
|
new { key = "jobreport", title = "업무일지", url = "/Jobreport/index.html", icon = "M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2", isVisible = true, sortOrder = 3 },
|
||||||
|
new { key = "kuntae", title = "근태관리", url = "/Kuntae/index.html", icon = "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z", isVisible = true, sortOrder = 4 },
|
||||||
|
new { key = "todo", title = "할일관리", url = "/Todo/index.html", icon = "M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2M12 12l2 2 4-4", isVisible = true, sortOrder = 5 },
|
||||||
|
new { key = "project", title = "프로젝트", url = "/Project/index.html", icon = "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", isVisible = true, sortOrder = 6 }
|
||||||
|
};
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = menuItems, Message = "메뉴 정보를 성공적으로 가져왔습니다." });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Data = (object)null, Message = "메뉴 정보를 가져오는 중 오류가 발생했습니다: " + ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 공용코드 그룹 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string Common_GetGroups()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = "select code, svalue, memo from common WITH (nolock) " +
|
||||||
|
"where gcode = @gcode and grp = '99' " +
|
||||||
|
"order by code";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(dt, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Common_GetGroups 오류: {ex.Message}");
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 공용코드 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string Common_GetList(string grp)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(grp)) grp = "99";
|
||||||
|
|
||||||
|
var sql = "select * from common where gcode = @gcode and grp = @grp order by code, svalue";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
cmd.Parameters.AddWithValue("@grp", grp);
|
||||||
|
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(dt, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Common_GetList 오류: {ex.Message}");
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 공용코드 저장
|
||||||
|
/// </summary>
|
||||||
|
public string Common_Save(int idx, string grp, string code, string svalue, int ivalue, float fvalue, string svalue2, string memo)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var sql = string.Empty;
|
||||||
|
var cmd = new SqlCommand();
|
||||||
|
cmd.Connection = cn;
|
||||||
|
|
||||||
|
if (idx > 0)
|
||||||
|
{
|
||||||
|
// 업데이트
|
||||||
|
sql = @"UPDATE common SET
|
||||||
|
grp = @grp,
|
||||||
|
code = @code,
|
||||||
|
svalue = @svalue,
|
||||||
|
ivalue = @ivalue,
|
||||||
|
fvalue = @fvalue,
|
||||||
|
svalue2 = @svalue2,
|
||||||
|
memo = @memo,
|
||||||
|
wuid = @wuid,
|
||||||
|
wdate = GETDATE()
|
||||||
|
WHERE idx = @idx AND gcode = @gcode";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 신규 추가
|
||||||
|
sql = @"INSERT INTO common (gcode, grp, code, svalue, ivalue, fvalue, svalue2, memo, wuid, wdate)
|
||||||
|
VALUES (@gcode, @grp, @code, @svalue, @ivalue, @fvalue, @svalue2, @memo, @wuid, GETDATE())";
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.CommandText = sql;
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
cmd.Parameters.AddWithValue("@grp", grp ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@code", code ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@svalue", svalue ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@ivalue", ivalue);
|
||||||
|
cmd.Parameters.AddWithValue("@fvalue", fvalue);
|
||||||
|
cmd.Parameters.AddWithValue("@svalue2", svalue2 ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@memo", memo ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
|
||||||
|
|
||||||
|
if (idx > 0)
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("@idx", idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
cn.Open();
|
||||||
|
var result = cmd.ExecuteNonQuery();
|
||||||
|
cn.Close();
|
||||||
|
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "저장되었습니다." : "저장에 실패했습니다." });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "오류가 발생했습니다: " + ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 공용코드 삭제
|
||||||
|
/// </summary>
|
||||||
|
public string Common_Delete(int idx)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var sql = "DELETE FROM common WHERE idx = @idx AND gcode = @gcode";
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
|
||||||
|
cmd.Parameters.AddWithValue("@idx", idx);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
|
||||||
|
cn.Open();
|
||||||
|
var result = cmd.ExecuteNonQuery();
|
||||||
|
cn.Close();
|
||||||
|
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "삭제되었습니다." : "삭제에 실패했습니다." });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "오류가 발생했습니다: " + ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 현재 로그인 사용자 정보 조회
|
||||||
|
/// </summary>
|
||||||
|
public string GetCurrentUser()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인되지 않은 상태입니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Data = new
|
||||||
|
{
|
||||||
|
id = info.Login.no,
|
||||||
|
name = info.Login.nameK,
|
||||||
|
userName = info.Login.nameK,
|
||||||
|
email = info.Login.email,
|
||||||
|
dept = info.Login.dept,
|
||||||
|
gcode = info.Login.gcode
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
268
Project/Web/MachineBridge/MachineBridge.Dashboard.cs
Normal file
268
Project/Web/MachineBridge/MachineBridge.Dashboard.cs
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using FCOMMON;
|
||||||
|
|
||||||
|
namespace Project.Web
|
||||||
|
{
|
||||||
|
public partial class MachineBridge
|
||||||
|
{
|
||||||
|
#region Dashboard API
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 오늘 휴가 인원 수 조회
|
||||||
|
/// </summary>
|
||||||
|
public string TodayCountH()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = "select count(*) from EETGW_HolydayRequest WITH (nolock) " +
|
||||||
|
" where gcode = @gcode and isnull(conf,0) = 1 " +
|
||||||
|
" and sdate <= convert(varchar(10),GETDATE(),120) and edate >= convert(varchar(10),GETDATE(),120)";
|
||||||
|
|
||||||
|
var cn = DBM.getCn();
|
||||||
|
cn.Open();
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.Add("gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
var cnt = (int)cmd.ExecuteScalar();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return cnt.ToString();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"TodayCountH 오류: {ex.Message}");
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 휴가요청 대기 건수 조회
|
||||||
|
/// </summary>
|
||||||
|
public string GetHolydayRequestCount()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = "select count(*) from EETGW_HolydayRequest WITH (nolock) " +
|
||||||
|
" where gcode = @gcode and isnull(conf,0) = 0";
|
||||||
|
|
||||||
|
var cn = DBM.getCn();
|
||||||
|
cn.Open();
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.Add("gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
var cnt = (int)cmd.ExecuteScalar();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { HOLY = cnt, Message = "" });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { HOLY = 0, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 현재 출근 대상 인원 수 조회
|
||||||
|
/// </summary>
|
||||||
|
public string GetCurrentUserCount()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = "select count(*) from vGroupUser WITH (nolock) " +
|
||||||
|
" where gcode = @gcode and useUserState = 1 and useJobReport = 1" +
|
||||||
|
" and id not in (select uid from vEETGW_TodayNoneWorkUser where gcode = @gcode and kunmu = 0)";
|
||||||
|
|
||||||
|
var cn = DBM.getCn();
|
||||||
|
cn.Open();
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.Add("gcode", SqlDbType.VarChar).Value = info.Login.gcode;
|
||||||
|
var cnt = (int)cmd.ExecuteScalar();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Count = cnt, Message = "" });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Count = 0, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 구매요청 대기 건수 조회 (NR, CR)
|
||||||
|
/// </summary>
|
||||||
|
public string GetPurchaseWaitCount()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DBM.GetPurchaseWaitCount(info.Login.gcode, out int cnt1, out int cnt2);
|
||||||
|
return JsonConvert.SerializeObject(new { NR = cnt1, CR = cnt2, Message = "" });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { NR = 0, CR = 0, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 휴가자 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string GetHolyUser()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = " select uid,type,cate,sdate,edate,title,dbo.getusername(uid) as name " +
|
||||||
|
" from vEETGW_TodayNoneWorkUser WITH (nolock)" +
|
||||||
|
" where gcode = @gcode and kunmu=0";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("gcode", info.Login.gcode ?? "");
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(dt, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"GetHolyUser 오류: {ex.Message}");
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 휴가요청 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string GetHolyRequestUser()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = " select uid,cate,sdate,edate,HolyReason,Users.name,holydays,holytimes,remark " +
|
||||||
|
" from EETGW_HolydayRequest WITH (nolock) INNER JOIN " +
|
||||||
|
" Users ON EETGW_HolydayRequest.uid = Users.id " +
|
||||||
|
" where EETGW_HolydayRequest.gcode = @gcode" +
|
||||||
|
" and isnull(conf,0) = 0";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("gcode", info.Login.gcode);
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(dt, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"GetHolyRequestUser 오류: {ex.Message}");
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 출근 대상자 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string GetPresentUserList()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = "select * from vGroupUser WITH (nolock) " +
|
||||||
|
" where gcode = @gcode and useUserState = 1 and useJobReport = 1" +
|
||||||
|
" and id not in (select uid from vEETGW_TodayNoneWorkUser where gcode = @gcode and kunmu = 0)";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("gcode", info.Login.gcode);
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(dt, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"GetPresentUserList 오류: {ex.Message}");
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 구매요청(NR) 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string GetPurchaseNRList()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = "select pdate, process, pumname, pumscale, pumunit, pumqtyreq, pumprice, pumamt from Purchase WITH (nolock) where gcode = @gcode and state = '---' order by pdate desc";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("gcode", info.Login.gcode);
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(dt, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"GetPurchaseNRList 오류: {ex.Message}");
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 구매요청(CR) 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string GetPurchaseCRList()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = "select pdate, process, pumname, pumscale, pumunit, pumqtyreq, pumprice, pumamt " +
|
||||||
|
" from EETGW_PurchaseCR WITH (nolock) " +
|
||||||
|
" where gcode = @gcode and state = '---'" +
|
||||||
|
" order by pdate desc";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("gcode", info.Login.gcode);
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(dt, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"GetPurchaseCRList 오류: {ex.Message}");
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
260
Project/Web/MachineBridge/MachineBridge.Jobreport.cs
Normal file
260
Project/Web/MachineBridge/MachineBridge.Jobreport.cs
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using FCOMMON;
|
||||||
|
|
||||||
|
namespace Project.Web
|
||||||
|
{
|
||||||
|
public partial class MachineBridge
|
||||||
|
{
|
||||||
|
#region Jobreport API
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 업무일지 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string Jobreport_GetList(string sd, string ed, string uid, string cate, string doit)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = @"SELECT j.idx, j.jdate, j.uid, j.cate, j.title, j.doit, j.remark, j.jfrom, j.jto,
|
||||||
|
u.name as userName, j.wdate
|
||||||
|
FROM EETGW_Jobreport j WITH (nolock)
|
||||||
|
LEFT JOIN Users u ON j.uid = u.id
|
||||||
|
WHERE j.gcode = @gcode";
|
||||||
|
|
||||||
|
var parameters = new List<SqlParameter>();
|
||||||
|
parameters.Add(new SqlParameter("@gcode", info.Login.gcode));
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(sd))
|
||||||
|
{
|
||||||
|
sql += " AND j.jdate >= @sd";
|
||||||
|
parameters.Add(new SqlParameter("@sd", sd));
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(ed))
|
||||||
|
{
|
||||||
|
sql += " AND j.jdate <= @ed";
|
||||||
|
parameters.Add(new SqlParameter("@ed", ed));
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(uid))
|
||||||
|
{
|
||||||
|
sql += " AND j.uid = @uid";
|
||||||
|
parameters.Add(new SqlParameter("@uid", uid));
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(cate))
|
||||||
|
{
|
||||||
|
sql += " AND j.cate = @cate";
|
||||||
|
parameters.Add(new SqlParameter("@cate", cate));
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(doit))
|
||||||
|
{
|
||||||
|
sql += " AND j.doit = @doit";
|
||||||
|
parameters.Add(new SqlParameter("@doit", doit));
|
||||||
|
}
|
||||||
|
|
||||||
|
sql += " ORDER BY j.jdate DESC, j.idx DESC";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddRange(parameters.ToArray());
|
||||||
|
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = dt }, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 업무일지 사용자 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string Jobreport_GetUsers()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = @"SELECT u.id, u.name
|
||||||
|
FROM vGroupUser u WITH (nolock)
|
||||||
|
WHERE u.gcode = @gcode AND u.useJobReport = 1
|
||||||
|
ORDER BY u.name";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(dt, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Jobreport_GetUsers 오류: {ex.Message}");
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 업무일지 상세 조회
|
||||||
|
/// </summary>
|
||||||
|
public string Jobreport_GetDetail(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = @"SELECT j.*, u.name as userName
|
||||||
|
FROM EETGW_Jobreport j WITH (nolock)
|
||||||
|
LEFT JOIN Users u ON j.uid = u.id
|
||||||
|
WHERE j.idx = @idx AND j.gcode = @gcode";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@idx", id);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
if (dt.Rows.Count > 0)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = dt.Rows[0] }, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "데이터를 찾을 수 없습니다." });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 업무일지 추가
|
||||||
|
/// </summary>
|
||||||
|
public string Jobreport_Add(string jdate, string cate, string title, string doit, string remark, string jfrom, string jto)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = @"INSERT INTO EETGW_Jobreport (gcode, uid, jdate, cate, title, doit, remark, jfrom, jto, wuid, wdate)
|
||||||
|
VALUES (@gcode, @uid, @jdate, @cate, @title, @doit, @remark, @jfrom, @jto, @wuid, GETDATE());
|
||||||
|
SELECT SCOPE_IDENTITY();";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
cmd.Parameters.AddWithValue("@uid", info.Login.no);
|
||||||
|
cmd.Parameters.AddWithValue("@jdate", jdate ?? DateTime.Now.ToString("yyyy-MM-dd"));
|
||||||
|
cmd.Parameters.AddWithValue("@cate", cate ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@title", title ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@doit", doit ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@remark", remark ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@jfrom", jfrom ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@jto", jto ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
|
||||||
|
|
||||||
|
cn.Open();
|
||||||
|
var newId = Convert.ToInt32(cmd.ExecuteScalar());
|
||||||
|
cn.Close();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Message = "저장되었습니다.", Data = new { idx = newId } });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 업무일지 수정
|
||||||
|
/// </summary>
|
||||||
|
public string Jobreport_Edit(int idx, string jdate, string cate, string title, string doit, string remark, string jfrom, string jto)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = @"UPDATE EETGW_Jobreport SET
|
||||||
|
jdate = @jdate, cate = @cate, title = @title, doit = @doit,
|
||||||
|
remark = @remark, jfrom = @jfrom, jto = @jto,
|
||||||
|
wuid = @wuid, wdate = GETDATE()
|
||||||
|
WHERE idx = @idx AND gcode = @gcode";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@idx", idx);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
cmd.Parameters.AddWithValue("@jdate", jdate ?? DateTime.Now.ToString("yyyy-MM-dd"));
|
||||||
|
cmd.Parameters.AddWithValue("@cate", cate ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@title", title ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@doit", doit ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@remark", remark ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@jfrom", jfrom ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@jto", jto ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
|
||||||
|
|
||||||
|
cn.Open();
|
||||||
|
var result = cmd.ExecuteNonQuery();
|
||||||
|
cn.Close();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "수정되었습니다." : "수정에 실패했습니다." });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 업무일지 삭제
|
||||||
|
/// </summary>
|
||||||
|
public string Jobreport_Delete(int idx)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = "DELETE FROM EETGW_Jobreport WHERE idx = @idx AND gcode = @gcode";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@idx", idx);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
|
||||||
|
cn.Open();
|
||||||
|
var result = cmd.ExecuteNonQuery();
|
||||||
|
cn.Close();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "삭제되었습니다." : "삭제에 실패했습니다." });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
95
Project/Web/MachineBridge/MachineBridge.Kuntae.cs
Normal file
95
Project/Web/MachineBridge/MachineBridge.Kuntae.cs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using FCOMMON;
|
||||||
|
|
||||||
|
namespace Project.Web
|
||||||
|
{
|
||||||
|
public partial class MachineBridge
|
||||||
|
{
|
||||||
|
#region Kuntae API
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 근태 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string Kuntae_GetList(string sd, string ed)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = @"SELECT k.*, u.name as userName
|
||||||
|
FROM EETGW_Kuntae k WITH (nolock)
|
||||||
|
LEFT JOIN Users u ON k.uid = u.id
|
||||||
|
WHERE k.gcode = @gcode AND k.uid = @uid";
|
||||||
|
|
||||||
|
var parameters = new List<SqlParameter>();
|
||||||
|
parameters.Add(new SqlParameter("@gcode", info.Login.gcode));
|
||||||
|
parameters.Add(new SqlParameter("@uid", info.Login.no));
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(sd))
|
||||||
|
{
|
||||||
|
sql += " AND k.kdate >= @sd";
|
||||||
|
parameters.Add(new SqlParameter("@sd", sd));
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(ed))
|
||||||
|
{
|
||||||
|
sql += " AND k.kdate <= @ed";
|
||||||
|
parameters.Add(new SqlParameter("@ed", ed));
|
||||||
|
}
|
||||||
|
|
||||||
|
sql += " ORDER BY k.kdate DESC";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddRange(parameters.ToArray());
|
||||||
|
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = dt }, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 근태 삭제
|
||||||
|
/// </summary>
|
||||||
|
public string Kuntae_Delete(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = "DELETE FROM EETGW_Kuntae WHERE idx = @idx AND gcode = @gcode AND uid = @uid";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@idx", id);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
cmd.Parameters.AddWithValue("@uid", info.Login.no);
|
||||||
|
|
||||||
|
cn.Open();
|
||||||
|
var result = cmd.ExecuteNonQuery();
|
||||||
|
cn.Close();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "삭제되었습니다." : "삭제에 실패했습니다." });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
293
Project/Web/MachineBridge/MachineBridge.Login.cs
Normal file
293
Project/Web/MachineBridge/MachineBridge.Login.cs
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using FCOMMON;
|
||||||
|
|
||||||
|
namespace Project.Web
|
||||||
|
{
|
||||||
|
public partial class MachineBridge
|
||||||
|
{
|
||||||
|
#region Login API
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 로그인 처리 (fLogin_WB.cs의 button1_Click 로직 참고)
|
||||||
|
/// </summary>
|
||||||
|
public string Login(string gcode, string id, string password, bool rememberMe)
|
||||||
|
{
|
||||||
|
var result = new LoginResult();
|
||||||
|
DateTime dt = DateTime.Now;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(id))
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message = "사용자 ID를 입력하세요.";
|
||||||
|
return JsonConvert.SerializeObject(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(password))
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message = "비밀번호를 입력하세요.";
|
||||||
|
return JsonConvert.SerializeObject(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(gcode))
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message = "소속 부서를 선택하세요.";
|
||||||
|
return JsonConvert.SerializeObject(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 부서명 조회
|
||||||
|
var deptName = DBM.ExecuteScalar($"SELECT dept FROM UserGroup WHERE gcode = '{gcode}'") ?? "";
|
||||||
|
|
||||||
|
var encpass = Pub.MakePasswordEnc(password.Trim());
|
||||||
|
|
||||||
|
// 개발자 계정 처리
|
||||||
|
if (id.ToUpper().Equals("DEV"))
|
||||||
|
{
|
||||||
|
if (!password.Equals("123"))
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message = "암호가 일치하지 않습니다.";
|
||||||
|
return JsonConvert.SerializeObject(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetDevLoginInfo(gcode, deptName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 일반 사용자 로그인 처리
|
||||||
|
var taGrpUser = new dsMSSQLTableAdapters.EETGW_GroupUserTableAdapter();
|
||||||
|
var drGrpUser = taGrpUser.GetbyID(gcode, id).FirstOrDefault();
|
||||||
|
|
||||||
|
if (drGrpUser == null)
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message = $"입력한 사용자 계정이 존재하지 않습니다.\n\n담당부서명:{deptName}\n부서코드:{gcode}\n\n접속 부서를 확인하시고 관리자 문의 하세요";
|
||||||
|
return JsonConvert.SerializeObject(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drGrpUser.level == 0)
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message = "해당 계정이 활성화되지 않았습니다.\n계정 담당자 문의하세요";
|
||||||
|
return JsonConvert.SerializeObject(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 사용자 테이블에서 암호 확인
|
||||||
|
var taUser = new dsMSSQLTableAdapters.UsersTableAdapter();
|
||||||
|
var drUser = taUser.GetID(id).FirstOrDefault();
|
||||||
|
|
||||||
|
if (drUser == null)
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message = $"입력한 사용자 계정이 존재하지 않습니다.\n\n담당부서명:{deptName}\n부서코드:{gcode}\n\n접속 부서를 확인하시고 관리자 문의 하세요";
|
||||||
|
return JsonConvert.SerializeObject(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drUser.password.Equals(encpass))
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message = "암호가 일치하지 않습니다.\n암호를 분실했을 경우에는 계정담당자에 초기화를 요청 하세요";
|
||||||
|
return JsonConvert.SerializeObject(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 버전 체크
|
||||||
|
var MaxVersion = DBM.GetMaxVersion();
|
||||||
|
if (!MaxVersion.isEmpty())
|
||||||
|
{
|
||||||
|
var curversion = Application.ProductVersion;
|
||||||
|
var verchk = curversion.CompareTo(MaxVersion);
|
||||||
|
if (verchk < 0)
|
||||||
|
{
|
||||||
|
result.VersionWarning = "현재 구 버젼을 사용하고 있습니다.\n업데이트를 진행 하고 사용하시기 바랍니다";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 로그인 정보 설정
|
||||||
|
var gperm = DBM.ExecuteScalar($"SELECT ISNULL(permission,0) FROM UserGroup WHERE gcode = '{gcode}'");
|
||||||
|
|
||||||
|
info.Login.no = drUser.id;
|
||||||
|
info.Login.nameK = drUser.name;
|
||||||
|
info.Login.dept = deptName;
|
||||||
|
info.Login.level = drGrpUser.level;
|
||||||
|
info.Login.email = drUser.email;
|
||||||
|
info.Login.nameE = drUser.nameE;
|
||||||
|
info.Login.hp = drUser.hp;
|
||||||
|
info.Login.tel = drUser.tel;
|
||||||
|
info.Login.title = drUser.dept + "(" + drUser.grade + ")";
|
||||||
|
info.NotShowJobReportview = Pub.setting.NotShowJobreportPRewView;
|
||||||
|
info.Login.gcode = gcode;
|
||||||
|
info.Login.process = drGrpUser.Process;
|
||||||
|
info.Login.permission = 0;
|
||||||
|
info.Login.gpermission = int.Parse(gperm);
|
||||||
|
info.ShowBuyerror = Pub.setting.Showbuyerror;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 설정 저장 (rememberMe 처리)
|
||||||
|
if (rememberMe)
|
||||||
|
{
|
||||||
|
var idlist = new List<string> { id.Trim() };
|
||||||
|
var vuserlist = "";
|
||||||
|
foreach (var item in idlist)
|
||||||
|
vuserlist += ";" + item;
|
||||||
|
|
||||||
|
Pub.setting.lastid = vuserlist;
|
||||||
|
Pub.setting.lastdpt = deptName;
|
||||||
|
Pub.setting.lastgcode = gcode;
|
||||||
|
Pub.setting.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 로그인 정보 기록
|
||||||
|
AddLoginInfo();
|
||||||
|
|
||||||
|
// 자동 업무일지 생성
|
||||||
|
Pub.MakeAutoJobReportbyLogin();
|
||||||
|
Pub.MakeAutoJobReportByAuto();
|
||||||
|
|
||||||
|
info.Login.loginusetime = (DateTime.Now - dt).TotalMilliseconds;
|
||||||
|
|
||||||
|
// fMain의 로그인 완료 후처리 호출
|
||||||
|
CallMainFormLoginCompleted();
|
||||||
|
|
||||||
|
result.Success = true;
|
||||||
|
result.Message = "로그인 성공";
|
||||||
|
result.RedirectUrl = "/DashBoard/index.html";
|
||||||
|
result.UserName = info.Login.nameK;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
result.Message = "데이터베이스 조회 실패. 다음 오류 메세지를 참고하세요.\n\n" + ex.Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// fMain의 OnLoginCompleted() 호출
|
||||||
|
/// </summary>
|
||||||
|
private void CallMainFormLoginCompleted()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Application.OpenForms에서 fMain 인스턴스 찾기
|
||||||
|
foreach (Form form in Application.OpenForms)
|
||||||
|
{
|
||||||
|
if (form is fMain mainForm)
|
||||||
|
{
|
||||||
|
// UI 스레드에서 실행
|
||||||
|
if (mainForm.InvokeRequired)
|
||||||
|
{
|
||||||
|
mainForm.Invoke(new Action(() => mainForm.OnLoginCompleted()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mainForm.OnLoginCompleted();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"OnLoginCompleted 호출 오류: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 개발자 로그인 정보 설정
|
||||||
|
/// </summary>
|
||||||
|
private void SetDevLoginInfo(string gcode, string deptName)
|
||||||
|
{
|
||||||
|
var gperm = DBM.ExecuteScalar($"SELECT ISNULL(permission,0) FROM UserGroup WHERE gcode = '{gcode}'");
|
||||||
|
|
||||||
|
info.Login.no = "dev";
|
||||||
|
info.Login.nameK = "개발자";
|
||||||
|
info.Login.dept = deptName;
|
||||||
|
info.Login.level = 10;
|
||||||
|
info.Login.email = "";
|
||||||
|
info.Login.nameE = "DEVELOPER";
|
||||||
|
info.Login.hp = "";
|
||||||
|
info.Login.tel = "";
|
||||||
|
info.Login.title = "업무일지 개발자";
|
||||||
|
info.NotShowJobReportview = Pub.setting.NotShowJobreportPRewView;
|
||||||
|
info.Login.gcode = gcode;
|
||||||
|
info.Login.process = "개발자";
|
||||||
|
info.Login.permission = 0;
|
||||||
|
info.Login.gpermission = int.Parse(gperm);
|
||||||
|
info.ShowBuyerror = Pub.setting.Showbuyerror;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 로그인 정보 기록
|
||||||
|
/// </summary>
|
||||||
|
private void AddLoginInfo()
|
||||||
|
{
|
||||||
|
string ip = string.Empty;
|
||||||
|
string hostname = Dns.GetHostName();
|
||||||
|
string fullname = Dns.GetHostEntry("").HostName;
|
||||||
|
|
||||||
|
var host = Dns.GetHostEntry(hostname);
|
||||||
|
foreach (IPAddress r in host.AddressList)
|
||||||
|
{
|
||||||
|
string str = r.ToString();
|
||||||
|
if (!string.IsNullOrEmpty(str) && str.StartsWith("10."))
|
||||||
|
{
|
||||||
|
ip = str;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(ip) || string.IsNullOrEmpty(hostname)) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ta = new dsMSSQLTableAdapters.EETGW_LoginInfoTableAdapter();
|
||||||
|
ta.Insert(info.Login.no, DateTime.Now, ip, fullname, info.Login.no, DateTime.Now);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 그룹 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string GetUserGroups()
|
||||||
|
{
|
||||||
|
var dt = DBM.GetUserGroups();
|
||||||
|
return JsonConvert.SerializeObject(dt, new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
NullValueHandling = NullValueHandling.Ignore
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 이전 로그인 정보 조회
|
||||||
|
/// </summary>
|
||||||
|
public string GetPreviousLoginInfo()
|
||||||
|
{
|
||||||
|
var result = new
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Data = new
|
||||||
|
{
|
||||||
|
LastGcode = Pub.setting.lastgcode ?? "",
|
||||||
|
LastDept = Pub.setting.lastdpt ?? "",
|
||||||
|
LastId = Pub.setting.lastid?.TrimStart(';').Split(';').FirstOrDefault() ?? ""
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return JsonConvert.SerializeObject(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
210
Project/Web/MachineBridge/MachineBridge.Project.cs
Normal file
210
Project/Web/MachineBridge/MachineBridge.Project.cs
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using FCOMMON;
|
||||||
|
|
||||||
|
namespace Project.Web
|
||||||
|
{
|
||||||
|
public partial class MachineBridge
|
||||||
|
{
|
||||||
|
#region Project API
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 프로젝트 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string Project_GetProjects(string status, string userFilter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = @"SELECT * FROM EETGW_Project WITH (nolock)
|
||||||
|
WHERE gcode = @gcode";
|
||||||
|
|
||||||
|
var parameters = new List<SqlParameter>();
|
||||||
|
parameters.Add(new SqlParameter("@gcode", info.Login.gcode));
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(status))
|
||||||
|
{
|
||||||
|
sql += " AND 상태 = @status";
|
||||||
|
parameters.Add(new SqlParameter("@status", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userFilter == "my")
|
||||||
|
{
|
||||||
|
sql += " AND (프로젝트관리자 LIKE @userName OR 설계담당 LIKE @userName OR 전장담당 LIKE @userName OR 프로그램담당 LIKE @userName)";
|
||||||
|
parameters.Add(new SqlParameter("@userName", "%" + info.Login.nameK + "%"));
|
||||||
|
}
|
||||||
|
|
||||||
|
sql += " ORDER BY 시작일 DESC";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddRange(parameters.ToArray());
|
||||||
|
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = dt, CurrentUser = info.Login.nameK }, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 프로젝트 상세 조회
|
||||||
|
/// </summary>
|
||||||
|
public string Project_GetProject(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = "SELECT * FROM EETGW_Project WHERE idx = @idx AND gcode = @gcode";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@idx", id);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
if (dt.Rows.Count > 0)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = dt.Rows[0] }, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "프로젝트를 찾을 수 없습니다." });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 프로젝트 생성
|
||||||
|
/// </summary>
|
||||||
|
public string Project_CreateProject(string name, string process, string sdate, string edate, string ddate, string odate, string userManager, string status, string memo)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = @"INSERT INTO EETGW_Project (gcode, 프로젝트명, 프로젝트공정, 시작일, 완료일, 만료일, 출고일, 프로젝트관리자, 상태, memo, wuid, wdate)
|
||||||
|
VALUES (@gcode, @name, @process, @sdate, @edate, @ddate, @odate, @userManager, @status, @memo, @wuid, GETDATE());
|
||||||
|
SELECT SCOPE_IDENTITY();";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
cmd.Parameters.AddWithValue("@name", name ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@process", process ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@sdate", string.IsNullOrEmpty(sdate) ? (object)DBNull.Value : sdate);
|
||||||
|
cmd.Parameters.AddWithValue("@edate", string.IsNullOrEmpty(edate) ? (object)DBNull.Value : edate);
|
||||||
|
cmd.Parameters.AddWithValue("@ddate", string.IsNullOrEmpty(ddate) ? (object)DBNull.Value : ddate);
|
||||||
|
cmd.Parameters.AddWithValue("@odate", string.IsNullOrEmpty(odate) ? (object)DBNull.Value : odate);
|
||||||
|
cmd.Parameters.AddWithValue("@userManager", userManager ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@status", status ?? "진행");
|
||||||
|
cmd.Parameters.AddWithValue("@memo", memo ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
|
||||||
|
|
||||||
|
cn.Open();
|
||||||
|
var newId = Convert.ToInt32(cmd.ExecuteScalar());
|
||||||
|
cn.Close();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Message = "프로젝트가 생성되었습니다.", Data = new { idx = newId } });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 프로젝트 수정
|
||||||
|
/// </summary>
|
||||||
|
public string Project_UpdateProject(int idx, string name, string process, string sdate, string edate, string ddate, string odate, string userManager, string status, string memo)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = @"UPDATE EETGW_Project SET
|
||||||
|
프로젝트명 = @name, 프로젝트공정 = @process, 시작일 = @sdate, 완료일 = @edate,
|
||||||
|
만료일 = @ddate, 출고일 = @odate, 프로젝트관리자 = @userManager, 상태 = @status, memo = @memo,
|
||||||
|
wuid = @wuid, wdate = GETDATE()
|
||||||
|
WHERE idx = @idx AND gcode = @gcode";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@idx", idx);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
cmd.Parameters.AddWithValue("@name", name ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@process", process ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@sdate", string.IsNullOrEmpty(sdate) ? (object)DBNull.Value : sdate);
|
||||||
|
cmd.Parameters.AddWithValue("@edate", string.IsNullOrEmpty(edate) ? (object)DBNull.Value : edate);
|
||||||
|
cmd.Parameters.AddWithValue("@ddate", string.IsNullOrEmpty(ddate) ? (object)DBNull.Value : ddate);
|
||||||
|
cmd.Parameters.AddWithValue("@odate", string.IsNullOrEmpty(odate) ? (object)DBNull.Value : odate);
|
||||||
|
cmd.Parameters.AddWithValue("@userManager", userManager ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@status", status ?? "진행");
|
||||||
|
cmd.Parameters.AddWithValue("@memo", memo ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
|
||||||
|
|
||||||
|
cn.Open();
|
||||||
|
var result = cmd.ExecuteNonQuery();
|
||||||
|
cn.Close();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "수정되었습니다." : "수정에 실패했습니다." });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 프로젝트 삭제
|
||||||
|
/// </summary>
|
||||||
|
public string Project_DeleteProject(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sql = "DELETE FROM EETGW_Project WHERE idx = @idx AND gcode = @gcode";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@idx", id);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
|
||||||
|
cn.Open();
|
||||||
|
var result = cmd.ExecuteNonQuery();
|
||||||
|
cn.Close();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "삭제되었습니다." : "삭제에 실패했습니다." });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
280
Project/Web/MachineBridge/MachineBridge.Todo.cs
Normal file
280
Project/Web/MachineBridge/MachineBridge.Todo.cs
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using FCOMMON;
|
||||||
|
using Project.Web.Model;
|
||||||
|
|
||||||
|
namespace Project.Web
|
||||||
|
{
|
||||||
|
public partial class MachineBridge
|
||||||
|
{
|
||||||
|
#region Todo API
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 급한 할일 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string GetUrgentTodos()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인되지 않은 상태입니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var sql = @"SELECT * FROM EETGW_Todo
|
||||||
|
WHERE gcode = @gcode AND uid = @uid
|
||||||
|
and isnull(status,'0') not in ('2','3','5')
|
||||||
|
ORDER BY flag DESC, seqno DESC, expire ASC, wdate ASC";
|
||||||
|
|
||||||
|
var todos = DBM.Query<TodoModel>(sql, new { gcode = info.Login.gcode, uid = info.Login.no });
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = todos }, new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
NullValueHandling = NullValueHandling.Ignore,
|
||||||
|
DateFormatString = "yyyy-MM-dd HH:mm:ss"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "급한 Todo 목록을 가져오는 중 오류가 발생했습니다: " + ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 할일 상세 조회
|
||||||
|
/// </summary>
|
||||||
|
public string GetTodo(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인되지 않은 상태입니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id <= 0)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "유효하지 않은 Todo ID입니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var sql = "SELECT * FROM EETGW_Todo WHERE idx = @idx AND gcode = @gcode AND uid = @uid";
|
||||||
|
var todo = DBM.QuerySingleOrDefault<TodoModel>(sql, new { idx = id, gcode = info.Login.gcode, uid = info.Login.no });
|
||||||
|
|
||||||
|
if (todo == null)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "할일을 찾을 수 없습니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = todo }, new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
NullValueHandling = NullValueHandling.Ignore,
|
||||||
|
DateFormatString = "yyyy-MM-dd HH:mm:ss"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "할일 조회 중 오류가 발생했습니다: " + ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 할일 추가
|
||||||
|
/// </summary>
|
||||||
|
public string CreateTodo(string title, string remark, string expire, int seqno, bool flag, string request, string status)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인되지 않은 상태입니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(remark))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "할일 내용은 필수입니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime? expireDate = null;
|
||||||
|
if (!string.IsNullOrEmpty(expire))
|
||||||
|
{
|
||||||
|
if (DateTime.TryParse(expire, out DateTime parsed))
|
||||||
|
expireDate = parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime? okdateValue = null;
|
||||||
|
if (status == "5")
|
||||||
|
{
|
||||||
|
okdateValue = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sql = @"
|
||||||
|
INSERT INTO EETGW_Todo (gcode, uid, title, remark, flag, expire, seqno, request, status, okdate, wuid, wdate)
|
||||||
|
VALUES (@gcode, @uid, @title, @remark, @flag, @expire, @seqno, @request, @status, @okdate, @wuid, @wdate);
|
||||||
|
SELECT SCOPE_IDENTITY();";
|
||||||
|
|
||||||
|
var newId = DBM.QuerySingle<int>(sql, new
|
||||||
|
{
|
||||||
|
gcode = info.Login.gcode,
|
||||||
|
uid = info.Login.no,
|
||||||
|
title = title,
|
||||||
|
remark = remark,
|
||||||
|
flag = flag,
|
||||||
|
expire = expireDate,
|
||||||
|
seqno = seqno,
|
||||||
|
request = request,
|
||||||
|
status = string.IsNullOrEmpty(status) ? "0" : status,
|
||||||
|
okdate = okdateValue,
|
||||||
|
wuid = info.Login.no,
|
||||||
|
wdate = DateTime.Now
|
||||||
|
});
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Message = "할일이 추가되었습니다.", Data = new { idx = newId } });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "할일 추가 중 오류가 발생했습니다: " + ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Todo Extended API
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 할일 전체 목록 조회
|
||||||
|
/// </summary>
|
||||||
|
public string Todo_GetTodos()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인되지 않은 상태입니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var sql = @"SELECT * FROM EETGW_Todo
|
||||||
|
WHERE gcode = @gcode AND uid = @uid
|
||||||
|
ORDER BY flag DESC, seqno DESC, expire ASC, wdate ASC";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
cmd.Parameters.AddWithValue("@uid", info.Login.no);
|
||||||
|
|
||||||
|
var da = new SqlDataAdapter(cmd);
|
||||||
|
var dt = new DataTable();
|
||||||
|
da.Fill(dt);
|
||||||
|
da.Dispose();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = true, Data = dt }, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, DateFormatString = "yyyy-MM-dd HH:mm:ss" });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 할일 수정
|
||||||
|
/// </summary>
|
||||||
|
public string Todo_UpdateTodo(int idx, string title, string remark, string expire, int seqno, bool flag, string request, string status)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인되지 않은 상태입니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime? expireDate = null;
|
||||||
|
if (!string.IsNullOrEmpty(expire))
|
||||||
|
{
|
||||||
|
if (DateTime.TryParse(expire, out DateTime parsed))
|
||||||
|
expireDate = parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime? okdateValue = null;
|
||||||
|
if (status == "5")
|
||||||
|
{
|
||||||
|
okdateValue = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sql = @"UPDATE EETGW_Todo SET
|
||||||
|
title = @title, remark = @remark, expire = @expire, seqno = @seqno,
|
||||||
|
flag = @flag, request = @request, status = @status, okdate = @okdate,
|
||||||
|
wuid = @wuid, wdate = GETDATE()
|
||||||
|
WHERE idx = @idx AND gcode = @gcode AND uid = @uid";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@idx", idx);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
cmd.Parameters.AddWithValue("@uid", info.Login.no);
|
||||||
|
cmd.Parameters.AddWithValue("@title", title ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@remark", remark ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@expire", expireDate.HasValue ? (object)expireDate.Value : DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("@seqno", seqno);
|
||||||
|
cmd.Parameters.AddWithValue("@flag", flag);
|
||||||
|
cmd.Parameters.AddWithValue("@request", request ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@status", string.IsNullOrEmpty(status) ? "0" : status);
|
||||||
|
cmd.Parameters.AddWithValue("@okdate", okdateValue.HasValue ? (object)okdateValue.Value : DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
|
||||||
|
|
||||||
|
cn.Open();
|
||||||
|
var result = cmd.ExecuteNonQuery();
|
||||||
|
cn.Close();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "수정되었습니다." : "수정에 실패했습니다." });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 할일 삭제
|
||||||
|
/// </summary>
|
||||||
|
public string Todo_DeleteTodo(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(info.Login.no))
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = "로그인되지 않은 상태입니다." });
|
||||||
|
}
|
||||||
|
|
||||||
|
var sql = "DELETE FROM EETGW_Todo WHERE idx = @idx AND gcode = @gcode AND uid = @uid";
|
||||||
|
|
||||||
|
var cs = Properties.Settings.Default.gwcs;
|
||||||
|
var cn = new SqlConnection(cs);
|
||||||
|
var cmd = new SqlCommand(sql, cn);
|
||||||
|
cmd.Parameters.AddWithValue("@idx", id);
|
||||||
|
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||||
|
cmd.Parameters.AddWithValue("@uid", info.Login.no);
|
||||||
|
|
||||||
|
cn.Open();
|
||||||
|
var result = cmd.ExecuteNonQuery();
|
||||||
|
cn.Close();
|
||||||
|
cmd.Dispose();
|
||||||
|
cn.Dispose();
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "삭제되었습니다." : "삭제에 실패했습니다." });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Project/Web/MachineBridge/MachineBridge.cs
Normal file
42
Project/Web/MachineBridge/MachineBridge.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using FCOMMON;
|
||||||
|
using Project.Web.Model;
|
||||||
|
|
||||||
|
namespace Project.Web
|
||||||
|
{
|
||||||
|
// Important: Allows JavaScript to see this class
|
||||||
|
[ClassInterface(ClassInterfaceType.AutoDual)]
|
||||||
|
[ComVisible(true)]
|
||||||
|
public partial class MachineBridge
|
||||||
|
{
|
||||||
|
// Reference to the main form to update logic
|
||||||
|
private Dialog.fDashboardNew _host;
|
||||||
|
|
||||||
|
public MachineBridge(Dialog.fDashboardNew host)
|
||||||
|
{
|
||||||
|
_host = host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 로그인 결과 클래스
|
||||||
|
/// </summary>
|
||||||
|
public class LoginResult
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
public string RedirectUrl { get; set; }
|
||||||
|
public string UserName { get; set; }
|
||||||
|
public string VersionWarning { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
221
Project/Web/WebSocketServer.cs
Normal file
221
Project/Web/WebSocketServer.cs
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace Project.Web
|
||||||
|
{
|
||||||
|
public class WebSocketServer
|
||||||
|
{
|
||||||
|
private HttpListener _httpListener;
|
||||||
|
private List<WebSocket> _clients = new List<WebSocket>();
|
||||||
|
private Dialog.fDashboardNew _mainForm;
|
||||||
|
|
||||||
|
public WebSocketServer(string url, Dialog.fDashboardNew form)
|
||||||
|
{
|
||||||
|
_mainForm = form;
|
||||||
|
_httpListener = new HttpListener();
|
||||||
|
_httpListener.Prefixes.Add(url);
|
||||||
|
_httpListener.Start();
|
||||||
|
Console.WriteLine($"[WS] Listening on {url}");
|
||||||
|
Task.Run(AcceptConnections);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AcceptConnections()
|
||||||
|
{
|
||||||
|
while (_httpListener.IsListening)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var context = await _httpListener.GetContextAsync();
|
||||||
|
if (context.Request.IsWebSocketRequest)
|
||||||
|
{
|
||||||
|
ProcessRequest(context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
context.Response.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[WS] Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private System.Collections.Concurrent.ConcurrentDictionary<WebSocket, SemaphoreSlim> _socketLocks = new System.Collections.Concurrent.ConcurrentDictionary<WebSocket, SemaphoreSlim>();
|
||||||
|
|
||||||
|
private async void ProcessRequest(HttpListenerContext context)
|
||||||
|
{
|
||||||
|
WebSocketContext wsContext = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
wsContext = await context.AcceptWebSocketAsync(subProtocol: null);
|
||||||
|
WebSocket socket = wsContext.WebSocket;
|
||||||
|
_socketLocks.TryAdd(socket, new SemaphoreSlim(1, 1));
|
||||||
|
|
||||||
|
lock (_clients) { _clients.Add(socket); }
|
||||||
|
Console.WriteLine("[WS] Client Connected");
|
||||||
|
|
||||||
|
await ReceiveLoop(socket);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[WS] Accept Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (wsContext != null)
|
||||||
|
{
|
||||||
|
WebSocket socket = wsContext.WebSocket;
|
||||||
|
lock (_clients) { _clients.Remove(socket); }
|
||||||
|
|
||||||
|
if (_socketLocks.TryRemove(socket, out var semaphore))
|
||||||
|
{
|
||||||
|
semaphore.Dispose();
|
||||||
|
}
|
||||||
|
socket.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ReceiveLoop(WebSocket socket)
|
||||||
|
{
|
||||||
|
var buffer = new byte[1024 * 4];
|
||||||
|
while (socket.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||||
|
if (result.MessageType == WebSocketMessageType.Close)
|
||||||
|
{
|
||||||
|
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
|
||||||
|
}
|
||||||
|
else if (result.MessageType == WebSocketMessageType.Text)
|
||||||
|
{
|
||||||
|
string msg = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||||
|
HandleMessage(msg, socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void HandleMessage(string msg, WebSocket socket)
|
||||||
|
{
|
||||||
|
// Simple JSON parsing (manual or Newtonsoft)
|
||||||
|
// Expected format: { "type": "...", "data": ... }
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dynamic json = Newtonsoft.Json.JsonConvert.DeserializeObject(msg);
|
||||||
|
string type = json.type;
|
||||||
|
|
||||||
|
Console.WriteLine($"HandleMessage:{type}");
|
||||||
|
if (type == "GET_CONFIG")
|
||||||
|
{
|
||||||
|
//// Simulate Delay for Loading Screen Test
|
||||||
|
//await Task.Delay(1000);
|
||||||
|
|
||||||
|
//// Send Config back
|
||||||
|
//var bridge = new MachineBridge(_mainForm); // Re-use logic
|
||||||
|
//string configJson = bridge.GetConfig();
|
||||||
|
//var response = new { type = "CONFIG_DATA", data = Newtonsoft.Json.JsonConvert.DeserializeObject(configJson) };
|
||||||
|
//await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "GET_IO_LIST")
|
||||||
|
{
|
||||||
|
// var bridge = new MachineBridge(_mainForm);
|
||||||
|
// string ioJson = bridge.GetIOList();
|
||||||
|
// var response = new { type = "IO_LIST_DATA", data = Newtonsoft.Json.JsonConvert.DeserializeObject(ioJson) };
|
||||||
|
// await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
//}
|
||||||
|
//else if (type == "GET_RECIPE_LIST")
|
||||||
|
//{
|
||||||
|
// var bridge = new MachineBridge(_mainForm);
|
||||||
|
// string recipeJson = bridge.GetRecipeList();
|
||||||
|
// var response = new { type = "RECIPE_LIST_DATA", data = Newtonsoft.Json.JsonConvert.DeserializeObject(recipeJson) };
|
||||||
|
// await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||||
|
}
|
||||||
|
else if (type == "SAVE_CONFIG")
|
||||||
|
{
|
||||||
|
//string configJson = Newtonsoft.Json.JsonConvert.SerializeObject(json.data);
|
||||||
|
//var bridge = new MachineBridge(_mainForm);
|
||||||
|
//bridge.SaveConfig(configJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[WS] Msg Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Send(WebSocket socket, string message)
|
||||||
|
{
|
||||||
|
if (_socketLocks.TryGetValue(socket, out var semaphore))
|
||||||
|
{
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (socket.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
byte[] buffer = Encoding.UTF8.GetBytes(message);
|
||||||
|
await socket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
semaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Broadcast(string message)
|
||||||
|
{
|
||||||
|
byte[] buffer = Encoding.UTF8.GetBytes(message);
|
||||||
|
WebSocket[] clientsCopy;
|
||||||
|
|
||||||
|
lock (_clients)
|
||||||
|
{
|
||||||
|
clientsCopy = _clients.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var client in clientsCopy)
|
||||||
|
{
|
||||||
|
if (client.State == WebSocketState.Open && _socketLocks.TryGetValue(client, out var semaphore))
|
||||||
|
{
|
||||||
|
// Fire and forget, but safely
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
// Try to get lock immediately. If busy (sending previous frame), skip this frame to prevent lag.
|
||||||
|
if (await semaphore.WaitAsync(0))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (client.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
await client.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { /* Ignore send errors */ }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
semaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -462,151 +462,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 공통 네비게이션 -->
|
||||||
|
<script src="/js/common-navigation.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/**
|
// Machine HostObject 초기화
|
||||||
* 공통 네비게이션 컴포넌트
|
const machine = window.chrome.webview.hostObjects.machine;
|
||||||
*/
|
|
||||||
class CommonNavigation {
|
|
||||||
constructor(currentPage = '') {
|
|
||||||
this.currentPage = currentPage;
|
|
||||||
this.menuItems = [];
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
try {
|
|
||||||
await this.loadMenuItems();
|
|
||||||
this.createNavigation();
|
|
||||||
this.addEventListeners();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Navigation initialization failed:', error);
|
|
||||||
this.createFallbackNavigation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadMenuItems() {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/Common/GetNavigationMenu');
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
|
||||||
}
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (data.Success && data.Data) {
|
|
||||||
this.menuItems = data.Data;
|
|
||||||
} else {
|
|
||||||
throw new Error(data.Message || 'Failed to load menu items');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to load navigation menu:', error);
|
|
||||||
this.menuItems = this.getDefaultMenuItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getDefaultMenuItems() {
|
|
||||||
return [
|
|
||||||
{ key: 'dashboard', title: '대시보드', url: '/Dashboard/', icon: 'M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z', isVisible: true, sortOrder: 1 },
|
|
||||||
{ key: 'common', title: '공용코드', url: '/Common', icon: 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z', isVisible: true, sortOrder: 2 },
|
|
||||||
{ key: 'jobreport', title: '업무일지', url: '/Jobreport/', icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2', isVisible: true, sortOrder: 3 },
|
|
||||||
{ key: 'kuntae', title: '근태관리', url: '/Kuntae/', icon: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z', isVisible: true, sortOrder: 4 },
|
|
||||||
{ key: 'todo', title: '할일관리', url: '/Todo/', icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2M12 12l2 2 4-4', isVisible: true, sortOrder: 5 }
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
createNavigation() {
|
|
||||||
const nav = document.createElement('nav');
|
|
||||||
nav.className = 'glass-effect border-b border-white/10';
|
|
||||||
nav.innerHTML = this.getNavigationHTML();
|
|
||||||
document.body.insertBefore(nav, document.body.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
createFallbackNavigation() {
|
|
||||||
this.createNavigation();
|
|
||||||
}
|
|
||||||
|
|
||||||
getNavigationHTML() {
|
|
||||||
const visibleItems = this.menuItems.filter(item => item.isVisible).sort((a, b) => a.sortOrder - b.sortOrder);
|
|
||||||
return `
|
|
||||||
<div class="container mx-auto px-4">
|
|
||||||
<div class="flex items-center justify-between h-16">
|
|
||||||
<div class="flex items-center space-x-8">
|
|
||||||
<a href="/Dashboard/" class="flex items-center space-x-2 hover:opacity-80 transition-opacity cursor-pointer">
|
|
||||||
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
|
|
||||||
</svg>
|
|
||||||
<span class="text-xl font-bold text-white">GroupWare</span>
|
|
||||||
</a>
|
|
||||||
<nav class="hidden md:flex space-x-1">
|
|
||||||
${visibleItems.map(item => `
|
|
||||||
<a href="${item.url}" class="px-3 py-2 rounded-md text-sm font-medium transition-colors ${
|
|
||||||
this.currentPage === item.key
|
|
||||||
? 'bg-white/20 text-white'
|
|
||||||
: 'text-white/60 hover:text-white hover:bg-white/10'
|
|
||||||
}">
|
|
||||||
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${item.icon}"></path>
|
|
||||||
</svg>
|
|
||||||
${item.title}
|
|
||||||
</a>
|
|
||||||
`).join('')}
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center space-x-4">
|
|
||||||
<div class="text-sm text-white/60">
|
|
||||||
<span id="currentUser">사용자</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMenuItemHTML(item) {
|
|
||||||
const isActive = this.currentPage === item.key;
|
|
||||||
const activeClass = isActive ? 'text-white bg-white/20' : 'text-white/80 hover:text-white hover:bg-white/10';
|
|
||||||
return `
|
|
||||||
<a href="${item.url}" class="${activeClass} transition-colors px-3 py-2 rounded-lg">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${item.icon}"></path>
|
|
||||||
</svg>
|
|
||||||
${item.title}
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMobileMenuItemHTML(item) {
|
|
||||||
const isActive = this.currentPage === item.key;
|
|
||||||
const activeClass = isActive ? 'text-white bg-white/20' : 'text-white/80 hover:text-white hover:bg-white/10';
|
|
||||||
return `
|
|
||||||
<a href="${item.url}" class="block ${activeClass} transition-colors px-3 py-2 rounded-lg mb-2">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${item.icon}"></path>
|
|
||||||
</svg>
|
|
||||||
${item.title}
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
addEventListeners() {
|
|
||||||
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
|
||||||
const mobileMenu = document.getElementById('mobile-menu');
|
|
||||||
if (mobileMenuButton && mobileMenu) {
|
|
||||||
mobileMenuButton.addEventListener('click', function() {
|
|
||||||
mobileMenu.classList.toggle('hidden');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initNavigation(currentPage = '') {
|
|
||||||
if (document.readyState === 'loading') {
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
new CommonNavigation(currentPage);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
new CommonNavigation(currentPage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 전역 변수
|
// 전역 변수
|
||||||
let jobData = [];
|
let jobData = [];
|
||||||
@@ -743,46 +604,37 @@
|
|||||||
const startDate = document.getElementById('startDate').value;
|
const startDate = document.getElementById('startDate').value;
|
||||||
const endDate = document.getElementById('endDate').value;
|
const endDate = document.getElementById('endDate').value;
|
||||||
const selectedUser = document.getElementById('userFilter').value;
|
const selectedUser = document.getElementById('userFilter').value;
|
||||||
|
|
||||||
// API URL 구성 - 새로운 GetJobData API 사용
|
console.log('Loading job data with params:', { startDate, endDate, selectedUser });
|
||||||
let url = '/Jobreport/GetJobData';
|
|
||||||
const params = new URLSearchParams();
|
// machine.Jobreport_GetList(sd, ed, uid, cate, doit) 호출
|
||||||
if (startDate) params.append('startDate', startDate);
|
const jsonStr = await machine.Jobreport_GetList(
|
||||||
if (endDate) params.append('endDate', endDate);
|
startDate || '',
|
||||||
if (selectedUser) params.append('user', selectedUser);
|
endDate || '',
|
||||||
|
selectedUser || '',
|
||||||
if (params.toString()) {
|
'', // cate (빈 값)
|
||||||
url += '?' + params.toString();
|
'' // doit (빈 값)
|
||||||
}
|
);
|
||||||
|
|
||||||
console.log('Fetching data from:', url);
|
responseData = JSON.parse(jsonStr);
|
||||||
const response = await fetch(url);
|
|
||||||
console.log('Response status:', response.status);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP ${response.status}: 데이터를 불러오는데 실패했습니다.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSON 응답 직접 파싱
|
|
||||||
const responseText = await response.text();
|
|
||||||
console.log('Raw response:', responseText);
|
|
||||||
|
|
||||||
responseData = JSON.parse(responseText);
|
|
||||||
console.log('Parsed response:', responseData);
|
console.log('Parsed response:', responseData);
|
||||||
|
|
||||||
// 응답 데이터가 배열인지 확인
|
// MachineBridge 응답 형식: { Success: true, Data: [...] }
|
||||||
if (Array.isArray(responseData)) {
|
if (responseData.Success && Array.isArray(responseData.Data)) {
|
||||||
jobData = responseData;
|
jobData = responseData.Data;
|
||||||
} else if (responseData.error) {
|
} else if (responseData.error) {
|
||||||
throw new Error(responseData.error);
|
throw new Error(responseData.error);
|
||||||
|
} else if (Array.isArray(responseData)) {
|
||||||
|
// 하위 호환성: 배열이 직접 반환되는 경우
|
||||||
|
jobData = responseData;
|
||||||
} else {
|
} else {
|
||||||
// 데이터가 배열이 아닌 경우 빈 배열로 초기화
|
// 데이터가 없는 경우 빈 배열로 초기화
|
||||||
console.warn('Response is not an array, initializing empty array');
|
console.warn('Response has no data, initializing empty array');
|
||||||
jobData = [];
|
jobData = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredData = [...jobData];
|
filteredData = [...jobData];
|
||||||
|
|
||||||
updateStatistics();
|
updateStatistics();
|
||||||
updateProjectFilter();
|
updateProjectFilter();
|
||||||
sortData();
|
sortData();
|
||||||
@@ -867,7 +719,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadUserList() {
|
async function loadUserList() {
|
||||||
const userSelect = document.getElementById('userFilter');
|
const userSelect = document.getElementById('userFilter');
|
||||||
|
|
||||||
// 기존 옵션 제거 (전체 옵션 제외)
|
// 기존 옵션 제거 (전체 옵션 제외)
|
||||||
@@ -875,74 +727,72 @@
|
|||||||
userSelect.removeChild(userSelect.lastChild);
|
userSelect.removeChild(userSelect.lastChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 서버에서 사용자 목록 가져오기
|
try {
|
||||||
fetch('/Jobreport/GetUsers')
|
// machine.Jobreport_GetUsers() 호출
|
||||||
.then(response => {
|
const jsonStr = await machine.Jobreport_GetUsers();
|
||||||
if (!response.ok) {
|
const data = JSON.parse(jsonStr);
|
||||||
throw new Error(`HTTP ${response.status}: 사용자 목록을 불러오는데 실패했습니다.`);
|
|
||||||
}
|
console.log('사용자 목록 데이터:', data);
|
||||||
return response.json();
|
|
||||||
})
|
// 응답 형식 처리: { Success: true, Data: [...] } 또는 배열 직접
|
||||||
.then(data => {
|
const users = (data.Success && data.Data) ? data.Data : (Array.isArray(data) ? data : []);
|
||||||
console.log('사용자 목록 데이터:', data);
|
|
||||||
if (data && Array.isArray(data)) {
|
if (users.length > 0) {
|
||||||
data.forEach(user => {
|
users.forEach(user => {
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
option.value = user.id;
|
option.value = user.id;
|
||||||
option.textContent = `${user.name} [${user.id}] ${user.process}`;
|
option.textContent = `${user.name} [${user.id}] ${user.process}`;
|
||||||
userSelect.appendChild(option);
|
userSelect.appendChild(option);
|
||||||
});
|
});
|
||||||
console.log('사용자 목록 로드 완료:', userSelect.children.length - 1, '명');
|
console.log('사용자 목록 로드 완료:', userSelect.children.length - 1, '명');
|
||||||
} else {
|
} else {
|
||||||
console.warn('사용자 목록 데이터가 배열이 아닙니다:', data);
|
console.warn('사용자 목록 데이터가 없습니다:', data);
|
||||||
}
|
}
|
||||||
})
|
} catch (error) {
|
||||||
.catch(error => {
|
console.error('사용자 목록 로드 중 오류:', error);
|
||||||
console.error('사용자 목록 로드 중 오류:', error);
|
// 에러 시 기본 사용자 옵션 추가
|
||||||
// 에러 시 기본 사용자 옵션 추가
|
const option = document.createElement('option');
|
||||||
const option = document.createElement('option');
|
option.value = FCOMMON?.info?.Login?.no || '';
|
||||||
option.value = FCOMMON?.info?.Login?.no || '';
|
option.textContent = '현재 사용자';
|
||||||
option.textContent = '현재 사용자';
|
userSelect.appendChild(option);
|
||||||
userSelect.appendChild(option);
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 공용코드 12번(상태코드) 로드
|
// 공용코드 12번(상태코드) 로드
|
||||||
function loadStatusCodes() {
|
async function loadStatusCodes() {
|
||||||
fetch('/Common/GetList?grp=12')
|
try {
|
||||||
.then(response => {
|
const jsonStr = await machine.Common_GetList('12');
|
||||||
if (!response.ok) {
|
const data = JSON.parse(jsonStr);
|
||||||
throw new Error(`HTTP ${response.status}: 상태코드를 불러오는데 실패했습니다.`);
|
|
||||||
}
|
console.log('상태코드 원본 데이터:', data);
|
||||||
return response.json();
|
|
||||||
})
|
// 응답 형식 처리: { Success: true, Data: [...] } 또는 배열 직접
|
||||||
.then(data => {
|
const codes = (data.Success && data.Data) ? data.Data : (Array.isArray(data) ? data : []);
|
||||||
console.log('상태코드 원본 데이터:', data);
|
|
||||||
if (data && Array.isArray(data)) {
|
if (codes.length > 0) {
|
||||||
statusCodes = data;
|
statusCodes = codes;
|
||||||
console.log('상태코드 로드 완료:', statusCodes.length, '개');
|
console.log('상태코드 로드 완료:', statusCodes.length, '개');
|
||||||
console.log('첫번째 상태코드 샘플:', statusCodes[0]);
|
console.log('첫번째 상태코드 샘플:', statusCodes[0]);
|
||||||
} else {
|
} else {
|
||||||
console.warn('상태코드 데이터가 배열이 아닙니다:', data);
|
console.warn('상태코드 데이터가 없습니다:', data);
|
||||||
// 기본 상태코드 설정
|
// 기본 상태코드 설정
|
||||||
statusCodes = [
|
|
||||||
{ svalue: '진행 중' },
|
|
||||||
{ svalue: '완료' },
|
|
||||||
{ svalue: '대기' },
|
|
||||||
{ svalue: '보류' }
|
|
||||||
];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('상태코드 로드 중 오류:', error);
|
|
||||||
// 에러 시 기본 상태코드 사용
|
|
||||||
statusCodes = [
|
statusCodes = [
|
||||||
{ svalue: '진행 중' },
|
{ svalue: '진행 중' },
|
||||||
{ svalue: '완료' },
|
{ svalue: '완료' },
|
||||||
{ svalue: '대기' },
|
{ svalue: '대기' },
|
||||||
{ svalue: '보류' }
|
{ svalue: '보류' }
|
||||||
];
|
];
|
||||||
});
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('상태코드 로드 중 오류:', error);
|
||||||
|
// 에러 시 기본 상태코드 사용
|
||||||
|
statusCodes = [
|
||||||
|
{ svalue: '진행 중' },
|
||||||
|
{ svalue: '완료' },
|
||||||
|
{ svalue: '대기' },
|
||||||
|
{ svalue: '보류' }
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 상태코드 드롭다운 옵션 생성 헬퍼 함수
|
// 상태코드 드롭다운 옵션 생성 헬퍼 함수
|
||||||
@@ -962,78 +812,95 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 공용코드 13번(요청부서) 로드
|
// 공용코드 13번(요청부서) 로드
|
||||||
function loadRequestDeptCodes() {
|
async function loadRequestDeptCodes() {
|
||||||
fetch('/Common/GetList?grp=13')
|
try {
|
||||||
.then(response => response.json())
|
const jsonStr = await machine.Common_GetList('13');
|
||||||
.then(data => {
|
const data = JSON.parse(jsonStr);
|
||||||
if (data && Array.isArray(data)) {
|
|
||||||
requestDeptCodes = data;
|
// 응답 형식 처리: { Success: true, Data: [...] } 또는 배열 직접
|
||||||
console.log('요청부서 코드 로드 완료:', requestDeptCodes.length, '개');
|
const codes = (data.Success && data.Data) ? data.Data : (Array.isArray(data) ? data : []);
|
||||||
}
|
|
||||||
})
|
if (codes.length > 0) {
|
||||||
.catch(error => {
|
requestDeptCodes = codes;
|
||||||
console.error('요청부서 코드 로드 중 오류:', error);
|
console.log('요청부서 코드 로드 완료:', requestDeptCodes.length, '개');
|
||||||
|
} else {
|
||||||
requestDeptCodes = [];
|
requestDeptCodes = [];
|
||||||
});
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('요청부서 코드 로드 중 오류:', error);
|
||||||
|
requestDeptCodes = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 공용코드 14번(패키지) 로드
|
// 공용코드 14번(패키지) 로드
|
||||||
function loadPackageCodes() {
|
async function loadPackageCodes() {
|
||||||
fetch('/Common/GetList?grp=14')
|
try {
|
||||||
.then(response => response.json())
|
const jsonStr = await machine.Common_GetList('14');
|
||||||
.then(data => {
|
const data = JSON.parse(jsonStr);
|
||||||
if (data && Array.isArray(data)) {
|
|
||||||
packageCodes = data;
|
// 응답 형식 처리: { Success: true, Data: [...] } 또는 배열 직접
|
||||||
console.log('패키지 코드 로드 완료:', packageCodes.length, '개');
|
const codes = (data.Success && data.Data) ? data.Data : (Array.isArray(data) ? data : []);
|
||||||
}
|
|
||||||
})
|
if (codes.length > 0) {
|
||||||
.catch(error => {
|
packageCodes = codes;
|
||||||
console.error('패키지 코드 로드 중 오류:', error);
|
console.log('패키지 코드 로드 완료:', packageCodes.length, '개');
|
||||||
|
} else {
|
||||||
packageCodes = [];
|
packageCodes = [];
|
||||||
});
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('패키지 코드 로드 중 오류:', error);
|
||||||
|
packageCodes = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 공용코드 15번(업무형태 - 트리구조) 로드
|
// 공용코드 15번(업무형태 - 트리구조) 로드
|
||||||
function loadJobTypeCodes() {
|
async function loadJobTypeCodes() {
|
||||||
fetch('/Common/GetList?grp=15')
|
try {
|
||||||
.then(response => response.json())
|
const jsonStr = await machine.Common_GetList('15');
|
||||||
.then(data => {
|
const data = JSON.parse(jsonStr);
|
||||||
console.log('업무형태 원본 데이터:', data);
|
|
||||||
if (data && Array.isArray(data)) {
|
console.log('업무형태 원본 데이터:', data);
|
||||||
jobTypeCodes = data;
|
|
||||||
console.log('업무형태 코드 로드 완료:', jobTypeCodes.length, '개');
|
// 응답 형식 처리: { Success: true, Data: [...] } 또는 배열 직접
|
||||||
if (jobTypeCodes.length > 0) {
|
const codes = (data.Success && data.Data) ? data.Data : (Array.isArray(data) ? data : []);
|
||||||
console.log('업무형태 첫번째 샘플:', jobTypeCodes[0]);
|
|
||||||
console.log('업무형태 데이터 구조 확인:');
|
if (codes.length > 0) {
|
||||||
console.log(' - svalue2 (프로세스):', jobTypeCodes[0].svalue2);
|
jobTypeCodes = codes;
|
||||||
console.log(' - svalue (분류):', jobTypeCodes[0].svalue);
|
console.log('업무형태 코드 로드 완료:', jobTypeCodes.length, '개');
|
||||||
console.log(' - memo (항목):', jobTypeCodes[0].memo);
|
console.log('업무형태 첫번째 샘플:', jobTypeCodes[0]);
|
||||||
}
|
console.log('업무형태 데이터 구조 확인:');
|
||||||
} else {
|
console.log(' - svalue2 (프로세스):', jobTypeCodes[0].svalue2);
|
||||||
console.warn('업무형태 데이터가 배열이 아닙니다:', data);
|
console.log(' - svalue (분류):', jobTypeCodes[0].svalue);
|
||||||
jobTypeCodes = [];
|
console.log(' - memo (항목):', jobTypeCodes[0].memo);
|
||||||
}
|
} else {
|
||||||
})
|
console.warn('업무형태 데이터가 없습니다:', data);
|
||||||
.catch(error => {
|
|
||||||
console.error('업무형태 코드 로드 중 오류:', error);
|
|
||||||
jobTypeCodes = [];
|
jobTypeCodes = [];
|
||||||
});
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('업무형태 코드 로드 중 오류:', error);
|
||||||
|
jobTypeCodes = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 공용코드 16번(프로세스) 로드
|
// 공용코드 16번(프로세스) 로드
|
||||||
function loadProcessCodes() {
|
async function loadProcessCodes() {
|
||||||
fetch('/Common/GetList?grp=16')
|
try {
|
||||||
.then(response => response.json())
|
const jsonStr = await machine.Common_GetList('16');
|
||||||
.then(data => {
|
const data = JSON.parse(jsonStr);
|
||||||
if (data && Array.isArray(data)) {
|
|
||||||
processCodes = data;
|
// 응답 형식 처리: { Success: true, Data: [...] } 또는 배열 직접
|
||||||
console.log('프로세스 코드 로드 완료:', processCodes.length, '개');
|
const codes = (data.Success && data.Data) ? data.Data : (Array.isArray(data) ? data : []);
|
||||||
}
|
|
||||||
})
|
if (codes.length > 0) {
|
||||||
.catch(error => {
|
processCodes = codes;
|
||||||
console.error('프로세스 코드 로드 중 오류:', error);
|
console.log('프로세스 코드 로드 완료:', processCodes.length, '개');
|
||||||
|
} else {
|
||||||
processCodes = [];
|
processCodes = [];
|
||||||
});
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('프로세스 코드 로드 중 오류:', error);
|
||||||
|
processCodes = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 드롭다운 옵션 생성 헬퍼 함수들
|
// 드롭다운 옵션 생성 헬퍼 함수들
|
||||||
@@ -1392,15 +1259,20 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 전체 상세 정보를 서버에서 가져오기
|
// 전체 상세 정보를 서버에서 가져오기
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/Jobreport/GetJobDetail?id=${item.idx}`);
|
const jsonStr = await machine.Jobreport_GetDetail(item.idx);
|
||||||
if (response.ok) {
|
const data = JSON.parse(jsonStr);
|
||||||
const fullItem = await response.json();
|
|
||||||
// 전체 정보가 있으면 사용, 없으면 기존 item 사용
|
// 응답 형식 처리: { Success: true, Data: {...} } 또는 객체 직접
|
||||||
item = fullItem.error ? item : fullItem;
|
if (data.Success && data.Data) {
|
||||||
|
item = data.Data;
|
||||||
|
} else if (!data.error && typeof data === 'object') {
|
||||||
|
// 하위 호환성: 객체가 직접 반환되는 경우
|
||||||
|
item = data;
|
||||||
}
|
}
|
||||||
|
// error가 있으면 기존 item 유지
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Failed to load full details, using truncated data:', error);
|
console.warn('Failed to load full details, using truncated data:', error);
|
||||||
}
|
}
|
||||||
@@ -1679,57 +1551,37 @@
|
|||||||
|
|
||||||
async function handleAddSubmit(event) {
|
async function handleAddSubmit(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// URLSearchParams 사용 (application/x-www-form-urlencoded)
|
|
||||||
const formData = new URLSearchParams();
|
|
||||||
formData.append('pdate', document.getElementById('editPdate').value);
|
|
||||||
formData.append('status', document.getElementById('editStatus').value);
|
|
||||||
formData.append('projectName', document.getElementById('editProjectName').value);
|
|
||||||
formData.append('requestpart', document.getElementById('editRequestpart').value);
|
|
||||||
formData.append('package', document.getElementById('editPackage').value);
|
|
||||||
formData.append('jobprocess', document.getElementById('editJobProcess').value);
|
|
||||||
formData.append('jobtype', document.getElementById('editJobType').value);
|
|
||||||
formData.append('jobgrp', document.getElementById('editJobGrp').value);
|
|
||||||
formData.append('jobprocess2', document.getElementById('editJobProcess2').value);
|
|
||||||
formData.append('hrs', document.getElementById('editHrs').value);
|
|
||||||
formData.append('ot', document.getElementById('editOt').value);
|
|
||||||
formData.append('otStart', document.getElementById('editOtStart').value);
|
|
||||||
formData.append('otEnd', document.getElementById('editOtEnd').value);
|
|
||||||
formData.append('description', document.getElementById('editDescription').value);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/Jobreport/Add', {
|
// 폼 데이터 수집
|
||||||
method: 'POST',
|
const jdate = document.getElementById('editPdate').value;
|
||||||
headers: {
|
const cate = document.getElementById('editJobType').value || '';
|
||||||
'Content-Type': 'application/x-www-form-urlencoded'
|
const title = document.getElementById('editProjectName').value;
|
||||||
},
|
const doit = document.getElementById('editDescription').value;
|
||||||
body: formData
|
const remark = document.getElementById('editStatus').value || '';
|
||||||
});
|
const jfrom = document.getElementById('editOtStart').value || '';
|
||||||
|
const jto = document.getElementById('editOtEnd').value || '';
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP ${response.status}: 추가에 실패했습니다.`);
|
console.log('Adding job with params:', { jdate, cate, title, doit, remark, jfrom, jto });
|
||||||
}
|
|
||||||
|
// machine.Jobreport_Add(jdate, cate, title, doit, remark, jfrom, jto) 호출
|
||||||
const result = await response.text();
|
const jsonStr = await machine.Jobreport_Add(jdate, cate, title, doit, remark, jfrom, jto);
|
||||||
|
const result = JSON.parse(jsonStr);
|
||||||
|
|
||||||
console.log('Add result:', result);
|
console.log('Add result:', result);
|
||||||
|
|
||||||
// JSON 응답인지 확인
|
// 응답 형식 처리: { Success: true } 또는 { success: true }
|
||||||
try {
|
if (result.Success === false || result.success === false) {
|
||||||
const jsonResult = JSON.parse(result);
|
throw new Error(result.Message || result.message || '추가에 실패했습니다.');
|
||||||
if (!jsonResult.success) {
|
|
||||||
throw new Error(jsonResult.message || '추가에 실패했습니다.');
|
|
||||||
}
|
|
||||||
// 성공시 alert 없이 바로 진행
|
|
||||||
} catch (parseError) {
|
|
||||||
// JSON이 아닌 경우도 성공으로 처리 (alert 없음)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 성공시 alert 없이 바로 진행
|
||||||
// 모달 닫기
|
// 모달 닫기
|
||||||
closeModal();
|
closeModal();
|
||||||
|
|
||||||
// 데이터 새로고침
|
// 데이터 새로고침
|
||||||
await loadJobData();
|
await loadJobData();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error adding job:', error);
|
console.error('Error adding job:', error);
|
||||||
alert('업무일지 추가 중 오류가 발생했습니다: ' + error.message);
|
alert('업무일지 추가 중 오류가 발생했습니다: ' + error.message);
|
||||||
@@ -1738,58 +1590,38 @@
|
|||||||
|
|
||||||
async function handleEditSubmit(event) {
|
async function handleEditSubmit(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// URLSearchParams 사용 (application/x-www-form-urlencoded)
|
|
||||||
const formData = new URLSearchParams();
|
|
||||||
formData.append('idx', document.getElementById('editIdx').value);
|
|
||||||
formData.append('pdate', document.getElementById('editPdate').value);
|
|
||||||
formData.append('status', document.getElementById('editStatus').value);
|
|
||||||
formData.append('projectName', document.getElementById('editProjectName').value);
|
|
||||||
formData.append('requestpart', document.getElementById('editRequestpart').value);
|
|
||||||
formData.append('package', document.getElementById('editPackage').value);
|
|
||||||
formData.append('jobprocess', document.getElementById('editJobProcess').value);
|
|
||||||
formData.append('jobtype', document.getElementById('editJobType').value);
|
|
||||||
formData.append('jobgrp', document.getElementById('editJobGrp').value);
|
|
||||||
formData.append('jobprocess2', document.getElementById('editJobProcess2').value);
|
|
||||||
formData.append('hrs', document.getElementById('editHrs').value);
|
|
||||||
formData.append('ot', document.getElementById('editOt').value);
|
|
||||||
formData.append('otStart', document.getElementById('editOtStart').value);
|
|
||||||
formData.append('otEnd', document.getElementById('editOtEnd').value);
|
|
||||||
formData.append('description', document.getElementById('editDescription').value);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/Jobreport/Edit', {
|
// 폼 데이터 수집
|
||||||
method: 'POST',
|
const idx = document.getElementById('editIdx').value;
|
||||||
headers: {
|
const jdate = document.getElementById('editPdate').value;
|
||||||
'Content-Type': 'application/x-www-form-urlencoded'
|
const cate = document.getElementById('editJobType').value || '';
|
||||||
},
|
const title = document.getElementById('editProjectName').value;
|
||||||
body: formData
|
const doit = document.getElementById('editDescription').value;
|
||||||
});
|
const remark = document.getElementById('editStatus').value || '';
|
||||||
|
const jfrom = document.getElementById('editOtStart').value || '';
|
||||||
if (!response.ok) {
|
const jto = document.getElementById('editOtEnd').value || '';
|
||||||
throw new Error(`HTTP ${response.status}: 업데이트에 실패했습니다.`);
|
|
||||||
}
|
console.log('Editing job with params:', { idx, jdate, cate, title, doit, remark, jfrom, jto });
|
||||||
|
|
||||||
const result = await response.text();
|
// machine.Jobreport_Edit(idx, jdate, cate, title, doit, remark, jfrom, jto) 호출
|
||||||
|
const jsonStr = await machine.Jobreport_Edit(idx, jdate, cate, title, doit, remark, jfrom, jto);
|
||||||
|
const result = JSON.parse(jsonStr);
|
||||||
|
|
||||||
console.log('Edit result:', result);
|
console.log('Edit result:', result);
|
||||||
|
|
||||||
// JSON 응답인지 확인
|
// 응답 형식 처리: { Success: true } 또는 { success: true }
|
||||||
try {
|
if (result.Success === false || result.success === false) {
|
||||||
const jsonResult = JSON.parse(result);
|
throw new Error(result.Message || result.message || '수정에 실패했습니다.');
|
||||||
if (!jsonResult.success) {
|
|
||||||
throw new Error(jsonResult.message || '수정에 실패했습니다.');
|
|
||||||
}
|
|
||||||
// 성공시 alert 없이 바로 진행
|
|
||||||
} catch (parseError) {
|
|
||||||
// JSON이 아닌 경우도 성공으로 처리 (alert 없음)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 성공시 alert 없이 바로 진행
|
||||||
// 모달 닫기
|
// 모달 닫기
|
||||||
closeModal();
|
closeModal();
|
||||||
|
|
||||||
// 데이터 새로고침
|
// 데이터 새로고침
|
||||||
await loadJobData();
|
await loadJobData();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating job:', error);
|
console.error('Error updating job:', error);
|
||||||
alert('업무일지 수정 중 오류가 발생했습니다: ' + error.message);
|
alert('업무일지 수정 중 오류가 발생했습니다: ' + error.message);
|
||||||
@@ -1798,35 +1630,34 @@
|
|||||||
|
|
||||||
async function handleDeleteJob() {
|
async function handleDeleteJob() {
|
||||||
const idx = document.getElementById('editIdx').value;
|
const idx = document.getElementById('editIdx').value;
|
||||||
|
|
||||||
if (!idx) {
|
if (!idx) {
|
||||||
alert('삭제할 수 없는 항목입니다.');
|
alert('삭제할 수 없는 항목입니다.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!confirm('정말로 이 업무일지를 삭제하시겠습니까?')) {
|
if (!confirm('정말로 이 업무일지를 삭제하시겠습니까?')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/Jobreport/Delete/${idx}`, {
|
console.log('Deleting job with idx:', idx);
|
||||||
method: 'DELETE'
|
|
||||||
});
|
// machine.Jobreport_Delete(idx) 호출
|
||||||
|
const jsonStr = await machine.Jobreport_Delete(idx);
|
||||||
if (!response.ok) {
|
const result = JSON.parse(jsonStr);
|
||||||
throw new Error('삭제에 실패했습니다.');
|
|
||||||
|
console.log('Delete result:', result);
|
||||||
|
|
||||||
|
// 응답 형식 처리: { Success: true } 또는 { success: true }
|
||||||
|
if (result.Success === false || result.success === false) {
|
||||||
|
throw new Error(result.Message || result.message || '삭제에 실패했습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json();
|
// 성공시 alert 없이 바로 모달 닫기
|
||||||
|
closeModal();
|
||||||
if (result.success) {
|
await loadJobData();
|
||||||
// 성공시 alert 없이 바로 모달 닫기
|
|
||||||
closeModal();
|
|
||||||
await loadJobData();
|
|
||||||
} else {
|
|
||||||
throw new Error(result.message || '삭제에 실패했습니다.');
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting job:', error);
|
console.error('Error deleting job:', error);
|
||||||
alert('업무일지 삭제 중 오류가 발생했습니다: ' + error.message);
|
alert('업무일지 삭제 중 오류가 발생했습니다: ' + error.message);
|
||||||
|
|||||||
@@ -473,6 +473,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 공통 네비게이션 -->
|
||||||
|
<script src="/js/common-navigation.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let projects = [];
|
let projects = [];
|
||||||
let currentProjectIdx = null;
|
let currentProjectIdx = null;
|
||||||
@@ -483,109 +486,14 @@
|
|||||||
let itemsPerPage = 10;
|
let itemsPerPage = 10;
|
||||||
let filteredProjects = [];
|
let filteredProjects = [];
|
||||||
|
|
||||||
|
// machine HostObject 참조
|
||||||
|
const machine = window.chrome.webview.hostObjects.machine;
|
||||||
|
|
||||||
// 초기화
|
// 초기화
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
initializeApp();
|
initializeApp();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 네비게이션 클래스
|
|
||||||
class CommonNavigation {
|
|
||||||
constructor(currentPage = '') {
|
|
||||||
this.currentPage = currentPage;
|
|
||||||
this.createNavigation();
|
|
||||||
}
|
|
||||||
|
|
||||||
async createNavigation() {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/Common/GetNavigationMenu');
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (data.Success && data.Data) {
|
|
||||||
this.createNavigationFromData(data.Data);
|
|
||||||
} else {
|
|
||||||
this.createFallbackNavigation();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Navigation initialization failed:', error);
|
|
||||||
this.createFallbackNavigation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createNavigationFromData(menuItems) {
|
|
||||||
const nav = document.createElement('nav');
|
|
||||||
nav.className = 'glass-effect border-b border-white/10';
|
|
||||||
nav.innerHTML = this.getNavigationHTML(menuItems);
|
|
||||||
document.body.insertBefore(nav, document.body.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
createFallbackNavigation() {
|
|
||||||
this.createNavigation();
|
|
||||||
}
|
|
||||||
|
|
||||||
createNavigation() {
|
|
||||||
const nav = document.createElement('nav');
|
|
||||||
nav.className = 'glass-effect border-b border-white/10';
|
|
||||||
nav.innerHTML = this.getNavigationHTML();
|
|
||||||
document.body.insertBefore(nav, document.body.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
getNavigationHTML(menuItems = null) {
|
|
||||||
if (!menuItems) {
|
|
||||||
menuItems = [
|
|
||||||
{ key: 'dashboard', title: '대시보드', url: '/Dashboard/', icon: 'M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z' },
|
|
||||||
{ key: 'common', title: '공용코드', url: '/Common', icon: 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z' },
|
|
||||||
{ key: 'jobreport', title: '업무일지', url: '/Jobreport/', icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' },
|
|
||||||
{ key: 'kuntae', title: '근태관리', url: '/Kuntae/', icon: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z' },
|
|
||||||
{ key: 'todo', title: '할일관리', url: '/Todo/', icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2M12 12l2 2 4-4' },
|
|
||||||
{ key: 'project', title: '프로젝트', url: '/Project/', icon: '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' }
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return `
|
|
||||||
<div class="container mx-auto px-4">
|
|
||||||
<div class="flex items-center justify-between h-16">
|
|
||||||
<div class="flex items-center space-x-8">
|
|
||||||
<a href="/Dashboard/" class="flex items-center space-x-2 hover:opacity-80 transition-opacity cursor-pointer">
|
|
||||||
<svg class="w-8 h-8 text-white" 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>
|
|
||||||
</svg>
|
|
||||||
<span class="text-xl font-bold text-white">GroupWare</span>
|
|
||||||
</a>
|
|
||||||
<nav class="hidden md:flex space-x-1">
|
|
||||||
${menuItems.map(item => `
|
|
||||||
<a href="${item.url}" class="px-3 py-2 rounded-md text-sm font-medium transition-colors ${
|
|
||||||
this.currentPage === item.key
|
|
||||||
? 'bg-white/20 text-white'
|
|
||||||
: 'text-white/60 hover:text-white hover:bg-white/10'
|
|
||||||
}">
|
|
||||||
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${item.icon}"></path>
|
|
||||||
</svg>
|
|
||||||
${item.title}
|
|
||||||
</a>
|
|
||||||
`).join('')}
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center space-x-4">
|
|
||||||
<div class="text-sm text-white/60">
|
|
||||||
<span id="currentUser">사용자</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initNavigation(currentPage = '') {
|
|
||||||
try {
|
|
||||||
new CommonNavigation(currentPage);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Navigation initialization failed:', error);
|
|
||||||
new CommonNavigation(currentPage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initializeApp() {
|
function initializeApp() {
|
||||||
initNavigation('project');
|
initNavigation('project');
|
||||||
getCurrentUser();
|
getCurrentUser();
|
||||||
@@ -593,24 +501,24 @@
|
|||||||
setupEventListeners();
|
setupEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentUser() {
|
async function getCurrentUser() {
|
||||||
// 현재 로그인된 사용자 정보 가져오기
|
// 현재 로그인된 사용자 정보 가져오기
|
||||||
fetch('/Common/GetCurrentUser')
|
try {
|
||||||
.then(response => response.json())
|
const jsonStr = await machine.GetCurrentUser();
|
||||||
.then(data => {
|
const data = JSON.parse(jsonStr);
|
||||||
if (data.Success && data.Data) {
|
|
||||||
currentUser = data.Data.userName || data.Data.name || '사용자';
|
if (data.Success && data.Data) {
|
||||||
document.getElementById('currentUser').textContent = currentUser;
|
currentUser = data.Data.userName || data.Data.name || '사용자';
|
||||||
} else {
|
document.getElementById('currentUser').textContent = currentUser;
|
||||||
currentUser = '사용자';
|
} else {
|
||||||
document.getElementById('currentUser').textContent = currentUser;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error getting current user:', error);
|
|
||||||
currentUser = '사용자';
|
currentUser = '사용자';
|
||||||
document.getElementById('currentUser').textContent = currentUser;
|
document.getElementById('currentUser').textContent = currentUser;
|
||||||
});
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting current user:', error);
|
||||||
|
currentUser = '사용자';
|
||||||
|
document.getElementById('currentUser').textContent = currentUser;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupEventListeners() {
|
function setupEventListeners() {
|
||||||
@@ -648,31 +556,31 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadProjects() {
|
async function loadProjects() {
|
||||||
const status = document.getElementById('statusFilter').value;
|
const status = document.getElementById('statusFilter').value;
|
||||||
const userFilter = document.getElementById('managerFilter').value;
|
const userFilter = document.getElementById('managerFilter').value;
|
||||||
|
|
||||||
fetch(`/Project/GetProjects?status=${status}&userFilter=${userFilter}`)
|
try {
|
||||||
.then(response => response.json())
|
const jsonStr = await machine.Project_GetProjects(status, userFilter);
|
||||||
.then(data => {
|
const data = JSON.parse(jsonStr);
|
||||||
if (!data.Success) {
|
|
||||||
console.error('Error:', data.Message);
|
if (!data.Success) {
|
||||||
document.getElementById('projectTableBody').innerHTML = '<tr><td colspan="23" class="px-6 py-4 text-center text-red-400">데이터를 불러오는데 실패했습니다.</td></tr>';
|
console.error('Error:', data.Message);
|
||||||
return;
|
|
||||||
}
|
|
||||||
projects = data.Data || [];
|
|
||||||
if (data.CurrentUser) {
|
|
||||||
currentUser = data.CurrentUser;
|
|
||||||
document.getElementById('currentUser').textContent = currentUser;
|
|
||||||
}
|
|
||||||
currentPage = 1;
|
|
||||||
filterData();
|
|
||||||
updateStatusCounts();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error:', error);
|
|
||||||
document.getElementById('projectTableBody').innerHTML = '<tr><td colspan="23" class="px-6 py-4 text-center text-red-400">데이터를 불러오는데 실패했습니다.</td></tr>';
|
document.getElementById('projectTableBody').innerHTML = '<tr><td colspan="23" class="px-6 py-4 text-center text-red-400">데이터를 불러오는데 실패했습니다.</td></tr>';
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
|
projects = data.Data || [];
|
||||||
|
if (data.CurrentUser) {
|
||||||
|
currentUser = data.CurrentUser;
|
||||||
|
document.getElementById('currentUser').textContent = currentUser;
|
||||||
|
}
|
||||||
|
currentPage = 1;
|
||||||
|
filterData();
|
||||||
|
updateStatusCounts();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
document.getElementById('projectTableBody').innerHTML = '<tr><td colspan="23" class="px-6 py-4 text-center text-red-400">데이터를 불러오는데 실패했습니다.</td></tr>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateStatusCounts() {
|
function updateStatusCounts() {
|
||||||
@@ -830,37 +738,37 @@
|
|||||||
document.getElementById('projectModal').classList.remove('hidden');
|
document.getElementById('projectModal').classList.remove('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
function editProject(idx) {
|
async function editProject(idx) {
|
||||||
currentProjectIdx = idx;
|
currentProjectIdx = idx;
|
||||||
|
|
||||||
fetch(`/Project/GetProject?id=${idx}`)
|
try {
|
||||||
.then(response => response.json())
|
const jsonStr = await machine.Project_GetProject(idx);
|
||||||
.then(data => {
|
const data = JSON.parse(jsonStr);
|
||||||
if (!data.Success) {
|
|
||||||
alert('프로젝트 정보를 불러오는데 실패했습니다: ' + data.Message);
|
if (!data.Success) {
|
||||||
return;
|
alert('프로젝트 정보를 불러오는데 실패했습니다: ' + data.Message);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
const project = data.Data;
|
|
||||||
document.getElementById('modalTitle').textContent = '프로젝트 편집';
|
const project = data.Data;
|
||||||
document.getElementById('projectIdx').value = project.idx;
|
document.getElementById('modalTitle').textContent = '프로젝트 편집';
|
||||||
document.getElementById('projectName').value = project.프로젝트명 || '';
|
document.getElementById('projectIdx').value = project.idx;
|
||||||
document.getElementById('projectProcess').value = project.프로젝트공정 || '';
|
document.getElementById('projectName').value = project.프로젝트명 || '';
|
||||||
document.getElementById('projectSdate').value = project.시작일 || '';
|
document.getElementById('projectProcess').value = project.프로젝트공정 || '';
|
||||||
document.getElementById('projectEdate').value = project.완료일 || '';
|
document.getElementById('projectSdate').value = project.시작일 || '';
|
||||||
document.getElementById('projectDdate').value = project.만료일 || '';
|
document.getElementById('projectEdate').value = project.완료일 || '';
|
||||||
document.getElementById('projectOdate').value = project.출고일 || '';
|
document.getElementById('projectDdate').value = project.만료일 || '';
|
||||||
document.getElementById('projectUserManager').value = project.프로젝트관리자 || '';
|
document.getElementById('projectOdate').value = project.출고일 || '';
|
||||||
document.getElementById('projectStatus').value = project.상태 || '진행';
|
document.getElementById('projectUserManager').value = project.프로젝트관리자 || '';
|
||||||
document.getElementById('projectMemo').value = project.memo || '';
|
document.getElementById('projectStatus').value = project.상태 || '진행';
|
||||||
|
document.getElementById('projectMemo').value = project.memo || '';
|
||||||
document.getElementById('deleteProjectBtn').classList.remove('hidden');
|
|
||||||
document.getElementById('projectModal').classList.remove('hidden');
|
document.getElementById('deleteProjectBtn').classList.remove('hidden');
|
||||||
})
|
document.getElementById('projectModal').classList.remove('hidden');
|
||||||
.catch(error => {
|
} catch (error) {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
alert('프로젝트 정보를 불러오는데 실패했습니다.');
|
alert('프로젝트 정보를 불러오는데 실패했습니다.');
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeProjectModal() {
|
function closeProjectModal() {
|
||||||
@@ -868,9 +776,9 @@
|
|||||||
document.getElementById('deleteModal').classList.add('hidden');
|
document.getElementById('deleteModal').classList.add('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFormSubmit(e) {
|
async function handleFormSubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const projectData = {
|
const projectData = {
|
||||||
idx: currentProjectIdx ? parseInt(document.getElementById('projectIdx').value) : 0,
|
idx: currentProjectIdx ? parseInt(document.getElementById('projectIdx').value) : 0,
|
||||||
name: document.getElementById('projectName').value,
|
name: document.getElementById('projectName').value,
|
||||||
@@ -883,19 +791,16 @@
|
|||||||
status: document.getElementById('projectStatus').value,
|
status: document.getElementById('projectStatus').value,
|
||||||
memo: document.getElementById('projectMemo').value
|
memo: document.getElementById('projectMemo').value
|
||||||
};
|
};
|
||||||
|
|
||||||
const url = currentProjectIdx ? '/Project/UpdateProject' : '/Project/CreateProject';
|
try {
|
||||||
const method = currentProjectIdx ? 'PUT' : 'POST';
|
let jsonStr;
|
||||||
|
if (currentProjectIdx) {
|
||||||
fetch(url, {
|
jsonStr = await machine.Project_UpdateProject(JSON.stringify(projectData));
|
||||||
method: method,
|
} else {
|
||||||
headers: {
|
jsonStr = await machine.Project_CreateProject(JSON.stringify(projectData));
|
||||||
'Content-Type': 'application/json',
|
}
|
||||||
},
|
const data = JSON.parse(jsonStr);
|
||||||
body: JSON.stringify(projectData)
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (!data.Success) {
|
if (!data.Success) {
|
||||||
alert('오류: ' + data.Message);
|
alert('오류: ' + data.Message);
|
||||||
} else {
|
} else {
|
||||||
@@ -903,11 +808,10 @@
|
|||||||
closeProjectModal();
|
closeProjectModal();
|
||||||
loadProjects();
|
loadProjects();
|
||||||
}
|
}
|
||||||
})
|
} catch (error) {
|
||||||
.catch(error => {
|
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
alert('저장 중 오류가 발생했습니다.');
|
alert('저장 중 오류가 발생했습니다.');
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteCurrentProject() {
|
function deleteCurrentProject() {
|
||||||
@@ -918,14 +822,13 @@
|
|||||||
document.getElementById('deleteModal').classList.add('hidden');
|
document.getElementById('deleteModal').classList.add('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmDelete() {
|
async function confirmDelete() {
|
||||||
if (!currentProjectIdx) return;
|
if (!currentProjectIdx) return;
|
||||||
|
|
||||||
fetch(`/Project/DeleteProject?id=${currentProjectIdx}`, {
|
try {
|
||||||
method: 'DELETE'
|
const jsonStr = await machine.Project_DeleteProject(currentProjectIdx);
|
||||||
})
|
const data = JSON.parse(jsonStr);
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (!data.Success) {
|
if (!data.Success) {
|
||||||
alert('오류: ' + data.Message);
|
alert('오류: ' + data.Message);
|
||||||
} else {
|
} else {
|
||||||
@@ -933,11 +836,10 @@
|
|||||||
closeProjectModal();
|
closeProjectModal();
|
||||||
loadProjects();
|
loadProjects();
|
||||||
}
|
}
|
||||||
})
|
} catch (error) {
|
||||||
.catch(error => {
|
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
alert('삭제 중 오류가 발생했습니다.');
|
alert('삭제 중 오류가 발생했습니다.');
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStatusClass(status) {
|
function getStatusClass(status) {
|
||||||
|
|||||||
@@ -1,158 +0,0 @@
|
|||||||
<!-- 공통 네비게이션 -->
|
|
||||||
<nav id="main-navigation" class="glass-effect border-b border-white/10 relative z-40">
|
|
||||||
<div class="container mx-auto px-4">
|
|
||||||
<div class="flex items-center justify-between h-16">
|
|
||||||
<!-- 로고 및 브랜드 -->
|
|
||||||
<div class="flex items-center space-x-8">
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z"></path>
|
|
||||||
</svg>
|
|
||||||
<span class="text-xl font-bold text-white">GroupWare</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 데스크톱 메뉴 -->
|
|
||||||
<nav class="hidden md:flex space-x-1">
|
|
||||||
<a href="/DashBoard/"
|
|
||||||
class="nav-item px-3 py-2 rounded-md text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10"
|
|
||||||
data-page="dashboard">
|
|
||||||
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z"></path>
|
|
||||||
</svg>
|
|
||||||
대시보드
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/Common/"
|
|
||||||
class="nav-item px-3 py-2 rounded-md text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10"
|
|
||||||
data-page="common">
|
|
||||||
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
|
||||||
</svg>
|
|
||||||
공용코드
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/Jobreport/"
|
|
||||||
class="nav-item px-3 py-2 rounded-md text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10"
|
|
||||||
data-page="jobreport">
|
|
||||||
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
|
|
||||||
</svg>
|
|
||||||
업무일지
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/Kuntae/"
|
|
||||||
class="nav-item px-3 py-2 rounded-md text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10"
|
|
||||||
data-page="kuntae">
|
|
||||||
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
||||||
</svg>
|
|
||||||
근태관리
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/Todo/"
|
|
||||||
class="nav-item px-3 py-2 rounded-md text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10"
|
|
||||||
data-page="todo">
|
|
||||||
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2M12 12l2 2 4-4"></path>
|
|
||||||
</svg>
|
|
||||||
할일관리
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/Project/"
|
|
||||||
class="nav-item px-3 py-2 rounded-md text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10"
|
|
||||||
data-page="project">
|
|
||||||
<svg class="w-4 h-4 inline mr-1" 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>
|
|
||||||
</svg>
|
|
||||||
프로젝트
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/Purchase/"
|
|
||||||
class="nav-item px-3 py-2 rounded-md text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10"
|
|
||||||
data-page="purchase">
|
|
||||||
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"></path>
|
|
||||||
</svg>
|
|
||||||
구매관리
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/Customer/"
|
|
||||||
class="nav-item px-3 py-2 rounded-md text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10"
|
|
||||||
data-page="customer">
|
|
||||||
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
|
||||||
</svg>
|
|
||||||
고객관리
|
|
||||||
</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 우측 메뉴 -->
|
|
||||||
<div class="flex items-center space-x-4">
|
|
||||||
<div class="text-sm text-white/60">
|
|
||||||
<span>사용자</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 모바일 메뉴 버튼 -->
|
|
||||||
<button id="mobile-menu-button"
|
|
||||||
class="md:hidden text-white/60 hover:text-white focus:outline-none focus:text-white">
|
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 모바일 메뉴 -->
|
|
||||||
<div id="mobile-menu" class="md:hidden border-t border-white/10 py-2 hidden">
|
|
||||||
<a href="/DashBoard/" class="nav-item-mobile block px-3 py-2 text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10" data-page="dashboard">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z"></path>
|
|
||||||
</svg>
|
|
||||||
대시보드
|
|
||||||
</a>
|
|
||||||
<a href="/Common/" class="nav-item-mobile block px-3 py-2 text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10" data-page="common">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
|
||||||
</svg>
|
|
||||||
공용코드
|
|
||||||
</a>
|
|
||||||
<a href="/Jobreport/" class="nav-item-mobile block px-3 py-2 text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10" data-page="jobreport">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
|
|
||||||
</svg>
|
|
||||||
업무일지
|
|
||||||
</a>
|
|
||||||
<a href="/Kuntae/" class="nav-item-mobile block px-3 py-2 text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10" data-page="kuntae">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
||||||
</svg>
|
|
||||||
근태관리
|
|
||||||
</a>
|
|
||||||
<a href="/Todo/" class="nav-item-mobile block px-3 py-2 text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10" data-page="todo">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2M12 12l2 2 4-4"></path>
|
|
||||||
</svg>
|
|
||||||
할일관리
|
|
||||||
</a>
|
|
||||||
<a href="/Project/" class="nav-item-mobile block px-3 py-2 text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10" data-page="project">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" 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>
|
|
||||||
</svg>
|
|
||||||
프로젝트
|
|
||||||
</a>
|
|
||||||
<a href="/Purchase/" class="nav-item-mobile block px-3 py-2 text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10" data-page="purchase">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"></path>
|
|
||||||
</svg>
|
|
||||||
구매관리
|
|
||||||
</a>
|
|
||||||
<a href="/Customer/" class="nav-item-mobile block px-3 py-2 text-sm font-medium transition-colors text-white/60 hover:text-white hover:bg-white/10" data-page="customer">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
|
||||||
</svg>
|
|
||||||
고객관리
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
// 공통 네비게이션 컴포넌트
|
|
||||||
class CommonNavigation {
|
|
||||||
constructor(currentPage = '') {
|
|
||||||
this.currentPage = currentPage;
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
this.createNavigation();
|
|
||||||
this.addEventListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
createNavigation() {
|
|
||||||
const nav = document.createElement('nav');
|
|
||||||
nav.className = 'glass-effect border-b border-white/10';
|
|
||||||
nav.innerHTML = this.getNavigationHTML();
|
|
||||||
|
|
||||||
// body의 첫 번째 자식으로 추가
|
|
||||||
document.body.insertBefore(nav, document.body.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
getNavigationHTML() {
|
|
||||||
return `
|
|
||||||
<div class="container mx-auto px-4">
|
|
||||||
<div class="flex items-center justify-between h-16">
|
|
||||||
<!-- 로고/타이틀 -->
|
|
||||||
<div class="flex items-center">
|
|
||||||
<h2 class="text-xl font-bold text-white">GroupWare</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 메뉴 -->
|
|
||||||
<div class="hidden md:flex items-center space-x-8">
|
|
||||||
${this.getMenuItemHTML('dashboard', '/Dashboard/', '대시보드', 'M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z')}
|
|
||||||
${this.getMenuItemHTML('common', '/Common', '공용코드', 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z')}
|
|
||||||
${this.getMenuItemHTML('jobreport', '/Jobreport/', '업무일지', 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2')}
|
|
||||||
${this.getMenuItemHTML('kuntae', '/Kuntae/', '근태관리', 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 모바일 메뉴 버튼 -->
|
|
||||||
<div class="md:hidden">
|
|
||||||
<button id="mobile-menu-button" class="text-white/80 hover:text-white transition-colors p-2">
|
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 모바일 메뉴 -->
|
|
||||||
<div id="mobile-menu" class="md:hidden hidden border-t border-white/10 pt-4 pb-4">
|
|
||||||
${this.getMobileMenuItemHTML('dashboard', '/Dashboard/', '대시보드', 'M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z')}
|
|
||||||
${this.getMobileMenuItemHTML('common', '/Common', '공용코드', 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z')}
|
|
||||||
${this.getMobileMenuItemHTML('jobreport', '/Jobreport/', '업무일지', 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2')}
|
|
||||||
${this.getMobileMenuItemHTML('kuntae', '/Kuntae/', '근태관리', 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMenuItemHTML(pageKey, href, text, svgPath) {
|
|
||||||
const isActive = this.currentPage === pageKey;
|
|
||||||
const activeClass = isActive ? 'text-white bg-white/20' : 'text-white/80 hover:text-white hover:bg-white/10';
|
|
||||||
|
|
||||||
return `
|
|
||||||
<a href="${href}" class="${activeClass} transition-colors px-3 py-2 rounded-lg">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${svgPath}"></path>
|
|
||||||
</svg>
|
|
||||||
${text}
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMobileMenuItemHTML(pageKey, href, text, svgPath) {
|
|
||||||
const isActive = this.currentPage === pageKey;
|
|
||||||
const activeClass = isActive ? 'text-white bg-white/20' : 'text-white/80 hover:text-white hover:bg-white/10';
|
|
||||||
|
|
||||||
return `
|
|
||||||
<a href="${href}" class="block ${activeClass} transition-colors px-3 py-2 rounded-lg mb-2">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${svgPath}"></path>
|
|
||||||
</svg>
|
|
||||||
${text}
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
addEventListeners() {
|
|
||||||
// 모바일 메뉴 토글
|
|
||||||
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
|
||||||
const mobileMenu = document.getElementById('mobile-menu');
|
|
||||||
|
|
||||||
if (mobileMenuButton && mobileMenu) {
|
|
||||||
mobileMenuButton.addEventListener('click', function() {
|
|
||||||
mobileMenu.classList.toggle('hidden');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 전역 함수로 내비게이션 초기화
|
|
||||||
function initNavigation(currentPage = '') {
|
|
||||||
// DOM이 로드된 후에 실행
|
|
||||||
if (document.readyState === 'loading') {
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
new CommonNavigation(currentPage);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
new CommonNavigation(currentPage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
/**
|
|
||||||
* 공통 네비게이션 컴포넌트
|
|
||||||
* 서버에서 메뉴 정보를 받아와서 동적으로 네비게이션을 생성합니다.
|
|
||||||
*/
|
|
||||||
class CommonNavigation {
|
|
||||||
constructor(currentPage = '') {
|
|
||||||
this.currentPage = currentPage;
|
|
||||||
this.menuItems = [];
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
try {
|
|
||||||
await this.loadMenuItems();
|
|
||||||
this.createNavigation();
|
|
||||||
this.addEventListeners();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Navigation initialization failed:', error);
|
|
||||||
// 오류 발생 시 기본 메뉴로 폴백
|
|
||||||
this.createFallbackNavigation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadMenuItems() {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/Common/GetNavigationMenu');
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
|
||||||
}
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (data.Success && data.Data) {
|
|
||||||
this.menuItems = data.Data;
|
|
||||||
} else {
|
|
||||||
throw new Error(data.Message || 'Failed to load menu items');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to load navigation menu:', error);
|
|
||||||
// 기본 메뉴 항목으로 폴백
|
|
||||||
this.menuItems = this.getDefaultMenuItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getDefaultMenuItems() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
key: 'dashboard',
|
|
||||||
title: '대시보드',
|
|
||||||
url: '/Dashboard/',
|
|
||||||
icon: 'M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z M8 5a2 2 0 012-2h4a2 2 0 012 2v2H8V5z',
|
|
||||||
isVisible: true,
|
|
||||||
sortOrder: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'common',
|
|
||||||
title: '공용코드',
|
|
||||||
url: '/Common',
|
|
||||||
icon: 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z',
|
|
||||||
isVisible: true,
|
|
||||||
sortOrder: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'jobreport',
|
|
||||||
title: '업무일지',
|
|
||||||
url: '/Jobreport/',
|
|
||||||
icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2',
|
|
||||||
isVisible: true,
|
|
||||||
sortOrder: 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'kuntae',
|
|
||||||
title: '근태관리',
|
|
||||||
url: '/Kuntae/',
|
|
||||||
icon: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z',
|
|
||||||
isVisible: true,
|
|
||||||
sortOrder: 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'todo',
|
|
||||||
title: '할일관리',
|
|
||||||
url: '/Todo/',
|
|
||||||
icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2M12 12l2 2 4-4',
|
|
||||||
isVisible: true,
|
|
||||||
sortOrder: 5
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
createNavigation() {
|
|
||||||
const nav = document.createElement('nav');
|
|
||||||
nav.className = 'glass-effect border-b border-white/10';
|
|
||||||
nav.innerHTML = this.getNavigationHTML();
|
|
||||||
|
|
||||||
// body의 첫 번째 자식으로 추가
|
|
||||||
document.body.insertBefore(nav, document.body.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
createFallbackNavigation() {
|
|
||||||
console.log('Creating fallback navigation...');
|
|
||||||
this.createNavigation();
|
|
||||||
}
|
|
||||||
|
|
||||||
getNavigationHTML() {
|
|
||||||
const visibleItems = this.menuItems
|
|
||||||
.filter(item => item.isVisible)
|
|
||||||
.sort((a, b) => a.sortOrder - b.sortOrder);
|
|
||||||
|
|
||||||
return `
|
|
||||||
<div class="container mx-auto px-4">
|
|
||||||
<div class="flex items-center justify-between h-16">
|
|
||||||
<!-- 로고/타이틀 -->
|
|
||||||
<div class="flex items-center">
|
|
||||||
<h2 class="text-xl font-bold text-white">GroupWare</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 메뉴 -->
|
|
||||||
<div class="hidden md:flex items-center space-x-8">
|
|
||||||
${visibleItems.map(item => this.getMenuItemHTML(item)).join('')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 모바일 메뉴 버튼 -->
|
|
||||||
<div class="md:hidden">
|
|
||||||
<button id="mobile-menu-button" class="text-white/80 hover:text-white transition-colors p-2">
|
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 모바일 메뉴 -->
|
|
||||||
<div id="mobile-menu" class="md:hidden hidden border-t border-white/10 pt-4 pb-4">
|
|
||||||
${visibleItems.map(item => this.getMobileMenuItemHTML(item)).join('')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMenuItemHTML(item) {
|
|
||||||
const isActive = this.currentPage === item.key;
|
|
||||||
const activeClass = isActive ? 'text-white bg-white/20' : 'text-white/80 hover:text-white hover:bg-white/10';
|
|
||||||
|
|
||||||
return `
|
|
||||||
<a href="${item.url}" class="${activeClass} transition-colors px-3 py-2 rounded-lg">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${item.icon}"></path>
|
|
||||||
</svg>
|
|
||||||
${item.title}
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMobileMenuItemHTML(item) {
|
|
||||||
const isActive = this.currentPage === item.key;
|
|
||||||
const activeClass = isActive ? 'text-white bg-white/20' : 'text-white/80 hover:text-white hover:bg-white/10';
|
|
||||||
|
|
||||||
return `
|
|
||||||
<a href="${item.url}" class="block ${activeClass} transition-colors px-3 py-2 rounded-lg mb-2">
|
|
||||||
<svg class="w-4 h-4 inline mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${item.icon}"></path>
|
|
||||||
</svg>
|
|
||||||
${item.title}
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
addEventListeners() {
|
|
||||||
// 모바일 메뉴 토글
|
|
||||||
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
|
||||||
const mobileMenu = document.getElementById('mobile-menu');
|
|
||||||
|
|
||||||
if (mobileMenuButton && mobileMenu) {
|
|
||||||
mobileMenuButton.addEventListener('click', function() {
|
|
||||||
mobileMenu.classList.toggle('hidden');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 전역 함수로 내비게이션 초기화
|
|
||||||
function initNavigation(currentPage = '') {
|
|
||||||
// DOM이 로드된 후에 실행
|
|
||||||
if (document.readyState === 'loading') {
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
new CommonNavigation(currentPage);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
new CommonNavigation(currentPage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ES6 모듈로도 사용 가능하도록 export (필요시)
|
|
||||||
if (typeof module !== 'undefined' && module.exports) {
|
|
||||||
module.exports = { CommonNavigation, initNavigation };
|
|
||||||
}
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
C# 프로젝트 파일 자동 업데이트 도구
|
|
||||||
새로운 파일을 EETGW.csproj에 자동으로 등록합니다.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from typing import List, Tuple, Optional
|
|
||||||
|
|
||||||
class ProjectFileUpdater:
|
|
||||||
def __init__(self, project_path: str = "EETGW.csproj"):
|
|
||||||
self.project_path = project_path
|
|
||||||
self.content = ""
|
|
||||||
self.load_project()
|
|
||||||
|
|
||||||
def load_project(self) -> bool:
|
|
||||||
"""프로젝트 파일 로드"""
|
|
||||||
try:
|
|
||||||
with open(self.project_path, 'r', encoding='utf-8') as f:
|
|
||||||
self.content = f.read()
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 프로젝트 파일을 읽을 수 없습니다: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def save_project(self) -> bool:
|
|
||||||
"""프로젝트 파일 저장"""
|
|
||||||
try:
|
|
||||||
with open(self.project_path, 'w', encoding='utf-8') as f:
|
|
||||||
f.write(self.content)
|
|
||||||
print("✅ 프로젝트 파일이 업데이트되었습니다.")
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 프로젝트 파일 저장 실패: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_file_registered(self, file_path: str) -> bool:
|
|
||||||
"""파일이 이미 등록되어 있는지 확인"""
|
|
||||||
# Windows 스타일 경로로 변환
|
|
||||||
windows_path = file_path.replace('/', '\\')
|
|
||||||
unix_path = file_path.replace('\\', '/')
|
|
||||||
|
|
||||||
patterns = [
|
|
||||||
f'Include="{windows_path}"',
|
|
||||||
f"Include='{windows_path}'",
|
|
||||||
f'Include="{unix_path}"',
|
|
||||||
f"Include='{unix_path}'"
|
|
||||||
]
|
|
||||||
|
|
||||||
return any(pattern in self.content for pattern in patterns)
|
|
||||||
|
|
||||||
def find_last_wwwroot_entry(self) -> Optional[Tuple[int, int]]:
|
|
||||||
"""마지막 wwwroot 관련 항목의 위치 찾기"""
|
|
||||||
pattern = r'<(?:Content|None) Include="Web\\wwwroot.*?</(?:Content|None)>'
|
|
||||||
matches = list(re.finditer(pattern, self.content, re.DOTALL))
|
|
||||||
|
|
||||||
if matches:
|
|
||||||
last_match = matches[-1]
|
|
||||||
return (last_match.start(), last_match.end())
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def add_file_to_project(self, file_path: str, file_type: str = "None") -> bool:
|
|
||||||
"""파일을 프로젝트에 추가"""
|
|
||||||
# Windows 스타일 경로로 변환
|
|
||||||
windows_path = file_path.replace('/', '\\')
|
|
||||||
|
|
||||||
# 이미 등록된 파일인지 확인
|
|
||||||
if self.is_file_registered(windows_path):
|
|
||||||
print(f"⚠️ 파일이 이미 등록되어 있습니다: {windows_path}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 새로운 항목 생성
|
|
||||||
new_entry = f''' <{file_type} Include="{windows_path}">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</{file_type}>'''
|
|
||||||
|
|
||||||
# 마지막 wwwroot 항목 찾기
|
|
||||||
last_entry_pos = self.find_last_wwwroot_entry()
|
|
||||||
|
|
||||||
if last_entry_pos:
|
|
||||||
start_pos, end_pos = last_entry_pos
|
|
||||||
# 마지막 항목 다음에 새 항목 삽입
|
|
||||||
self.content = (
|
|
||||||
self.content[:end_pos] +
|
|
||||||
'\n' + new_entry +
|
|
||||||
self.content[end_pos:]
|
|
||||||
)
|
|
||||||
print(f"✅ 파일이 프로젝트에 추가되었습니다: {windows_path}")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("❌ wwwroot 섹션을 찾을 수 없습니다.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def add_multiple_files(self, files: List[Tuple[str, str]]) -> int:
|
|
||||||
"""여러 파일을 한 번에 추가"""
|
|
||||||
added_count = 0
|
|
||||||
|
|
||||||
for file_path, file_type in files:
|
|
||||||
if self.add_file_to_project(file_path, file_type):
|
|
||||||
added_count += 1
|
|
||||||
|
|
||||||
return added_count
|
|
||||||
|
|
||||||
def add_react_component_files(component_name: str) -> bool:
|
|
||||||
"""React 컴포넌트 관련 파일들을 프로젝트에 추가"""
|
|
||||||
updater = ProjectFileUpdater()
|
|
||||||
|
|
||||||
files_to_add = [
|
|
||||||
(f"Web\\wwwroot\\react\\{component_name}.jsx", "None"),
|
|
||||||
(f"Web\\wwwroot\\react-{component_name.lower()}.html", "None")
|
|
||||||
]
|
|
||||||
|
|
||||||
added_count = updater.add_multiple_files(files_to_add)
|
|
||||||
|
|
||||||
if added_count > 0:
|
|
||||||
updater.save_project()
|
|
||||||
print(f"🎉 {component_name} 컴포넌트 관련 {added_count}개 파일이 등록되었습니다!")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("추가된 파일이 없습니다.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def add_single_file(file_path: str, file_type: str = "None") -> bool:
|
|
||||||
"""단일 파일을 프로젝트에 추가"""
|
|
||||||
updater = ProjectFileUpdater()
|
|
||||||
|
|
||||||
if updater.add_file_to_project(file_path, file_type):
|
|
||||||
updater.save_project()
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""CLI 메인 함수"""
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print("사용법:")
|
|
||||||
print(" python _add_to_project.py <파일경로> [파일타입]")
|
|
||||||
print(" python _add_to_project.py --react <컴포넌트명>")
|
|
||||||
print("")
|
|
||||||
print("예시:")
|
|
||||||
print(" python _add_to_project.py 'Web\\wwwroot\\react\\MyComponent.jsx'")
|
|
||||||
print(" python _add_to_project.py --react Dashboard")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if sys.argv[1] == "--react" and len(sys.argv) >= 3:
|
|
||||||
component_name = sys.argv[2]
|
|
||||||
add_react_component_files(component_name)
|
|
||||||
else:
|
|
||||||
file_path = sys.argv[1]
|
|
||||||
file_type = sys.argv[2] if len(sys.argv) > 2 else "None"
|
|
||||||
add_single_file(file_path, file_type)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
// C# 프로젝트 파일 자동 업데이트 헬퍼
|
|
||||||
// 새로운 파일을 생성할 때 EETGW.csproj에 자동으로 등록하는 스크립트
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
class ProjectUpdater {
|
|
||||||
constructor(projectPath = 'EETGW.csproj') {
|
|
||||||
this.projectPath = projectPath;
|
|
||||||
this.projectContent = '';
|
|
||||||
this.loadProject();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadProject() {
|
|
||||||
try {
|
|
||||||
this.projectContent = fs.readFileSync(this.projectPath, 'utf8');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('프로젝트 파일을 읽을 수 없습니다:', error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 새로운 파일을 프로젝트에 등록
|
|
||||||
* @param {string} filePath - Web/wwwroot로 시작하는 상대 경로
|
|
||||||
* @param {string} fileType - 'Content' 또는 'None' (기본값: 'None')
|
|
||||||
*/
|
|
||||||
addFile(filePath, fileType = 'None') {
|
|
||||||
// 파일 경로를 백슬래시로 변경 (Windows 형식)
|
|
||||||
const windowsPath = filePath.replace(/\//g, '\\');
|
|
||||||
|
|
||||||
// 이미 등록된 파일인지 확인
|
|
||||||
if (this.isFileAlreadyRegistered(windowsPath)) {
|
|
||||||
console.log(`파일이 이미 등록되어 있습니다: ${windowsPath}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 새로운 항목 생성
|
|
||||||
const newEntry = ` <${fileType} Include="${windowsPath}">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</${fileType}>`;
|
|
||||||
|
|
||||||
// 마지막 wwwroot 관련 항목 찾기
|
|
||||||
const lastWwwrootMatch = this.findLastWwwrootEntry();
|
|
||||||
|
|
||||||
if (lastWwwrootMatch) {
|
|
||||||
// 마지막 wwwroot 항목 다음에 삽입
|
|
||||||
const insertPosition = lastWwwrootMatch.index + lastWwwrootMatch[0].length;
|
|
||||||
this.projectContent =
|
|
||||||
this.projectContent.slice(0, insertPosition) +
|
|
||||||
'\n' + newEntry +
|
|
||||||
this.projectContent.slice(insertPosition);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error('wwwroot 섹션을 찾을 수 없습니다.');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 파일이 이미 등록되어 있는지 확인
|
|
||||||
*/
|
|
||||||
isFileAlreadyRegistered(filePath) {
|
|
||||||
const patterns = [
|
|
||||||
`Include="${filePath}"`,
|
|
||||||
`Include='${filePath}'`,
|
|
||||||
filePath.replace(/\\/g, '/') // 슬래시 형태도 확인
|
|
||||||
];
|
|
||||||
|
|
||||||
return patterns.some(pattern => this.projectContent.includes(pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 마지막 wwwroot 관련 항목 찾기
|
|
||||||
*/
|
|
||||||
findLastWwwrootEntry() {
|
|
||||||
const wwwrootPattern = /<(?:Content|None) Include="Web\\wwwroot.*?<\/(?:Content|None)>/gs;
|
|
||||||
let lastMatch = null;
|
|
||||||
let match;
|
|
||||||
|
|
||||||
while ((match = wwwrootPattern.exec(this.projectContent)) !== null) {
|
|
||||||
lastMatch = match;
|
|
||||||
}
|
|
||||||
|
|
||||||
return lastMatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 프로젝트 파일 저장
|
|
||||||
*/
|
|
||||||
saveProject() {
|
|
||||||
try {
|
|
||||||
fs.writeFileSync(this.projectPath, this.projectContent, 'utf8');
|
|
||||||
console.log('프로젝트 파일이 업데이트되었습니다.');
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('프로젝트 파일 저장 실패:', error.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 여러 파일을 한 번에 등록
|
|
||||||
*/
|
|
||||||
addFiles(files) {
|
|
||||||
let hasChanges = false;
|
|
||||||
|
|
||||||
files.forEach(({ path, type = 'None' }) => {
|
|
||||||
if (this.addFile(path, type)) {
|
|
||||||
hasChanges = true;
|
|
||||||
console.log(`추가됨: ${path}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return hasChanges;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* React 관련 파일들 자동 등록
|
|
||||||
*/
|
|
||||||
addReactFiles(basePath) {
|
|
||||||
const reactFiles = [
|
|
||||||
{ path: `${basePath}.html`, type: 'None' },
|
|
||||||
{ path: `${basePath}.jsx`, type: 'None' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return this.addFiles(reactFiles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 사용 예시
|
|
||||||
function addNewReactComponent(componentName) {
|
|
||||||
const updater = new ProjectUpdater();
|
|
||||||
const basePath = `Web\\wwwroot\\react\\${componentName}`;
|
|
||||||
|
|
||||||
const files = [
|
|
||||||
{ path: `${basePath}.jsx`, type: 'None' },
|
|
||||||
{ path: `Web\\wwwroot\\react-${componentName.toLowerCase()}.html`, type: 'None' }
|
|
||||||
];
|
|
||||||
|
|
||||||
if (updater.addFiles(files)) {
|
|
||||||
updater.saveProject();
|
|
||||||
console.log(`✅ ${componentName} 컴포넌트 파일들이 프로젝트에 등록되었습니다.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 일반 파일 추가
|
|
||||||
function addNewFile(filePath, fileType = 'None') {
|
|
||||||
const updater = new ProjectUpdater();
|
|
||||||
|
|
||||||
if (updater.addFile(filePath, fileType)) {
|
|
||||||
updater.saveProject();
|
|
||||||
console.log(`✅ 파일이 프로젝트에 등록되었습니다: ${filePath}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
ProjectUpdater,
|
|
||||||
addNewReactComponent,
|
|
||||||
addNewFile
|
|
||||||
};
|
|
||||||
|
|
||||||
// CLI에서 직접 실행할 수 있도록
|
|
||||||
if (require.main === module) {
|
|
||||||
const args = process.argv.slice(2);
|
|
||||||
|
|
||||||
if (args.length === 0) {
|
|
||||||
console.log('사용법: node _project_updater.js <파일경로> [파일타입]');
|
|
||||||
console.log('예시: node _project_updater.js "Web\\wwwroot\\react\\NewComponent.jsx" None');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const filePath = args[0];
|
|
||||||
const fileType = args[1] || 'None';
|
|
||||||
|
|
||||||
addNewFile(filePath, fileType);
|
|
||||||
}
|
|
||||||
2
Project/fMain.Designer.cs
generated
2
Project/fMain.Designer.cs
generated
@@ -241,6 +241,7 @@
|
|||||||
//
|
//
|
||||||
// menuStrip1
|
// menuStrip1
|
||||||
//
|
//
|
||||||
|
this.menuStrip1.Enabled = false;
|
||||||
this.menuStrip1.Font = new System.Drawing.Font("맑은 고딕", 10F);
|
this.menuStrip1.Font = new System.Drawing.Font("맑은 고딕", 10F);
|
||||||
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
this.btSetting,
|
this.btSetting,
|
||||||
@@ -1113,6 +1114,7 @@
|
|||||||
//
|
//
|
||||||
this.toolStrip1.AutoSize = false;
|
this.toolStrip1.AutoSize = false;
|
||||||
this.toolStrip1.Dock = System.Windows.Forms.DockStyle.Right;
|
this.toolStrip1.Dock = System.Windows.Forms.DockStyle.Right;
|
||||||
|
this.toolStrip1.Enabled = false;
|
||||||
this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
|
this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
|
||||||
this.toolStrip1.ImageScalingSize = new System.Drawing.Size(24, 24);
|
this.toolStrip1.ImageScalingSize = new System.Drawing.Size(24, 24);
|
||||||
this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||||
|
|||||||
@@ -206,15 +206,56 @@ namespace Project
|
|||||||
|
|
||||||
Func_Login();
|
Func_Login();
|
||||||
|
|
||||||
|
// WebView2 로그인이 아닌 경우에만 여기서 후처리 실행
|
||||||
|
// WebView2 로그인의 경우 OnLoginCompleted()에서 호출됨
|
||||||
|
if (!(webok && Pub.InitWebView == 1))
|
||||||
|
{
|
||||||
|
OnLoginCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 로그인 완료 후 실행되는 처리
|
||||||
|
/// WebView2 로그인의 경우 MachineBridge에서 호출됨
|
||||||
|
/// </summary>
|
||||||
|
public void OnLoginCompleted()
|
||||||
|
{
|
||||||
|
menuStrip1.Enabled = true;
|
||||||
|
toolStrip1.Enabled = true;
|
||||||
|
this.mn_purchase.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_purchase);
|
||||||
|
this.mn_project.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_project);
|
||||||
|
this.mn_dailyhistory.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_history);
|
||||||
|
this.mn_jago.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_jago);
|
||||||
|
//this.mn_eq.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_equipment);
|
||||||
|
this.mn_kuntae.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_workday);
|
||||||
|
this.mn_docu.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_docu);
|
||||||
|
//this.mn_logdata.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_logdata);
|
||||||
|
|
||||||
|
//220421
|
||||||
|
FCOMMON.info.Disable_8hourover = Pub.setting.Disable8HourOver;
|
||||||
|
|
||||||
|
if (FCOMMON.info.Login.level >= 10) btDev.Visible = true;
|
||||||
|
|
||||||
|
sbLogin.Text = string.Format("[{0}] ({1}-{2} T:{3}) - ({5}){4}",
|
||||||
|
FCOMMON.info.Login.title,
|
||||||
|
FCOMMON.info.Login.no,
|
||||||
|
FCOMMON.info.Login.nameK,
|
||||||
|
FCOMMON.info.Login.tel,
|
||||||
|
FCOMMON.info.Login.dept,
|
||||||
|
FCOMMON.info.Login.gcode);
|
||||||
|
FCOMMON.Pub.log.Add("Program Start");
|
||||||
|
|
||||||
|
sbLoginUseTime.Text = "접속시간:" + FCOMMON.info.Login.loginusetime.ToString("N1") + "ms";
|
||||||
|
|
||||||
// Start chat service after login
|
// Start chat service after login
|
||||||
StartChatService();
|
StartChatService();
|
||||||
|
|
||||||
///즐겨찾기 목록 갱신
|
// 즐겨찾기 목록 갱신
|
||||||
Update_FavoriteSite();
|
Update_FavoriteSite();
|
||||||
|
|
||||||
UpdateControls();
|
UpdateControls();
|
||||||
|
|
||||||
//사용기록추적
|
// 사용기록추적
|
||||||
var remark = $"{FCOMMON.info.Login.gcode}|{FCOMMON.info.Login.dept}";
|
var remark = $"{FCOMMON.info.Login.gcode}|{FCOMMON.info.Login.dept}";
|
||||||
Pub.CheckNRegister5(Application.ProductName, "chi", Application.ProductVersion, FCOMMON.info.Login.no, remark);
|
Pub.CheckNRegister5(Application.ProductName, "chi", Application.ProductVersion, FCOMMON.info.Login.no, remark);
|
||||||
}
|
}
|
||||||
@@ -247,9 +288,10 @@ namespace Project
|
|||||||
this.sbWeb.Text = $"Host:{(webok ? "O" : "X")},WebView:{Pub.InitWebView},Server:{Pub.WebServiceURL}";
|
this.sbWeb.Text = $"Host:{(webok ? "O" : "X")},WebView:{Pub.InitWebView},Server:{Pub.WebServiceURL}";
|
||||||
if (webok && Pub.InitWebView == 1)
|
if (webok && Pub.InitWebView == 1)
|
||||||
{
|
{
|
||||||
using (var f = new Dialog.fLogin_WB())
|
//using (var f = new Dialog.fLogin_WB())
|
||||||
if (f.ShowDialog() != System.Windows.Forms.DialogResult.OK)
|
// if (f.ShowDialog() != System.Windows.Forms.DialogResult.OK)
|
||||||
Application.ExitThread();
|
// Application.ExitThread();
|
||||||
|
Menu_Dashboard();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -259,32 +301,7 @@ namespace Project
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.mn_purchase.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_purchase);
|
|
||||||
this.mn_project.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_project);
|
|
||||||
this.mn_dailyhistory.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_history);
|
|
||||||
this.mn_jago.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_jago);
|
|
||||||
//this.mn_eq.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_equipment);
|
|
||||||
this.mn_kuntae.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_workday);
|
|
||||||
this.mn_docu.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_docu);
|
|
||||||
//this.mn_logdata.Visible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_logdata);
|
|
||||||
|
|
||||||
//220421
|
|
||||||
FCOMMON.info.Disable_8hourover = Pub.setting.Disable8HourOver;
|
|
||||||
|
|
||||||
if (FCOMMON.info.Login.level >= 10) btDev.Visible = true;
|
|
||||||
|
|
||||||
sbLogin.Text = string.Format("[{0}] ({1}-{2} T:{3}) - ({5}){4}",
|
|
||||||
FCOMMON.info.Login.title,
|
|
||||||
FCOMMON.info.Login.no,
|
|
||||||
FCOMMON.info.Login.nameK,
|
|
||||||
FCOMMON.info.Login.tel,
|
|
||||||
FCOMMON.info.Login.dept,
|
|
||||||
FCOMMON.info.Login.gcode);
|
|
||||||
FCOMMON.Pub.log.Add("Program Start");
|
|
||||||
|
|
||||||
sbLoginUseTime.Text = "접속시간:" + FCOMMON.info.Login.loginusetime.ToString("N1") + "ms";
|
|
||||||
|
|
||||||
Func_RunStartForm();
|
|
||||||
}
|
}
|
||||||
void Func_RunStartForm()
|
void Func_RunStartForm()
|
||||||
{
|
{
|
||||||
@@ -296,7 +313,6 @@ namespace Project
|
|||||||
var menu_kuntaeVisible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_workday);
|
var menu_kuntaeVisible = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_workday);
|
||||||
var menu_logdata = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_logdata);
|
var menu_logdata = FCOMMON.Util.getBit(FCOMMON.info.Login.gpermission, (int)FCOMMON.eGroupPermission.menu_logdata);
|
||||||
|
|
||||||
this.Menu_Dashboard();
|
|
||||||
}
|
}
|
||||||
void Menu_WorkTable()
|
void Menu_WorkTable()
|
||||||
{
|
{
|
||||||
@@ -1501,14 +1517,14 @@ namespace Project
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Dialog.fDashboard fdashboard = null;
|
Dialog.fDashboardNew fdashboard = null;
|
||||||
void Menu_Dashboard()
|
void Menu_Dashboard()
|
||||||
{
|
{
|
||||||
string formkey = "DASHBOARD";
|
string formkey = "DASHBOARD";
|
||||||
if (!ShowForm(formkey))
|
if (!ShowForm(formkey))
|
||||||
{
|
{
|
||||||
if (fdashboard == null || fdashboard.IsDisposed)
|
if (fdashboard == null || fdashboard.IsDisposed)
|
||||||
fdashboard = new Dialog.fDashboard();
|
fdashboard = new Dialog.fDashboardNew();
|
||||||
AddForm(formkey, fdashboard);
|
AddForm(formkey, fdashboard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -452,16 +452,16 @@
|
|||||||
<data name="toolStripButton3.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
<data name="toolStripButton3.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
<value>
|
<value>
|
||||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIHSURBVDhPY2CAgZnGrAxLtTMZluosYFim08KwWFcdLL5K
|
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIGSURBVDhPlY9PaNNgGMbfi7iD+Oei4t3NphkeJogwETxI
|
||||||
i4dhmVYhwzLteQzLdFoZlqlLwfWggKU6qxmW6fyH48U6PxiWaHoyLNO8jiK+VOsJwyotCTTNWjYgSYG1
|
E8GDKGyCXqZidUO7r+06EONhhS75vuq8DbQKWyJUBb30ItK4g1odztlpTRgM2hwmiCiDoWj6yheWugTm
|
||||||
lv/9Tub8T7lc/z/3ZMX/eXNiP9S05/9LvdzwP/Jc2X/VbV5QQ7T7UA1YpjOBf435seQr9d+Kb/X8L7nZ
|
nwceSJ73+T0kAL7Gu9aBHo2DLt4BQxyBic4OLy8KG8AQEmBEC2CIWTA6drSYgHTxHhgitjwhfoPJiARG
|
||||||
8//QzJz/jzrT/l+dlPkTJAbDJrtDTzAs0bmGasBSXcHiW911MEUdh+r/Xy+N+L+/1f7v9UKvzz3bCuEG
|
pBbIdcGBorA9BAvd/Lj5wT48UunH01UFByoZLNw8+eVy9mLzTPUq9r5O486SvDISzQcHDPH6pvt7n/XN
|
||||||
FN/qugBSj2oAAwND8c3uuTBFlZc6lp0vDDl4Odnt28Ugtf/bKpz2Fl/s+AiSK7jZ8w5dLxjAXFB0s6cE
|
KcvEppi0KE6N92N99Cy+uxH/zjPfex4ffwGT4vvggN65hdjaFb+Um1KwlurBcvaAW0vIS7SUaA0QW33D
|
||||||
xH/RGiH+uMzz/5UMrT9Psm3UCm/26hff6vpcdKvnPLpeMCi+02NcfKPrOIz/uNjT81Gpx38wLvHyAKu5
|
+8EBACCWdssvDb/NGTOJY0+rfYeWZ4+2Yylz8AmZzX3lt0sW/RxmPflfMGjRJH9fzPZsa6QlnDsn/HQu
|
||||||
2V1afKu7BUUjMii+0y0GYz+p8MqCGfCk0jsTJFZ/v54j99ZEPhRNuMCjCs++J+Ve/0H4cblXL7o8QfC4
|
dLcnLLab2OrSoE1nwqwnMk+7yAf1uf/eIJJUT8XQc1KOeR1LSxFbGwmAq0Xmta3+s5ORz/sDzvDhOM+U
|
||||||
zHMj3AulHhvQ5fGC//vrOV50RZ1+0xP3H4RfdkaeAomhq0MB/w70a/490Nv3/0DvmfsHen//P9D7HxlD
|
BaVtwB7bGIDWUj0j5Z0hGbkbQzIL3/+qRlp61PqFVOxh+P5HYVlpW1RPvPpETyH3x9HelzwL9wJqmtci
|
||||||
xc6A1ezv0UBo3DaR/e+B3vmne+r/oWvChUFq/x7omQfSy/B3f28vugJiMUgvw78DfbZ/D/Qs/3+gdxUp
|
rsnyaLLpBZP9QJPhaq9k016nTHf9Bktj612T3a6oSjMMrWXedU1a4Cy4ZcbChX81Z6Fp5ve7Jr2LJiv+
|
||||||
GKQHpBcAOYOqX5UHGvcAAAAASUVORK5CYII=
|
jznD2V8tj6pV862slAAAAABJRU5ErkJggg==
|
||||||
</value>
|
</value>
|
||||||
</data>
|
</data>
|
||||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
|||||||
Reference in New Issue
Block a user