上传文件至「/」
This commit is contained in:
@@ -0,0 +1,103 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Web;
|
||||||
|
using GThinking.N_MISPRO;
|
||||||
|
using GThinking.N_MISPROAS.Logging;
|
||||||
|
|
||||||
|
|
||||||
|
[assembly: AssemblyTitle("S_XLXSBJ")] // 部件ID,无版本号
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("江苏省金思维信息技术有限公司")]
|
||||||
|
[assembly: AssemblyProduct("金思维软件")]
|
||||||
|
[assembly: AssemblyCopyright("(c) 江苏省金思维信息技术有限公司 版权所有")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
// GUID。编号规则47544950-5333-4e45-5420-xxxxxxxxxxx。前四段固定。最后一段为部件ID字符串ASCII码,
|
||||||
|
// 不含平台版本号,最长6字节,超长需要缩写,不足长度补空格。
|
||||||
|
[assembly: Guid("47544950-5338-4E45-5420-535F4B435357")]
|
||||||
|
// 程序集版本信息
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")] // 固定为1.0.0.0
|
||||||
|
[assembly: AssemblyFileVersion("8.0.0.0")] // 部件版本
|
||||||
|
|
||||||
|
|
||||||
|
namespace GThinking.S_XLXSBJ
|
||||||
|
{
|
||||||
|
public class Mac : GThinking.N_MISPRO.Mac
|
||||||
|
{
|
||||||
|
internal static ILog MacLog;
|
||||||
|
static Mac()
|
||||||
|
{
|
||||||
|
// 注册哪些系统、跟踪到什么时间。只有当前系统的记录才保存。
|
||||||
|
MacLog = LogManager.GetMacLogger("S_XLXSBJ8", "", "2222-12-31");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mac()
|
||||||
|
{
|
||||||
|
|
||||||
|
// 以下设置该部件的有关信息
|
||||||
|
ID = "S_XLXSBJ8";
|
||||||
|
ClassID = "S_XLXSBJ";
|
||||||
|
Name = "线缆销售报价服务";
|
||||||
|
Protocol = "NET";
|
||||||
|
OACBVersion = 4;
|
||||||
|
Version = 8;
|
||||||
|
DBVersion = 2;
|
||||||
|
DbExtVersion = 0;
|
||||||
|
Vendor = "金思维信息技术有限公司";
|
||||||
|
Comment = "标准版";
|
||||||
|
|
||||||
|
// 以下设置各模块的信息
|
||||||
|
AddModuleInfo(102373000, 0, "线缆销售报价微信服务", "N", typeof(WXXLXSBJService));
|
||||||
|
AddAjaxService(102373000, "", typeof(WXXLXSBJService));
|
||||||
|
|
||||||
|
AddModuleInfo(102373001, 0, "线缆销售报价反馈数据接收服务", "N", typeof(XLXSBJFKJSService));
|
||||||
|
AddAjaxService(102373001, "", typeof(XLXSBJFKJSService));
|
||||||
|
|
||||||
|
AddModuleInfo(102373002, 0, "线缆销售价格表基础数据接收API接口服务", "S", typeof(XLXSJGBJCSJService));
|
||||||
|
AddAjaxService(102373002, "", typeof(XLXSJGBJCSJService));
|
||||||
|
|
||||||
|
AddModuleInfo(102373003, 0, "线缆销售调价表基础数据接收API接口服务", "S", typeof(XLXSTJBJCSJService));
|
||||||
|
AddAjaxService(102373003, "", typeof(XLXSTJBJCSJService));
|
||||||
|
|
||||||
|
AddModuleInfo(102373004, 0, "线缆销售调价因子基础数据接收API接口服务", "S", typeof(XLXSTJYZJCSJService));
|
||||||
|
AddAjaxService(102373004, "", typeof(XLXSTJYZJCSJService));
|
||||||
|
|
||||||
|
|
||||||
|
AddModuleInfo(102373006, 0, "AI助手解析页面服务", "S", typeof(AIZSJXService));
|
||||||
|
AddAjaxService(102373006, "", typeof(AIZSJXService));
|
||||||
|
AddModuleInfo(102373007, 0, "AI助手附件上传同步服务", "S", typeof(AIZSFJSCTBService));
|
||||||
|
AddAjaxService(102373007, "", typeof(AIZSFJSCTBService));
|
||||||
|
|
||||||
|
AddModuleInfo(102373010, 0, "AI助手附件上传异步服务", "S", typeof(AIZSFJSCYBService));
|
||||||
|
AddAjaxService(102373010, "", typeof(AIZSFJSCYBService));
|
||||||
|
|
||||||
|
AddModuleInfo(102373011, 0, "价格表导入服务", "S", typeof(JGBDRService));
|
||||||
|
AddAjaxService(102373011, "", typeof(JGBDRService));
|
||||||
|
|
||||||
|
AddModuleInfo(102373012, 0, "线缆报价深度解析导入服务", "S", typeof(XLBJJXDRService));
|
||||||
|
AddAjaxService(102373012, "", typeof(XLBJJXDRService));
|
||||||
|
|
||||||
|
AddModuleInfo(102373013, 0, "投标数据快速录入服务", "S", typeof(XLBJKSLRService));
|
||||||
|
AddAjaxService(102373013, "", typeof(XLBJKSLRService));
|
||||||
|
|
||||||
|
AddModuleInfo(102373016, 0, "线缆销售新批量调价服务", "S", typeof(BJDPLTJService));
|
||||||
|
AddAjaxService(102373016, "", typeof(BJDPLTJService));
|
||||||
|
|
||||||
|
AddModuleInfo(102373017, 0, "材料成本查询服务", "S", typeof(CLCBCXService));
|
||||||
|
AddAjaxService(102373017, "", typeof(CLCBCXService));
|
||||||
|
|
||||||
|
AddModuleInfo(102373018, 0, "报价材料清单接收服务", "S", typeof(BJCLQDJSService));
|
||||||
|
AddAjaxService(102373018, "", typeof(BJCLQDJSService));
|
||||||
|
|
||||||
|
AddModuleInfo(102373019, 0, "服务方法参数数据生成服务", "S", typeof(ServiceMethod));
|
||||||
|
AddAjaxService(102373019, "", typeof(ServiceMethod));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,943 @@
|
|||||||
|
using System;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Web;
|
||||||
|
using System.Web.Security;
|
||||||
|
using System.Web.UI;
|
||||||
|
using System.Web.UI.WebControls;
|
||||||
|
using System.Web.UI.WebControls.WebParts;
|
||||||
|
using System.Web.UI.HtmlControls;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Xml.XPath;
|
||||||
|
using GThinking.N_MISPROAS.Web.AjaxServices;
|
||||||
|
using GThinking.N_MISPRO.Data;
|
||||||
|
|
||||||
|
namespace GThinking.S_XLXSBJ
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 服务方法管理相关服务。
|
||||||
|
/// </summary>
|
||||||
|
public class ServiceMethod
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 根据传入的 DLL 文件地址,反射获取其中标记了 AjaxMethod 的服务方法信息,
|
||||||
|
/// 并写入数据库表:SRCMD、SRCMETHOD、SRCMETHODPARAMS、SRCTYPE、SRCPROP。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath">DLL 文件地址。</param>
|
||||||
|
/// <returns>操作结果。</returns>
|
||||||
|
[AjaxMethod(AuthRequried = false)]
|
||||||
|
public static Dictionary<string, object> SaveServiceMethodInfo(string filePath)
|
||||||
|
{
|
||||||
|
Dictionary<string, object> result = new Dictionary<string, object>();
|
||||||
|
TDatabase dberp = null;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
|
||||||
|
{
|
||||||
|
result["success"] = false;
|
||||||
|
result["msg"] = "文件地址不能为空或文件不存在";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dberp = TContextDatabases.GetDatabase("DBERP");
|
||||||
|
dberp.BeginTrans();
|
||||||
|
|
||||||
|
Assembly assembly = Assembly.LoadFrom(filePath);
|
||||||
|
string macId = Truncate(assembly.GetName().Name, 20);
|
||||||
|
|
||||||
|
// 先删除该部件旧数据
|
||||||
|
DeleteOldModuleData(dberp, macId);
|
||||||
|
|
||||||
|
XmlDocHelper xmlDoc = new XmlDocHelper(filePath);
|
||||||
|
int mdIndex = 0;
|
||||||
|
Dictionary<string, string> visitedTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
Type[] types = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
types = assembly.GetTypes();
|
||||||
|
}
|
||||||
|
catch (ReflectionTypeLoadException ex)
|
||||||
|
{
|
||||||
|
types = ex.Types;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (types == null)
|
||||||
|
{
|
||||||
|
dberp.CommitTrans();
|
||||||
|
result["success"] = true;
|
||||||
|
result["msg"] = "未找到可扫描的类型";
|
||||||
|
result["macId"] = macId;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Type type in types)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
continue;
|
||||||
|
if (!type.IsClass)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Dictionary<string, ServiceMethodInfo> methods = GetServiceMethods(type);
|
||||||
|
if (methods == null || methods.Count == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
mdIndex++;
|
||||||
|
double mdId = (double)mdIndex;
|
||||||
|
|
||||||
|
InsertSRCMD(dberp, macId, mdId, Truncate(type.FullName, 40));
|
||||||
|
|
||||||
|
foreach (ServiceMethodInfo smi in methods.Values)
|
||||||
|
{
|
||||||
|
string methodDesc = xmlDoc.GetMethodSummary(smi.MethodInfo);
|
||||||
|
string retDesc = xmlDoc.GetMethodReturns(smi.MethodInfo);
|
||||||
|
|
||||||
|
InsertSRCMETHOD(
|
||||||
|
dberp,
|
||||||
|
macId,
|
||||||
|
mdId,
|
||||||
|
Truncate(smi.MethodName, 32),
|
||||||
|
Truncate(GetMethodModifiers(smi.MethodInfo), 80),
|
||||||
|
smi.AuthRequried ? "Y" : "N",
|
||||||
|
smi.UseHttpGet ? "Y" : "N",
|
||||||
|
Truncate(methodDesc, 4000),
|
||||||
|
Truncate(GetTypeDisplayName(smi.ReturnType), 80),
|
||||||
|
Truncate(retDesc, 1000));
|
||||||
|
|
||||||
|
if (smi.ParameterList != null)
|
||||||
|
{
|
||||||
|
int paramXh = 0;
|
||||||
|
foreach (ServiceParameterInfo spi in smi.ParameterList)
|
||||||
|
{
|
||||||
|
string paramDesc = xmlDoc.GetParameterSummary(smi.MethodInfo, spi.Name);
|
||||||
|
|
||||||
|
InsertSRCMETHODPARAMS(
|
||||||
|
dberp,
|
||||||
|
macId,
|
||||||
|
mdId,
|
||||||
|
Truncate(smi.MethodName, 32),
|
||||||
|
(double)paramXh,
|
||||||
|
Truncate(spi.Name, 32),
|
||||||
|
Truncate(GetTypeDisplayName(spi.ParameterType), 80),
|
||||||
|
Truncate(paramDesc, 1000));
|
||||||
|
|
||||||
|
ReflectType(spi.ParameterType, dberp, visitedTypes, xmlDoc);
|
||||||
|
paramXh++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReflectType(smi.ReturnType, dberp, visitedTypes, xmlDoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dberp.CommitTrans();
|
||||||
|
result["success"] = true;
|
||||||
|
result["msg"] = "扫描完成";
|
||||||
|
result["macId"] = macId;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (dberp != null && dberp.InTransaction)
|
||||||
|
dberp.RollbackTrans();
|
||||||
|
|
||||||
|
result["success"] = false;
|
||||||
|
result["msg"] = ex.Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据 MACID 查询已扫描的服务方法数据。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="macId">服务部件ID(程序集名称)。</param>
|
||||||
|
/// <returns>包含各表数据的结果。</returns>
|
||||||
|
[AjaxMethod(AuthRequried = false)]
|
||||||
|
public static Dictionary<string, object> GetServiceMethodData(string macId)
|
||||||
|
{
|
||||||
|
Dictionary<string, object> result = new Dictionary<string, object>();
|
||||||
|
TDatabase dberp = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dberp = TContextDatabases.GetDatabase("DBERP");
|
||||||
|
|
||||||
|
result["SRCMD"] = QueryTableByMacId(dberp, "SRCMD", macId);
|
||||||
|
result["SRCMETHOD"] = QueryTableByMacId(dberp, "SRCMETHOD", macId);
|
||||||
|
result["SRCMETHODPARAMS"] = QueryTableByMacId(dberp, "SRCMETHODPARAMS", macId);
|
||||||
|
result["SRCTYPE"] = QueryAll(dberp, "SRCTYPE");
|
||||||
|
result["SRCPROP"] = QueryAll(dberp, "SRCPROP");
|
||||||
|
result["success"] = true;
|
||||||
|
result["msg"] = "";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
result["success"] = false;
|
||||||
|
result["msg"] = ex.Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 数据库操作
|
||||||
|
|
||||||
|
private static void DeleteOldModuleData(TDatabase dberp, string macId)
|
||||||
|
{
|
||||||
|
string[] tables = new string[] { "SRCMETHODPARAMS", "SRCMETHOD", "SRCMD" };
|
||||||
|
TSQL sql = new TSQL(dberp);
|
||||||
|
foreach (string table in tables)
|
||||||
|
{
|
||||||
|
sql.Clear();
|
||||||
|
sql.Add("delete from " + table + " where MACID=:pMACID");
|
||||||
|
sql.ParamByName("pMACID").Value = macId;
|
||||||
|
dberp.ExecSQL(sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeleteTypeData(TDatabase dberp, string typeFullName)
|
||||||
|
{
|
||||||
|
TSQL sql = new TSQL(dberp);
|
||||||
|
sql.Clear();
|
||||||
|
sql.Add("delete from SRCPROP where TYPEFULLNAME=:pTYPEFULLNAME");
|
||||||
|
sql.ParamByName("pTYPEFULLNAME").Value = typeFullName;
|
||||||
|
dberp.ExecSQL(sql);
|
||||||
|
|
||||||
|
sql.Clear();
|
||||||
|
sql.Add("delete from SRCTYPE where TYPEFULLNAME=:pTYPEFULLNAME");
|
||||||
|
sql.ParamByName("pTYPEFULLNAME").Value = typeFullName;
|
||||||
|
dberp.ExecSQL(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Dictionary<string, object>> QueryTableByMacId(TDatabase dberp, string tableName, string macId)
|
||||||
|
{
|
||||||
|
TSQL sql = new TSQL(dberp);
|
||||||
|
sql.Add("select " + GetTableColumns(tableName) + " from " + tableName + " where MACID=:pMACID");
|
||||||
|
sql.ParamByName("pMACID").Value = macId;
|
||||||
|
DataSet ds = dberp.OpenDataSet(sql);
|
||||||
|
return DataTableToList(ds.Tables[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Dictionary<string, object>> QueryAll(TDatabase dberp, string tableName)
|
||||||
|
{
|
||||||
|
TSQL sql = new TSQL(dberp);
|
||||||
|
sql.Add("select " + GetTableColumns(tableName) + " from " + tableName);
|
||||||
|
DataSet ds = dberp.OpenDataSet(sql);
|
||||||
|
return DataTableToList(ds.Tables[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetTableColumns(string tableName)
|
||||||
|
{
|
||||||
|
switch (tableName)
|
||||||
|
{
|
||||||
|
case "SRCMD":
|
||||||
|
return "MACID, MDID, MDNAME";
|
||||||
|
case "SRCMETHOD":
|
||||||
|
return "MACID, MDID, METHODNAME, MODIFIERS, AUTHREQUIRED, USEHTTPGET, METHODDESC, RETTYPE, RETDESC";
|
||||||
|
case "SRCMETHODPARAMS":
|
||||||
|
return "MACID, MDID, METHODNAME, PARAMXH, PARAMNAME, PARAMTYPE, PARAMDESC";
|
||||||
|
case "SRCTYPE":
|
||||||
|
return "TYPEFULLNAME, NAMESPACE, TYPENAME, TYPEDESC";
|
||||||
|
case "SRCPROP":
|
||||||
|
return "TYPEFULLNAME, PROPNAME, PROPTYPEFULLNAME, NAMESPACE, PROPTYPE, PROPDESC";
|
||||||
|
default:
|
||||||
|
return "*";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Dictionary<string, object>> DataTableToList(DataTable dt)
|
||||||
|
{
|
||||||
|
List<Dictionary<string, object>> list = new List<Dictionary<string, object>>();
|
||||||
|
foreach (DataRow row in dt.Rows)
|
||||||
|
{
|
||||||
|
Dictionary<string, object> item = new Dictionary<string, object>();
|
||||||
|
foreach (DataColumn col in dt.Columns)
|
||||||
|
{
|
||||||
|
object val = row[col];
|
||||||
|
item[col.ColumnName] = val == DBNull.Value ? null : val;
|
||||||
|
}
|
||||||
|
list.Add(item);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InsertSRCMD(TDatabase dberp, string macId, double mdId, string mdName)
|
||||||
|
{
|
||||||
|
TSQL sql = new TSQL(dberp);
|
||||||
|
sql.Clear();
|
||||||
|
sql.Add("insert into SRCMD(MACID, MDID, MDNAME) values(:pMACID, :pMDID, :pMDNAME)");
|
||||||
|
sql.ParamByName("pMACID").Value = macId;
|
||||||
|
sql.ParamByName("pMDID").Value = mdId;
|
||||||
|
sql.ParamByName("pMDNAME").Value = mdName;
|
||||||
|
dberp.ExecSQL(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InsertSRCMETHOD(TDatabase dberp, string macId, double mdId, string methodName,
|
||||||
|
string modifiers, string authRequired, string useHttpGet, string methodDesc,
|
||||||
|
string retType, string retDesc)
|
||||||
|
{
|
||||||
|
TSQL sql = new TSQL(dberp);
|
||||||
|
sql.Clear();
|
||||||
|
sql.Add("insert into SRCMETHOD(MACID, MDID, METHODNAME, MODIFIERS, AUTHREQUIRED, USEHTTPGET, METHODDESC, RETTYPE, RETDESC) ");
|
||||||
|
sql.Add("values(:pMACID, :pMDID, :pMETHODNAME, :pMODIFIERS, :pAUTHREQUIRED, :pUSEHTTPGET, :pMETHODDESC, :pRETTYPE, :pRETDESC)");
|
||||||
|
sql.ParamByName("pMACID").Value = macId;
|
||||||
|
sql.ParamByName("pMDID").Value = mdId;
|
||||||
|
sql.ParamByName("pMETHODNAME").Value = methodName;
|
||||||
|
sql.ParamByName("pMODIFIERS").Value = modifiers;
|
||||||
|
sql.ParamByName("pAUTHREQUIRED").Value = authRequired;
|
||||||
|
sql.ParamByName("pUSEHTTPGET").Value = useHttpGet;
|
||||||
|
sql.ParamByName("pMETHODDESC").Value = methodDesc;
|
||||||
|
sql.ParamByName("pRETTYPE").Value = retType;
|
||||||
|
sql.ParamByName("pRETDESC").Value = retDesc;
|
||||||
|
dberp.ExecSQL(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InsertSRCMETHODPARAMS(TDatabase dberp, string macId, double mdId, string methodName,
|
||||||
|
double paramXh, string paramName, string paramType, string paramDesc)
|
||||||
|
{
|
||||||
|
TSQL sql = new TSQL(dberp);
|
||||||
|
sql.Clear();
|
||||||
|
sql.Add("insert into SRCMETHODPARAMS(MACID, MDID, METHODNAME, PARAMXH, PARAMNAME, PARAMTYPE, PARAMDESC) ");
|
||||||
|
sql.Add("values(:pMACID, :pMDID, :pMETHODNAME, :pPARAMXH, :pPARAMNAME, :pPARAMTYPE, :pPARAMDESC)");
|
||||||
|
sql.ParamByName("pMACID").Value = macId;
|
||||||
|
sql.ParamByName("pMDID").Value = mdId;
|
||||||
|
sql.ParamByName("pMETHODNAME").Value = methodName;
|
||||||
|
sql.ParamByName("pPARAMXH").Value = paramXh;
|
||||||
|
sql.ParamByName("pPARAMNAME").Value = paramName;
|
||||||
|
sql.ParamByName("pPARAMTYPE").Value = paramType;
|
||||||
|
sql.ParamByName("pPARAMDESC").Value = paramDesc;
|
||||||
|
dberp.ExecSQL(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InsertSRCTYPE(TDatabase dberp, string typeFullName, string nameSpace,
|
||||||
|
string typeName, string typeDesc)
|
||||||
|
{
|
||||||
|
TSQL sql = new TSQL(dberp);
|
||||||
|
sql.Clear();
|
||||||
|
sql.Add("insert into SRCTYPE(TYPEFULLNAME, NAMESPACE, TYPENAME, TYPEDESC) ");
|
||||||
|
sql.Add("values(:pTYPEFULLNAME, :pNAMESPACE, :pTYPENAME, :pTYPEDESC)");
|
||||||
|
sql.ParamByName("pTYPEFULLNAME").Value = typeFullName;
|
||||||
|
sql.ParamByName("pNAMESPACE").Value = nameSpace;
|
||||||
|
sql.ParamByName("pTYPENAME").Value = typeName;
|
||||||
|
sql.ParamByName("pTYPEDESC").Value = typeDesc;
|
||||||
|
dberp.ExecSQL(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InsertSRCPROP(TDatabase dberp, string typeFullName, string propName,
|
||||||
|
string propTypeFullName, string nameSpace, string propType, string propDesc)
|
||||||
|
{
|
||||||
|
TSQL sql = new TSQL(dberp);
|
||||||
|
sql.Clear();
|
||||||
|
sql.Add("insert into SRCPROP(TYPEFULLNAME, PROPNAME, PROPTYPEFULLNAME, NAMESPACE, PROPTYPE, PROPDESC) ");
|
||||||
|
sql.Add("values(:pTYPEFULLNAME, :pPROPNAME, :pPROPTYPEFULLNAME, :pNAMESPACE, :pPROPTYPE, :pPROPDESC)");
|
||||||
|
sql.ParamByName("pTYPEFULLNAME").Value = typeFullName;
|
||||||
|
sql.ParamByName("pPROPNAME").Value = propName;
|
||||||
|
sql.ParamByName("pPROPTYPEFULLNAME").Value = propTypeFullName;
|
||||||
|
sql.ParamByName("pNAMESPACE").Value = nameSpace;
|
||||||
|
sql.ParamByName("pPROPTYPE").Value = propType;
|
||||||
|
sql.ParamByName("pPROPDESC").Value = propDesc;
|
||||||
|
dberp.ExecSQL(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 反射与类型辅助
|
||||||
|
|
||||||
|
private static Dictionary<string, ServiceMethodInfo> GetServiceMethods(Type classType)
|
||||||
|
{
|
||||||
|
if (classType == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
List<Type> typeList = new List<Type>();
|
||||||
|
Type current = classType;
|
||||||
|
typeList.Add(current);
|
||||||
|
while (current.BaseType != null)
|
||||||
|
{
|
||||||
|
current = current.BaseType;
|
||||||
|
if (current != typeof(object))
|
||||||
|
typeList.Add(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<string, ServiceMethodInfo> methods = new Dictionary<string, ServiceMethodInfo>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly |
|
||||||
|
BindingFlags.Static | BindingFlags.Instance;
|
||||||
|
|
||||||
|
for (int i = typeList.Count - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
MethodInfo[] methodInfos = typeList[i].GetMethods(flags);
|
||||||
|
foreach (MethodInfo method in methodInfos)
|
||||||
|
{
|
||||||
|
object[] attribs = method.GetCustomAttributes(typeof(AjaxMethodAttribute), true);
|
||||||
|
if (attribs.Length > 0)
|
||||||
|
{
|
||||||
|
AjaxMethodAttribute ajaxAttr = (AjaxMethodAttribute)attribs[0];
|
||||||
|
ServiceMethodInfo smi = new ServiceMethodInfo(method, ajaxAttr);
|
||||||
|
smi.LoadMethodParameters();
|
||||||
|
methods[smi.MethodName] = smi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetMethodModifiers(MethodInfo method)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (method.IsPublic)
|
||||||
|
sb.Append("public ");
|
||||||
|
else if (method.IsPrivate)
|
||||||
|
sb.Append("private ");
|
||||||
|
else if (method.IsFamily)
|
||||||
|
sb.Append("protected ");
|
||||||
|
else if (method.IsAssembly)
|
||||||
|
sb.Append("internal ");
|
||||||
|
|
||||||
|
if (method.IsStatic)
|
||||||
|
sb.Append("static ");
|
||||||
|
|
||||||
|
return sb.ToString().Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReflectType(Type type, TDatabase dberp,
|
||||||
|
Dictionary<string, string> visitedTypes, XmlDocHelper xmlDoc)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (type.IsByRef || type.IsPointer)
|
||||||
|
{
|
||||||
|
ReflectType(type.GetElementType(), dberp, visitedTypes, xmlDoc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.IsArray)
|
||||||
|
{
|
||||||
|
ReflectType(type.GetElementType(), dberp, visitedTypes, xmlDoc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type underlying = Nullable.GetUnderlyingType(type);
|
||||||
|
if (underlying != null)
|
||||||
|
{
|
||||||
|
ReflectType(underlying, dberp, visitedTypes, xmlDoc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
Type[] args = type.GetGenericArguments();
|
||||||
|
foreach (Type arg in args)
|
||||||
|
ReflectType(arg, dberp, visitedTypes, xmlDoc);
|
||||||
|
|
||||||
|
Type def = type.GetGenericTypeDefinition();
|
||||||
|
if (!IsCustomType(def))
|
||||||
|
return;
|
||||||
|
|
||||||
|
type = def;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsCustomType(type))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string key = type.FullName;
|
||||||
|
if (string.IsNullOrEmpty(key))
|
||||||
|
return;
|
||||||
|
if (visitedTypes.ContainsKey(key))
|
||||||
|
return;
|
||||||
|
visitedTypes[key] = key;
|
||||||
|
|
||||||
|
string typeFullName = type.FullName.Replace('+', '.');
|
||||||
|
|
||||||
|
// 先清理该类型旧数据,再重新写入
|
||||||
|
DeleteTypeData(dberp, typeFullName);
|
||||||
|
|
||||||
|
InsertSRCTYPE(
|
||||||
|
dberp,
|
||||||
|
Truncate(typeFullName, 80),
|
||||||
|
Truncate(type.Namespace, 80),
|
||||||
|
Truncate(GetTypeShortName(type), 80),
|
||||||
|
Truncate(xmlDoc.GetSummary(type), 1000));
|
||||||
|
|
||||||
|
List<MemberInfo> members = new List<MemberInfo>();
|
||||||
|
members.AddRange(type.GetProperties(BindingFlags.Public | BindingFlags.Instance));
|
||||||
|
members.AddRange(type.GetFields(BindingFlags.Public | BindingFlags.Instance));
|
||||||
|
members.Sort(new MemberNameComparer());
|
||||||
|
|
||||||
|
foreach (MemberInfo mi in members)
|
||||||
|
{
|
||||||
|
Type memberType = null;
|
||||||
|
if (mi is PropertyInfo)
|
||||||
|
memberType = ((PropertyInfo)mi).PropertyType;
|
||||||
|
else if (mi is FieldInfo)
|
||||||
|
memberType = ((FieldInfo)mi).FieldType;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string propTypeFullName = memberType.FullName == null ? memberType.Name : memberType.FullName.Replace('+', '.');
|
||||||
|
|
||||||
|
InsertSRCPROP(
|
||||||
|
dberp,
|
||||||
|
Truncate(typeFullName, 80),
|
||||||
|
Truncate(mi.Name, 32),
|
||||||
|
Truncate(propTypeFullName, 80),
|
||||||
|
Truncate(memberType.Namespace, 80),
|
||||||
|
Truncate(GetTypeDisplayName(memberType), 80),
|
||||||
|
Truncate(xmlDoc.GetSummary(mi), 250));
|
||||||
|
|
||||||
|
ReflectType(memberType, dberp, visitedTypes, xmlDoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsCustomType(Type type)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
return false;
|
||||||
|
if (type.IsGenericParameter)
|
||||||
|
return false;
|
||||||
|
if (type.IsPrimitive)
|
||||||
|
return false;
|
||||||
|
if (type == typeof(string) || type == typeof(object) || type == typeof(decimal)
|
||||||
|
|| type == typeof(DateTime) || type == typeof(TimeSpan) || type == typeof(Guid))
|
||||||
|
return false;
|
||||||
|
if (type.Namespace != null &&
|
||||||
|
(type.Namespace.StartsWith("System") || type.Namespace.StartsWith("Microsoft")))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetTypeDisplayName(Type type)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
if (type.IsByRef)
|
||||||
|
return GetTypeDisplayName(type.GetElementType()) + "&";
|
||||||
|
if (type.IsPointer)
|
||||||
|
return GetTypeDisplayName(type.GetElementType()) + "*";
|
||||||
|
if (type.IsArray)
|
||||||
|
return GetTypeDisplayName(type.GetElementType()) + "[]";
|
||||||
|
|
||||||
|
Type underlying = Nullable.GetUnderlyingType(type);
|
||||||
|
if (underlying != null)
|
||||||
|
return GetTypeDisplayName(underlying) + "?";
|
||||||
|
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
Type def = type.GetGenericTypeDefinition();
|
||||||
|
string baseName = def.FullName;
|
||||||
|
if (string.IsNullOrEmpty(baseName))
|
||||||
|
baseName = type.Name;
|
||||||
|
baseName = baseName.Replace('+', '.');
|
||||||
|
int backtick = baseName.IndexOf('`');
|
||||||
|
string cleanName = backtick > 0 ? baseName.Substring(0, backtick) : baseName;
|
||||||
|
|
||||||
|
Type[] genericArgs = type.GetGenericArguments();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(cleanName);
|
||||||
|
sb.Append("<");
|
||||||
|
for (int i = 0; i < genericArgs.Length; i++)
|
||||||
|
{
|
||||||
|
sb.Append(GetTypeDisplayName(genericArgs[i]));
|
||||||
|
if (i < genericArgs.Length - 1)
|
||||||
|
sb.Append(", ");
|
||||||
|
}
|
||||||
|
sb.Append(">");
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.FullName == null)
|
||||||
|
return type.Name;
|
||||||
|
return type.FullName.Replace('+', '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetTypeShortName(Type type)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
return "";
|
||||||
|
string name = type.Name;
|
||||||
|
int backtick = name.IndexOf('`');
|
||||||
|
if (backtick > 0)
|
||||||
|
name = name.Substring(0, backtick);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Truncate(string value, int maxLength)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
return string.Empty;
|
||||||
|
if (value.Length > maxLength)
|
||||||
|
return value.Substring(0, maxLength);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 嵌套辅助类
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 服务方法信息。
|
||||||
|
/// </summary>
|
||||||
|
private class ServiceMethodInfo
|
||||||
|
{
|
||||||
|
internal ServiceMethodInfo(MethodInfo methodInfo, AjaxMethodAttribute methodAttribute)
|
||||||
|
{
|
||||||
|
_methodInfo = methodInfo;
|
||||||
|
_methodName = methodInfo.Name;
|
||||||
|
_isPublic = methodInfo.IsPublic;
|
||||||
|
_isStatic = methodInfo.IsStatic;
|
||||||
|
_returnType = methodInfo.ReturnType;
|
||||||
|
_authRequried = methodAttribute.AuthRequried;
|
||||||
|
_useHttpGet = methodAttribute.UseHttpGet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodInfo _methodInfo;
|
||||||
|
internal MethodInfo MethodInfo
|
||||||
|
{
|
||||||
|
get { return _methodInfo; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _methodName;
|
||||||
|
public string MethodName
|
||||||
|
{
|
||||||
|
get { return _methodName == null ? string.Empty : _methodName; }
|
||||||
|
set { _methodName = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isPublic;
|
||||||
|
public bool IsPublic
|
||||||
|
{
|
||||||
|
get { return _isPublic; }
|
||||||
|
set { _isPublic = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isStatic;
|
||||||
|
public bool IsStatic
|
||||||
|
{
|
||||||
|
get { return _isStatic; }
|
||||||
|
set { _isStatic = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type _returnType;
|
||||||
|
public Type ReturnType
|
||||||
|
{
|
||||||
|
get { return _returnType; }
|
||||||
|
set { _returnType = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _authRequried;
|
||||||
|
public bool AuthRequried
|
||||||
|
{
|
||||||
|
get { return _authRequried; }
|
||||||
|
set { _authRequried = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _useHttpGet;
|
||||||
|
public bool UseHttpGet
|
||||||
|
{
|
||||||
|
get { return _useHttpGet; }
|
||||||
|
set { _useHttpGet = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ServiceParameterInfo> _parameterList;
|
||||||
|
public List<ServiceParameterInfo> ParameterList
|
||||||
|
{
|
||||||
|
get { return _parameterList; }
|
||||||
|
set { _parameterList = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void LoadMethodParameters()
|
||||||
|
{
|
||||||
|
if (_methodInfo == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
List<ServiceParameterInfo> list = new List<ServiceParameterInfo>();
|
||||||
|
int index = 0;
|
||||||
|
foreach (ParameterInfo param in _methodInfo.GetParameters())
|
||||||
|
{
|
||||||
|
list.Add(new ServiceParameterInfo(param, index));
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
_parameterList = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 服务方法参数信息。
|
||||||
|
/// </summary>
|
||||||
|
private class ServiceParameterInfo
|
||||||
|
{
|
||||||
|
private ParameterInfo _param;
|
||||||
|
private int _index;
|
||||||
|
|
||||||
|
internal ServiceParameterInfo(ParameterInfo param, int index)
|
||||||
|
{
|
||||||
|
_param = param;
|
||||||
|
_index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ParameterInfo ParameterInfo
|
||||||
|
{
|
||||||
|
get { return _param; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int Index
|
||||||
|
{
|
||||||
|
get { return _index; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { return _param.Name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type ParameterType
|
||||||
|
{
|
||||||
|
get { return _param.ParameterType; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// XML 文档注释读取辅助类。
|
||||||
|
/// </summary>
|
||||||
|
private class XmlDocHelper
|
||||||
|
{
|
||||||
|
private XPathDocument _doc;
|
||||||
|
private XPathNavigator _nav;
|
||||||
|
|
||||||
|
public XmlDocHelper(string dllPath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(dllPath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string fullPath = Path.GetFullPath(dllPath);
|
||||||
|
string dir = Path.GetDirectoryName(fullPath);
|
||||||
|
string xmlPath = Path.Combine(dir, Path.GetFileNameWithoutExtension(fullPath) + ".xml");
|
||||||
|
if (!File.Exists(xmlPath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_doc = new XPathDocument(xmlPath);
|
||||||
|
_nav = _doc.CreateNavigator();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_doc = null;
|
||||||
|
_nav = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetSummary(Type type)
|
||||||
|
{
|
||||||
|
if (_nav == null || type == null)
|
||||||
|
return "";
|
||||||
|
string id = GetTypeDocId(type);
|
||||||
|
return GetChildText("T", id, "summary");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetSummary(MemberInfo member)
|
||||||
|
{
|
||||||
|
if (_nav == null || member == null)
|
||||||
|
return "";
|
||||||
|
string prefix = "P";
|
||||||
|
if (member is FieldInfo)
|
||||||
|
prefix = "F";
|
||||||
|
else if (member is PropertyInfo)
|
||||||
|
prefix = "P";
|
||||||
|
else if (member is MethodInfo)
|
||||||
|
prefix = "M";
|
||||||
|
string id = GetMemberDocId(member);
|
||||||
|
return GetChildText(prefix, id, "summary");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetMethodSummary(MethodInfo method)
|
||||||
|
{
|
||||||
|
XPathNavigator node = FindMethodNode(method);
|
||||||
|
if (node == null)
|
||||||
|
return "";
|
||||||
|
XPathNavigator sum = node.SelectSingleNode("summary");
|
||||||
|
return sum == null ? "" : sum.Value.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetMethodReturns(MethodInfo method)
|
||||||
|
{
|
||||||
|
XPathNavigator node = FindMethodNode(method);
|
||||||
|
if (node == null)
|
||||||
|
return "";
|
||||||
|
XPathNavigator ret = node.SelectSingleNode("returns");
|
||||||
|
return ret == null ? "" : ret.Value.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetParameterSummary(MethodInfo method, string paramName)
|
||||||
|
{
|
||||||
|
XPathNavigator node = FindMethodNode(method);
|
||||||
|
if (node == null || string.IsNullOrEmpty(paramName))
|
||||||
|
return "";
|
||||||
|
string expr = "param[@name='" + Escape(paramName) + "']";
|
||||||
|
XPathNavigator pn = node.SelectSingleNode(expr);
|
||||||
|
return pn == null ? "" : pn.Value.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetChildText(string prefix, string id, string childName)
|
||||||
|
{
|
||||||
|
if (_nav == null)
|
||||||
|
return "";
|
||||||
|
string expr = "//member[@name='" + prefix + ":" + Escape(id) + "']";
|
||||||
|
XPathNavigator node = _nav.SelectSingleNode(expr);
|
||||||
|
if (node == null)
|
||||||
|
return "";
|
||||||
|
XPathNavigator child = node.SelectSingleNode(childName);
|
||||||
|
return child == null ? "" : child.Value.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private XPathNavigator FindMethodNode(MethodInfo method)
|
||||||
|
{
|
||||||
|
if (_nav == null || method == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
string exactId = GetMethodDocId(method);
|
||||||
|
string expr = "//member[@name='M:" + Escape(exactId) + "']";
|
||||||
|
XPathNavigator node = _nav.SelectSingleNode(expr);
|
||||||
|
if (node != null)
|
||||||
|
return node;
|
||||||
|
|
||||||
|
string typeId = GetTypeDocId(method.DeclaringType);
|
||||||
|
string prefix = "M:" + Escape(typeId) + "." + method.Name;
|
||||||
|
XPathNodeIterator iter = _nav.Select("//member[starts-with(@name,'" + prefix + "')]");
|
||||||
|
int pCount = method.GetParameters().Length;
|
||||||
|
|
||||||
|
while (iter.MoveNext())
|
||||||
|
{
|
||||||
|
string name = iter.Current.GetAttribute("name", "");
|
||||||
|
int open = name.IndexOf('(');
|
||||||
|
int close = name.LastIndexOf(')');
|
||||||
|
if (open > 0 && close > open)
|
||||||
|
{
|
||||||
|
string sig = name.Substring(open + 1, close - open - 1);
|
||||||
|
int count = string.IsNullOrEmpty(sig) ? 0 : CountChar(sig, ',') + 1;
|
||||||
|
if (count == pCount)
|
||||||
|
return iter.Current.Clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetTypeDocId(Type type)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
return "";
|
||||||
|
string fullName = type.FullName;
|
||||||
|
if (string.IsNullOrEmpty(fullName))
|
||||||
|
return type.Name;
|
||||||
|
return fullName.Replace('+', '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetMemberDocId(MemberInfo member)
|
||||||
|
{
|
||||||
|
Type type = member.DeclaringType;
|
||||||
|
string typeId = GetTypeDocId(type);
|
||||||
|
if (member is MethodInfo)
|
||||||
|
return GetMethodDocId((MethodInfo)member);
|
||||||
|
return typeId + "." + member.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetMethodDocId(MethodInfo method)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(GetTypeDocId(method.DeclaringType));
|
||||||
|
sb.Append(".");
|
||||||
|
sb.Append(method.Name);
|
||||||
|
if (method.IsGenericMethod)
|
||||||
|
sb.Append("``").Append(method.GetGenericArguments().Length);
|
||||||
|
sb.Append("(");
|
||||||
|
ParameterInfo[] ps = method.GetParameters();
|
||||||
|
for (int i = 0; i < ps.Length; i++)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
sb.Append(",");
|
||||||
|
sb.Append(GetParameterDocTypeName(ps[i].ParameterType));
|
||||||
|
}
|
||||||
|
sb.Append(")");
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetParameterDocTypeName(Type type)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
return "";
|
||||||
|
if (type.IsGenericParameter)
|
||||||
|
return type.Name;
|
||||||
|
if (type.IsByRef)
|
||||||
|
return GetParameterDocTypeName(type.GetElementType()) + "@";
|
||||||
|
if (type.IsPointer)
|
||||||
|
return GetParameterDocTypeName(type.GetElementType()) + "*";
|
||||||
|
if (type.IsArray)
|
||||||
|
return GetParameterDocTypeName(type.GetElementType()) + "[]";
|
||||||
|
|
||||||
|
Type underlying = Nullable.GetUnderlyingType(type);
|
||||||
|
if (underlying != null)
|
||||||
|
return GetParameterDocTypeName(underlying) + "?";
|
||||||
|
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
Type def = type.GetGenericTypeDefinition();
|
||||||
|
string baseName = def.FullName;
|
||||||
|
if (string.IsNullOrEmpty(baseName))
|
||||||
|
baseName = type.Name;
|
||||||
|
baseName = baseName.Replace('+', '.');
|
||||||
|
Type[] args = type.GetGenericArguments();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(baseName);
|
||||||
|
sb.Append("{");
|
||||||
|
for (int i = 0; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
sb.Append(",");
|
||||||
|
sb.Append(GetParameterDocTypeName(args[i]));
|
||||||
|
}
|
||||||
|
sb.Append("}");
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
string name = type.FullName;
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
name = type.Name;
|
||||||
|
return name.Replace('+', '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Escape(string s)
|
||||||
|
{
|
||||||
|
if (s == null)
|
||||||
|
return "";
|
||||||
|
return s.Replace("'", "''");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CountChar(string s, char c)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < s.Length; i++)
|
||||||
|
{
|
||||||
|
if (s[i] == c)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按成员名称排序的比较器。
|
||||||
|
/// </summary>
|
||||||
|
private class MemberNameComparer : IComparer<MemberInfo>
|
||||||
|
{
|
||||||
|
public int Compare(MemberInfo x, MemberInfo y)
|
||||||
|
{
|
||||||
|
return string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- 服务方法管理数据表建表脚本(Oracle)
|
||||||
|
-- 数据来源:服务方法管理数据表.docx
|
||||||
|
-- 注意:原文档中参数表名为 SRCPARAMS,已按最新要求改为 SRCMETHODPARAMS
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 1. 服务模块表:记录被扫描 DLL 中的类(AjaxMethod 方法所在的类)
|
||||||
|
CREATE TABLE SRCMD (
|
||||||
|
MACID CHAR(20) NOT NULL, -- 服务部件ID(程序集名称)
|
||||||
|
MDID FLOAT NOT NULL, -- 服务模块号(类序号)
|
||||||
|
MDNAME CHAR(40) NULL, -- 服务模块名称(类全名)
|
||||||
|
CONSTRAINT PK_SRCMD PRIMARY KEY (MACID, MDID)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE SRCMD IS '代码扫描服务模块';
|
||||||
|
COMMENT ON COLUMN SRCMD.MACID IS '服务部件ID';
|
||||||
|
COMMENT ON COLUMN SRCMD.MDID IS '服务模块号';
|
||||||
|
COMMENT ON COLUMN SRCMD.MDNAME IS '服务模块名称';
|
||||||
|
|
||||||
|
-- 2. 服务方法表:记录类中标记了 AjaxMethod 的方法
|
||||||
|
CREATE TABLE SRCMETHOD (
|
||||||
|
MACID CHAR(20) NOT NULL, -- 服务部件ID
|
||||||
|
MDID FLOAT NOT NULL, -- 服务模块号
|
||||||
|
METHODNAME CHAR(32) NOT NULL, -- 服务方法名
|
||||||
|
MODIFIERS VARCHAR2(80) NULL, -- 方法修饰符(例如 public static)
|
||||||
|
AUTHREQUIRED CHAR(1) NULL, -- 是否需要登录才能调用(Y/N)
|
||||||
|
USEHTTPGET CHAR(1) NULL, -- 是否支持 Http GET 调用(Y/N)
|
||||||
|
METHODDESC VARCHAR2(4000) NULL, -- 服务方法功能描述(<summary/>)
|
||||||
|
RETTYPE VARCHAR2(80) NULL, -- 返回值类型(包含命名空间)
|
||||||
|
RETDESC VARCHAR2(1000) NULL, -- 返回值描述(<returns/>)
|
||||||
|
CONSTRAINT PK_SRCMETHOD PRIMARY KEY (MACID, MDID, METHODNAME)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE SRCMETHOD IS '代码扫描服务方法';
|
||||||
|
COMMENT ON COLUMN SRCMETHOD.MACID IS '服务部件ID';
|
||||||
|
COMMENT ON COLUMN SRCMETHOD.MDID IS '服务模块号';
|
||||||
|
COMMENT ON COLUMN SRCMETHOD.METHODNAME IS '服务方法名';
|
||||||
|
COMMENT ON COLUMN SRCMETHOD.MODIFIERS IS '方法修饰符';
|
||||||
|
COMMENT ON COLUMN SRCMETHOD.AUTHREQUIRED IS '是否需要登录才能调用';
|
||||||
|
COMMENT ON COLUMN SRCMETHOD.USEHTTPGET IS '是否支持Http GET调用';
|
||||||
|
COMMENT ON COLUMN SRCMETHOD.METHODDESC IS '方法功能描述';
|
||||||
|
COMMENT ON COLUMN SRCMETHOD.RETTYPE IS '返回值类型';
|
||||||
|
COMMENT ON COLUMN SRCMETHOD.RETDESC IS '返回值描述';
|
||||||
|
|
||||||
|
-- 3. 服务方法参数表:记录每个方法的参数
|
||||||
|
CREATE TABLE SRCMETHODPARAMS (
|
||||||
|
MACID CHAR(20) NOT NULL, -- 服务部件ID
|
||||||
|
MDID FLOAT NOT NULL, -- 服务模块号
|
||||||
|
METHODNAME CHAR(32) NOT NULL, -- 服务方法名
|
||||||
|
PARAMXH FLOAT NOT NULL, -- 参数序号
|
||||||
|
PARAMNAME CHAR(32) NULL, -- 参数名
|
||||||
|
PARAMTYPE VARCHAR2(80) NULL, -- 参数类型(包含命名空间)
|
||||||
|
PARAMDESC VARCHAR2(1000) NULL, -- 参数描述(<param/>)
|
||||||
|
CONSTRAINT PK_SRCMETHODPARAMS PRIMARY KEY (MACID, MDID, METHODNAME, PARAMXH)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE SRCMETHODPARAMS IS '代码扫描服务方法参数';
|
||||||
|
COMMENT ON COLUMN SRCMETHODPARAMS.MACID IS '服务部件ID';
|
||||||
|
COMMENT ON COLUMN SRCMETHODPARAMS.MDID IS '服务模块号';
|
||||||
|
COMMENT ON COLUMN SRCMETHODPARAMS.METHODNAME IS '服务方法名';
|
||||||
|
COMMENT ON COLUMN SRCMETHODPARAMS.PARAMXH IS '参数序号';
|
||||||
|
COMMENT ON COLUMN SRCMETHODPARAMS.PARAMNAME IS '参数名';
|
||||||
|
COMMENT ON COLUMN SRCMETHODPARAMS.PARAMTYPE IS '参数类型';
|
||||||
|
COMMENT ON COLUMN SRCMETHODPARAMS.PARAMDESC IS '参数描述';
|
||||||
|
|
||||||
|
-- 4. 数据类型表:记录自定义类型(参数/返回值中用到的自定义类)
|
||||||
|
CREATE TABLE SRCTYPE (
|
||||||
|
TYPEFULLNAME VARCHAR2(80) NOT NULL, -- 类型全称,例如 GThinking.S_XSDD.SOXXData
|
||||||
|
NAMESPACE VARCHAR2(80) NULL, -- 命名空间
|
||||||
|
TYPENAME VARCHAR2(80) NULL, -- 显示名称,例如 SOXXData
|
||||||
|
TYPEDESC VARCHAR2(1000) NULL, -- 类型描述(<summary/>)
|
||||||
|
CONSTRAINT PK_SRCTYPE PRIMARY KEY (TYPEFULLNAME)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE SRCTYPE IS '代码扫描数据类型';
|
||||||
|
COMMENT ON COLUMN SRCTYPE.TYPEFULLNAME IS '类型全称';
|
||||||
|
COMMENT ON COLUMN SRCTYPE.NAMESPACE IS '命名空间';
|
||||||
|
COMMENT ON COLUMN SRCTYPE.TYPENAME IS '显示名称';
|
||||||
|
COMMENT ON COLUMN SRCTYPE.TYPEDESC IS '类型描述';
|
||||||
|
|
||||||
|
-- 5. 数据类型属性表:记录自定义类型的公共属性/字段
|
||||||
|
CREATE TABLE SRCPROP (
|
||||||
|
TYPEFULLNAME VARCHAR2(80) NOT NULL, -- 类型全称
|
||||||
|
PROPNAME CHAR(32) NOT NULL, -- 属性名称
|
||||||
|
PROPTYPEFULLNAME VARCHAR2(80) NOT NULL, -- 属性类型全称
|
||||||
|
NAMESPACE VARCHAR2(80) NULL, -- 属性类型命名空间
|
||||||
|
PROPTYPE VARCHAR2(80) NULL, -- 属性类型显示名称
|
||||||
|
PROPDESC VARCHAR2(250) NULL, -- 属性描述(<summary/>)
|
||||||
|
CONSTRAINT PK_SRCPROP PRIMARY KEY (TYPEFULLNAME, PROPNAME, PROPTYPEFULLNAME)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE SRCPROP IS '代码扫描数据类型属性';
|
||||||
|
COMMENT ON COLUMN SRCPROP.TYPEFULLNAME IS '类型全称';
|
||||||
|
COMMENT ON COLUMN SRCPROP.PROPNAME IS '属性名称';
|
||||||
|
COMMENT ON COLUMN SRCPROP.PROPTYPEFULLNAME IS '属性类型全称';
|
||||||
|
COMMENT ON COLUMN SRCPROP.NAMESPACE IS '属性类型命名空间';
|
||||||
|
COMMENT ON COLUMN SRCPROP.PROPTYPE IS '属性类型';
|
||||||
|
COMMENT ON COLUMN SRCPROP.PROPDESC IS '属性描述';
|
||||||
|
|
||||||
|
-- 可选索引:加速按 MACID 查询
|
||||||
|
CREATE INDEX IDX_SRCMETHOD_MACID ON SRCMETHOD(MACID);
|
||||||
|
CREATE INDEX IDX_SRCMETHODPARAMS_MACID ON SRCMETHODPARAMS(MACID);
|
||||||
|
CREATE INDEX IDX_SRCPROP_TYPEFULLNAME ON SRCPROP(TYPEFULLNAME);
|
||||||
@@ -0,0 +1,808 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>服务方法管理 - DLL 扫描浏览</title>
|
||||||
|
<!-- Vue 2 -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js"></script>
|
||||||
|
<!-- Element UI CSS -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-ui@2.15.13/lib/theme-chalk/index.css">
|
||||||
|
<!-- Element UI JS -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/element-ui@2.15.13/lib/index.js"></script>
|
||||||
|
<!-- Axios -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/axios@0.27.2/dist/axios.min.js"></script>
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: #f0f2f5;
|
||||||
|
font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
.page-container {
|
||||||
|
max-width: 1600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.top-bar {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0,0,0,.06);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.page-title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
.scan-form {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.scan-form .el-input {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 280px;
|
||||||
|
}
|
||||||
|
.scan-form .el-dropdown {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.el-dropdown-menu__item {
|
||||||
|
max-width: 500px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.stats-bar {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.stat-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0,0,0,.04);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.stat-label {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.stat-value {
|
||||||
|
color: #409eff;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.filter-bar {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.main-layout {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.left-panel {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.module-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.module-title i {
|
||||||
|
color: #e6a23c;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.type-badge {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #409eff;
|
||||||
|
font-family: Consolas, monospace;
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-style: dashed;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.type-badge:hover {
|
||||||
|
color: #66b1ff;
|
||||||
|
background: #ecf5ff;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.type-badge.system {
|
||||||
|
color: #606266;
|
||||||
|
cursor: default;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.type-badge.system:hover {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.type-badge.active {
|
||||||
|
background: #409eff;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 0 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.param-desc {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.empty-tip {
|
||||||
|
text-align: center;
|
||||||
|
padding: 60px 0;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
.type-dialog .el-dialog__header {
|
||||||
|
background: #409eff;
|
||||||
|
padding: 16px 20px;
|
||||||
|
}
|
||||||
|
.type-dialog .el-dialog__title {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
word-break: break-all;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.type-dialog .el-dialog__headerbtn .el-dialog__close {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.type-dialog .el-dialog__body {
|
||||||
|
padding: 20px;
|
||||||
|
max-height: 70vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.dialog-namespace {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.dialog-type-desc {
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.8;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
}
|
||||||
|
.dialog-section-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.dialog-prop-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.dialog-prop-item {
|
||||||
|
padding: 14px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
.dialog-prop-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.dialog-prop-name {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
font-family: Consolas, monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
word-break: break-all;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.dialog-prop-type {
|
||||||
|
color: #409eff;
|
||||||
|
font-family: Consolas, monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
word-break: break-all;
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.dialog-prop-type:hover {
|
||||||
|
color: #66b1ff;
|
||||||
|
}
|
||||||
|
.dialog-prop-type.system {
|
||||||
|
color: #909399;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.dialog-prop-desc {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.dialog-empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 0;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.type-dialog {
|
||||||
|
width: 92% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.module-empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 30px 0;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
.type-table-card {
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0,0,0,.04);
|
||||||
|
}
|
||||||
|
.modules-panel {
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0,0,0,.04);
|
||||||
|
}
|
||||||
|
.modules-panel .el-card__header {
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: #fafbfc;
|
||||||
|
}
|
||||||
|
.module-collapse {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.module-collapse .el-collapse-item__header {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
padding-left: 4px;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
.module-collapse .el-collapse-item__content {
|
||||||
|
padding: 0 0 16px 0;
|
||||||
|
}
|
||||||
|
.module-collapse-title {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.module-collapse-title span:first-child {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.method-table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.method-table .el-table__expanded-cell {
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: #fafbfc;
|
||||||
|
}
|
||||||
|
.expand-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
}
|
||||||
|
.expand-title {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.expand-params {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
gap: 8px 16px;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
.expand-params .param-name {
|
||||||
|
color: #303133;
|
||||||
|
font-family: Consolas, monospace;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.expand-params .param-type-desc {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.expand-params .param-desc {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.left-panel {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.type-badge {
|
||||||
|
word-break: break-all;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app" v-cloak>
|
||||||
|
<div class="page-container">
|
||||||
|
<div class="top-bar">
|
||||||
|
<h1 class="page-title">服务方法管理 — DLL 扫描浏览</h1>
|
||||||
|
<div class="scan-form">
|
||||||
|
<el-input
|
||||||
|
v-model="filePath"
|
||||||
|
placeholder="请输入 DLL 文件绝对路径,例如 D:\\GTHINKING\\Bin\\S_XLXSBJA.dll"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="scanDll">
|
||||||
|
</el-input>
|
||||||
|
<el-dropdown v-if="recentPaths.length" size="small" split-button type="info" @command="selectRecentPath" @click="filePath = recentPaths[0]">
|
||||||
|
<i class="el-icon-time"></i> 最近路径
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item v-for="(p, idx) in recentPaths" :key="idx" :command="p" :title="p">{{ p }}</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided command="__clear__">清空记录</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
<el-button type="primary" icon="el-icon-search" :loading="scanning" @click="scanDll">
|
||||||
|
扫描 DLL
|
||||||
|
</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" :loading="loading" @click="loadData" :disabled="!macId">
|
||||||
|
刷新数据
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="macId" class="stats-bar">
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="stat-label">服务部件</span>
|
||||||
|
<span class="stat-value" style="font-size: 16px;">{{ macId }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="stat-label">模块数</span>
|
||||||
|
<span class="stat-value">{{ srcmd.length }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="stat-label">方法数</span>
|
||||||
|
<span class="stat-value">{{ srcmethod.length }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="stat-label">参数数</span>
|
||||||
|
<span class="stat-value">{{ srcmethodparams.length }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="stat-label">自定义类型</span>
|
||||||
|
<span class="stat-value">{{ srctype.length }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="macId" class="filter-bar">
|
||||||
|
<el-input
|
||||||
|
v-model="filterText"
|
||||||
|
placeholder="搜索方法名 / 参数名 / 类型名"
|
||||||
|
prefix-icon="el-icon-search"
|
||||||
|
clearable
|
||||||
|
style="width: 260px;">
|
||||||
|
</el-input>
|
||||||
|
<el-select
|
||||||
|
v-if="viewMode === 'module'"
|
||||||
|
v-model="jumpModule"
|
||||||
|
placeholder="快速跳转模块"
|
||||||
|
size="small"
|
||||||
|
clearable
|
||||||
|
style="width: 220px;"
|
||||||
|
@change="jumpToModule">
|
||||||
|
<el-option
|
||||||
|
v-for="md in srcmd"
|
||||||
|
:key="md.MDID"
|
||||||
|
:label="md.MDNAME"
|
||||||
|
:value="md.MDID">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
<template v-if="viewMode === 'module'">
|
||||||
|
<el-button size="small" icon="el-icon-arrow-down" @click="expandAllModules">全部展开</el-button>
|
||||||
|
<el-button size="small" icon="el-icon-arrow-up" @click="collapseAllModules">全部收起</el-button>
|
||||||
|
</template>
|
||||||
|
<el-button size="small" icon="el-icon-close" @click="clearFilter">清空筛选</el-button>
|
||||||
|
<el-radio-group v-model="viewMode" size="small">
|
||||||
|
<el-radio-button label="module">按模块浏览</el-radio-button>
|
||||||
|
<el-radio-button label="type">按类型浏览</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
<el-tag v-if="viewMode === 'module' && filterText" type="info" size="small">{{ filteredMethodCount }} 个方法</el-tag>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="macId" class="main-layout">
|
||||||
|
<!-- 左侧:模块/方法列表 -->
|
||||||
|
<div class="left-panel">
|
||||||
|
<!-- 按模块视图 -->
|
||||||
|
<template v-if="viewMode === 'module'">
|
||||||
|
<el-card class="modules-panel" shadow="never">
|
||||||
|
<div slot="header" class="module-title">
|
||||||
|
<i class="el-icon-s-operation" style="color: #409eff;"></i>
|
||||||
|
<span>模块与方法</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="filteredModules.length === 0" class="module-empty">没有匹配的数据</div>
|
||||||
|
<el-collapse v-else v-model="expandedModules" class="module-collapse">
|
||||||
|
<el-collapse-item v-for="md in filteredModules" :key="md.MDID" :name="md.MDID" :data-module-id="md.MDID">
|
||||||
|
<template slot="title">
|
||||||
|
<div class="module-collapse-title">
|
||||||
|
<span :title="md.MDNAME">{{ md.MDNAME }}</span>
|
||||||
|
<el-tag size="mini" type="info">{{ md.methods.length }} 个方法</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-if="md.methods.length === 0" class="module-empty">该模块下没有 AjaxMethod 方法</div>
|
||||||
|
<el-table
|
||||||
|
v-else
|
||||||
|
:data="md.methods"
|
||||||
|
class="method-table"
|
||||||
|
size="small"
|
||||||
|
stripe
|
||||||
|
:header-cell-style="{ background: '#f5f7fa', color: '#606266', fontWeight: 600 }">
|
||||||
|
<el-table-column type="expand" width="40">
|
||||||
|
<template slot-scope="props">
|
||||||
|
<div class="expand-header">
|
||||||
|
<span class="expand-title">参数明细</span>
|
||||||
|
<el-button type="text" size="mini" icon="el-icon-document-copy" @click="copyMethodSignature(props.row)">复制签名</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="expand-params">
|
||||||
|
<template v-for="p in props.row.params">
|
||||||
|
<span class="param-name">{{ p.PARAMNAME }}</span>
|
||||||
|
<div class="param-type-desc">
|
||||||
|
<span
|
||||||
|
:class="['type-badge', { system: isSystemType(p.PARAMTYPE), active: isActiveType(p.PARAMTYPE) }]"
|
||||||
|
@click.stop="showType(p.PARAMTYPE)">
|
||||||
|
{{ p.PARAMTYPE }}
|
||||||
|
</span>
|
||||||
|
<span v-if="p.PARAMDESC" class="param-desc">{{ p.PARAMDESC }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-if="props.row.params.length === 0">
|
||||||
|
<span class="param-name"></span>
|
||||||
|
<span class="param-desc">无参数</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="METHODNAME" label="方法名" min-width="140"></el-table-column>
|
||||||
|
<el-table-column prop="MODIFIERS" label="修饰符" min-width="100"></el-table-column>
|
||||||
|
<el-table-column prop="AUTHREQUIRED" label="需登录" width="70" align="center"></el-table-column>
|
||||||
|
<el-table-column prop="USEHTTPGET" label="支持GET" width="80" align="center"></el-table-column>
|
||||||
|
<el-table-column label="返回类型" min-width="220">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span
|
||||||
|
:class="['type-badge', { system: isSystemType(scope.row.RETTYPE), active: isActiveType(scope.row.RETTYPE) }]"
|
||||||
|
@click.stop="showType(scope.row.RETTYPE)">
|
||||||
|
{{ scope.row.RETTYPE || 'void' }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="RETDESC" label="返回值描述" min-width="160" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="METHODDESC" label="方法描述" min-width="200" show-overflow-tooltip></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 按类型视图 -->
|
||||||
|
<template v-if="viewMode === 'type'">
|
||||||
|
<el-card class="type-table-card" shadow="never">
|
||||||
|
<div slot="header" class="module-title">
|
||||||
|
<i class="el-icon-coin" style="color: #409eff;"></i>
|
||||||
|
<span>自定义类型一览</span>
|
||||||
|
</div>
|
||||||
|
<el-table :data="filteredTypes" style="width: 100%" size="small" stripe>
|
||||||
|
<el-table-column label="类型全称" min-width="260">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-link type="primary" @click="showType(scope.row.TYPEFULLNAME)">{{ scope.row.TYPEFULLNAME }}</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="NAMESPACE" label="命名空间" min-width="180"></el-table-column>
|
||||||
|
<el-table-column prop="TYPEDESC" label="描述" show-overflow-tooltip></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 类型定义弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
:title="currentTypeFullName || '类型定义'"
|
||||||
|
width="70%"
|
||||||
|
:close-on-click-modal="true"
|
||||||
|
custom-class="type-dialog">
|
||||||
|
<div v-if="currentTypeNamespace" class="dialog-namespace">{{ currentTypeNamespace }}</div>
|
||||||
|
<div v-if="currentTypeDesc" class="dialog-type-desc">{{ currentTypeDesc }}</div>
|
||||||
|
<div class="dialog-section-title">公共属性 / 字段({{ currentTypeProps.length }})</div>
|
||||||
|
<div v-if="currentTypeProps.length === 0" class="dialog-empty">该类型没有公共属性/字段</div>
|
||||||
|
<div v-else class="dialog-prop-list">
|
||||||
|
<div v-for="prop in currentTypeProps" :key="prop.PROPNAME + '_' + prop.PROPTYPEFULLNAME" class="dialog-prop-item">
|
||||||
|
<div class="dialog-prop-header">
|
||||||
|
<span class="dialog-prop-name">{{ prop.PROPNAME }}</span>
|
||||||
|
<span
|
||||||
|
:class="['dialog-prop-type', { system: isSystemType(prop.PROPTYPEFULLNAME) }]"
|
||||||
|
@click="showType(prop.PROPTYPEFULLNAME)">
|
||||||
|
{{ prop.PROPTYPE || prop.PROPTYPEFULLNAME }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="prop.PROPDESC" class="dialog-prop-desc">{{ prop.PROPDESC }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<div v-if="!macId && !scanning && !loading" class="empty-tip">
|
||||||
|
<i class="el-icon-upload" style="font-size: 56px; color: #dcdfe6; display: block; margin-bottom: 16px;"></i>
|
||||||
|
<div>输入 DLL 文件路径并点击“扫描 DLL”开始解析服务方法</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
new Vue({
|
||||||
|
el: '#app',
|
||||||
|
data: {
|
||||||
|
// 服务调用基地址,按项目实际部署调整
|
||||||
|
baseUrl: 'http://127.0.0.1/GTHINKING/AjaxService/S_XLXSBJ/102373019.ashx',
|
||||||
|
filePath: '',
|
||||||
|
scanning: false,
|
||||||
|
loading: false,
|
||||||
|
macId: '',
|
||||||
|
srcmd: [],
|
||||||
|
srcmethod: [],
|
||||||
|
srcmethodparams: [],
|
||||||
|
srctype: [],
|
||||||
|
srcprop: [],
|
||||||
|
filterText: '',
|
||||||
|
viewMode: 'module',
|
||||||
|
expandedModules: [],
|
||||||
|
currentTypeFullName: '',
|
||||||
|
dialogVisible: false,
|
||||||
|
recentPaths: [],
|
||||||
|
jumpModule: '',
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
currentTypeNamespace() {
|
||||||
|
const t = this.srctype.find(x => x.TYPEFULLNAME === this.currentTypeFullName);
|
||||||
|
return t && t.NAMESPACE ? t.NAMESPACE : '';
|
||||||
|
},
|
||||||
|
currentTypeDesc() {
|
||||||
|
const t = this.srctype.find(x => x.TYPEFULLNAME === this.currentTypeFullName);
|
||||||
|
return t && t.TYPEDESC ? t.TYPEDESC : '';
|
||||||
|
},
|
||||||
|
currentTypeProps() {
|
||||||
|
return this.srcprop.filter(x => x.TYPEFULLNAME === this.currentTypeFullName);
|
||||||
|
},
|
||||||
|
filteredModules() {
|
||||||
|
const text = (this.filterText || '').toLowerCase();
|
||||||
|
return this.srcmd.map(md => {
|
||||||
|
const methods = this.srcmethod
|
||||||
|
.filter(m => m.MACID === md.MACID && m.MDID === md.MDID)
|
||||||
|
.map(m => {
|
||||||
|
const params = this.srcmethodparams.filter(p =>
|
||||||
|
p.MACID === m.MACID && p.MDID === m.MDID && p.METHODNAME === m.METHODNAME
|
||||||
|
);
|
||||||
|
return { ...m, params };
|
||||||
|
});
|
||||||
|
return { ...md, methods };
|
||||||
|
}).filter(md => {
|
||||||
|
if (!text) return true;
|
||||||
|
return md.methods.some(m =>
|
||||||
|
(m.METHODNAME || '').toLowerCase().includes(text) ||
|
||||||
|
m.params.some(p =>
|
||||||
|
(p.PARAMNAME || '').toLowerCase().includes(text) ||
|
||||||
|
(p.PARAMTYPE || '').toLowerCase().includes(text)
|
||||||
|
) ||
|
||||||
|
(m.RETTYPE || '').toLowerCase().includes(text)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
filteredTypes() {
|
||||||
|
const text = (this.filterText || '').toLowerCase();
|
||||||
|
if (!text) return this.srctype;
|
||||||
|
return this.srctype.filter(t =>
|
||||||
|
(t.TYPEFULLNAME || '').toLowerCase().includes(text) ||
|
||||||
|
(t.TYPENAME || '').toLowerCase().includes(text) ||
|
||||||
|
(t.NAMESPACE || '').toLowerCase().includes(text)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
filteredMethodCount() {
|
||||||
|
return this.filteredModules.reduce((sum, md) => sum + md.methods.length, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isSystemType(typeName) {
|
||||||
|
if (!typeName) return true;
|
||||||
|
const lower = typeName.toLowerCase();
|
||||||
|
if (lower.startsWith('system.')) return true;
|
||||||
|
if (lower.startsWith('microsoft.')) return true;
|
||||||
|
if (['string', 'int', 'bool', 'long', 'double', 'float', 'decimal', 'datetime', 'object', 'void', 'byte', 'char', 'short', 'uint', 'ulong', 'ushort'].includes(lower)) return true;
|
||||||
|
if (lower.endsWith('[]')) return this.isSystemType(lower.replace('[]', ''));
|
||||||
|
if (lower.endsWith('?')) return this.isSystemType(lower.replace('?', ''));
|
||||||
|
if (lower.includes('<')) {
|
||||||
|
const base = lower.split('<')[0];
|
||||||
|
const generic = ['list', 'ienumerable', 'icollection', 'dictionary', 'nullable'];
|
||||||
|
if (generic.some(g => base.includes(g))) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
isActiveType(typeName) {
|
||||||
|
if (!typeName || this.isSystemType(typeName)) return false;
|
||||||
|
let base = typeName;
|
||||||
|
if (base.endsWith('[]')) base = base.replace('[]', '');
|
||||||
|
if (base.endsWith('?')) base = base.replace('?', '');
|
||||||
|
if (base.indexOf('<') > 0) base = base.substring(0, base.indexOf('<'));
|
||||||
|
return base.trim() === this.currentTypeFullName;
|
||||||
|
},
|
||||||
|
callMethod(methodName, params) {
|
||||||
|
const url = this.baseUrl + '/' + methodName;
|
||||||
|
return axios.post(url, params, {
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
}).then(res => {
|
||||||
|
const data = res.data;
|
||||||
|
if (data && data.success === false) {
|
||||||
|
throw new Error(data.msg || '服务调用失败');
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
scanDll() {
|
||||||
|
if (!this.filePath) {
|
||||||
|
this.$message.warning('请输入 DLL 文件路径');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.scanning = true;
|
||||||
|
this.callMethod('SaveServiceMethodInfo', { filePath: this.filePath })
|
||||||
|
.then(data => {
|
||||||
|
this.$message.success(data.msg || '扫描完成');
|
||||||
|
this.macId = data.macId || '';
|
||||||
|
this.currentTypeFullName = '';
|
||||||
|
this.saveRecentPath(this.filePath);
|
||||||
|
return this.loadData();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.$message.error(err.message || '扫描失败');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.scanning = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadData() {
|
||||||
|
if (!this.macId) {
|
||||||
|
this.$message.warning('请先扫描 DLL');
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
this.loading = true;
|
||||||
|
return this.callMethod('GetServiceMethodData', { macId: this.macId })
|
||||||
|
.then(data => {
|
||||||
|
this.srcmd = data.SRCMD || [];
|
||||||
|
this.srcmethod = data.SRCMETHOD || [];
|
||||||
|
this.srcmethodparams = data.SRCMETHODPARAMS || [];
|
||||||
|
this.srctype = data.SRCTYPE || [];
|
||||||
|
this.srcprop = data.SRCPROP || [];
|
||||||
|
this.expandedModules = (data.SRCMD || []).map(md => md.MDID);
|
||||||
|
this.currentTypeFullName = '';
|
||||||
|
this.$message.success('数据加载完成');
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.$message.error(err.message || '加载数据失败');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showType(typeName) {
|
||||||
|
if (!typeName) return;
|
||||||
|
if (this.isSystemType(typeName)) return;
|
||||||
|
let base = typeName;
|
||||||
|
if (base.endsWith('[]')) base = base.replace('[]', '');
|
||||||
|
if (base.endsWith('?')) base = base.replace('?', '');
|
||||||
|
if (base.indexOf('<') > 0) base = base.substring(0, base.indexOf('<'));
|
||||||
|
base = base.trim();
|
||||||
|
if (!this.srctype.some(t => t.TYPEFULLNAME === base)) {
|
||||||
|
this.$message.info('该类型未在扫描结果中找到,可能为系统类型或未被引用');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.currentTypeFullName = base;
|
||||||
|
this.dialogVisible = true;
|
||||||
|
},
|
||||||
|
loadRecentPaths() {
|
||||||
|
try {
|
||||||
|
const list = JSON.parse(localStorage.getItem('svcMethodRecentPaths') || '[]');
|
||||||
|
this.recentPaths = Array.isArray(list) ? list : [];
|
||||||
|
} catch (e) {
|
||||||
|
this.recentPaths = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveRecentPath(path) {
|
||||||
|
if (!path) return;
|
||||||
|
let list = this.recentPaths.filter(p => p !== path);
|
||||||
|
list.unshift(path);
|
||||||
|
if (list.length > 10) list = list.slice(0, 10);
|
||||||
|
this.recentPaths = list;
|
||||||
|
try {
|
||||||
|
localStorage.setItem('svcMethodRecentPaths', JSON.stringify(list));
|
||||||
|
} catch (e) {}
|
||||||
|
},
|
||||||
|
selectRecentPath(path) {
|
||||||
|
if (path === '__clear__') {
|
||||||
|
this.clearRecentPaths();
|
||||||
|
this.$message.success('已清空最近路径');
|
||||||
|
} else {
|
||||||
|
this.filePath = path;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearRecentPaths() {
|
||||||
|
this.recentPaths = [];
|
||||||
|
try {
|
||||||
|
localStorage.removeItem('svcMethodRecentPaths');
|
||||||
|
} catch (e) {}
|
||||||
|
},
|
||||||
|
expandAllModules() {
|
||||||
|
this.expandedModules = this.srcmd.map(md => md.MDID);
|
||||||
|
},
|
||||||
|
collapseAllModules() {
|
||||||
|
this.expandedModules = [];
|
||||||
|
},
|
||||||
|
jumpToModule() {
|
||||||
|
if (!this.jumpModule) return;
|
||||||
|
const mdId = this.jumpModule;
|
||||||
|
if (!this.expandedModules.includes(mdId)) {
|
||||||
|
this.expandedModules.push(mdId);
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const el = document.querySelector('[data-module-id="' + mdId + '"]');
|
||||||
|
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
clearFilter() {
|
||||||
|
this.filterText = '';
|
||||||
|
this.jumpModule = '';
|
||||||
|
},
|
||||||
|
copyMethodSignature(method) {
|
||||||
|
const params = method.params.map(p => p.PARAMTYPE + ' ' + p.PARAMNAME).join(', ');
|
||||||
|
const sig = (method.MODIFIERS || 'public') + ' ' + (method.RETTYPE || 'void') + ' ' + method.METHODNAME + '(' + params + ')';
|
||||||
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
navigator.clipboard.writeText(sig).then(() => {
|
||||||
|
this.$message.success('方法签名已复制');
|
||||||
|
}).catch(() => {
|
||||||
|
this.$message.warning('复制失败,请手动复制');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const ta = document.createElement('textarea');
|
||||||
|
ta.value = sig;
|
||||||
|
document.body.appendChild(ta);
|
||||||
|
ta.select();
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
this.$message.success('方法签名已复制');
|
||||||
|
} catch (e) {
|
||||||
|
this.$message.warning('复制失败,请手动复制');
|
||||||
|
}
|
||||||
|
document.body.removeChild(ta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadRecentPaths();
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const path = urlParams.get('path');
|
||||||
|
if (path) {
|
||||||
|
this.filePath = path;
|
||||||
|
} else if (this.recentPaths.length) {
|
||||||
|
this.filePath = this.recentPaths[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user