C# VC VB.NET JS 注释小插件(外接程序)


在上一个项目中由于修改代码 里面的添加/删除/修改的注释都有规范,因此做了一个注释的第三方dll小插件。效果不错,被日本Nes要去全面推广让其他协力公司都用。

因此提取出来发给大家。先上代码。在Vs2012里面。我试了下没成功,因此先发代码。稍后我整合打包传上来了。


VS 创建外接程序 参考链接

http://technet.microsoft.com/zh-tw/library/90855k9f
http://technet.microsoft.com/zh-cn/library/ms165621
http://technet.microsoft.com/zh-cn/library/7k3w6w59

http://www.cnblogs.com/Reborn/archive/2010/02/04/1664010.html C#利用外接程序生成解决方案

http://technet.microsoft.com/zh-cn/magazine/ms228771%28VS.90%29.aspx  如何:在外接程序按钮上显示自定义图标

Connect.cs 最重要的类。注释的方法都在里面

/// <summary>用于实现外接程序的对象。</summary>
    public class Connect : IDTExtensibility2, IDTCommandTarget
    {
        /// <summary>实现外接程序对象的构造函数。请将您的初始化代码置于此方法内。</summary>
        public Connect()
        {
        }

        ///  <summary>实现 IDTExtensibility2 接口的 OnConnection 方法。接收正在加载外接程序的通知。</summary>
        /// <param term='application'>宿主应用程序的根对象。</param>
        /// <param term='connectMode'>描述外接程序的加载方式。</param>
        /// <param term='addInInst'>表示此外接程序的对象。</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            _applicationObject = (DTE2)application;
            _addInInstance = (AddIn)addInInst;
            if (connectMode == ext_ConnectMode.ext_cm_UISetup)
            {
                object[] contextGUIDS = new object[] { };
                Commands2 commands = (Commands2)_applicationObject.Commands;
                string toolsMenuName;
                try
                {
                    //  添加在菜单栏里面
                    //  CommandBar.resx.
                    ResourceManager resourceManager = new ResourceManager("StarkingCommentTool.CommandBar", Assembly.GetExecutingAssembly());
                    CultureInfo cultureInfo = new System.Globalization.CultureInfo(_applicationObject.LocaleID);
                    string resourceName = String.Concat(cultureInfo.TwoLetterISOLanguageName, "Tools");
                    toolsMenuName = resourceManager.GetString(resourceName);
                }
                catch
                {
                    toolsMenuName = "Tools";
                }

                //将此命令置于“工具”菜单上。
                //查找 MenuBar 命令栏,该命令栏是容纳所有主菜单项的顶级命令栏:
                Microsoft.VisualStudio.CommandBars.CommandBar menuBarCommandBar = ((Microsoft.VisualStudio.CommandBars.CommandBars)_applicationObject.CommandBars)["MenuBar"];

                //在 MenuBar 命令栏上查找“工具”命令栏:
                CommandBarControl toolsControl = menuBarCommandBar.Controls[toolsMenuName];
                CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl;

                //如果希望添加多个由您的外接程序处理的命令,可以重复此 try/catch 块,
                //  只需确保更新 QueryStatus/Exec 方法,使其包含新的命令名。
                try
                {
                    //将一个命令添加到 Commands 集合:
                    Command commandA = commands.AddNamedCommand2(_addInInstance, "Add", "Add", "Add Comment", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);
                    Command commandD = commands.AddNamedCommand2(_addInInstance, "Del", "Del", "Delete Comment", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);
                    Command commandC = commands.AddNamedCommand2(_addInInstance, "Chg", "Chg", "Change Comment", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);

                    //将对应于该命令的控件添加到“工具”菜单:
                    if ((commandA != null) && (commandD != null) && (commandC != null) && (toolsPopup != null))
                    {
                        commandA.AddControl(toolsPopup.CommandBar, 1);
                        commandD.AddControl(toolsPopup.CommandBar, 1);
                        commandC.AddControl(toolsPopup.CommandBar, 1);
                    }
                }
                catch (System.ArgumentException)
                {
                    //如果出现此异常,原因很可能是由于具有该名称的命令
                    //  已存在。如果确实如此,则无需重新创建此命令,并且
                    //  可以放心忽略此异常。
                }
            }
        }
        /// <summary>实现 IDTExtensibility2 接口的 OnDisconnection 方法。接收正在卸载外接程序的通知。</summary>
        /// <param term='disconnectMode'>描述外接程序的卸载方式。</param>
        /// <param term='custom'>特定于宿主应用程序的参数数组。</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
        {
        }

        /// <summary>实现 IDTExtensibility2 接口的 OnAddInsUpdate 方法。当外接程序集合已发生更改时接收通知。</summary>
        /// <param term='custom'>特定于宿主应用程序的参数数组。</param>
        /// <seealso class='IDTExtensibility2' />		
        public void OnAddInsUpdate(ref Array custom)
        {
        }

        /// <summary>实现 IDTExtensibility2 接口的 OnStartupComplete 方法。接收宿主应用程序已完成加载的通知。</summary>
        /// <param term='custom'>特定于宿主应用程序的参数数组。</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnStartupComplete(ref Array custom)
        {
            ReadConf();
        }

        /// <summary>实现 IDTExtensibility2 接口的 OnBeginShutdown 方法。接收正在卸载宿主应用程序的通知。</summary>
        /// <param term='custom'>特定于宿主应用程序的参数数组。</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnBeginShutdown(ref Array custom)
        {
        }

        /// <summary>实现 IDTCommandTarget 接口的 QueryStatus 方法。此方法在更新该命令的可用性时调用</summary>
        /// <param term='commandName'>要确定其状态的命令的名称。</param>
        /// <param term='neededText'>该命令所需的文本。</param>
        /// <param term='status'>该命令在用户界面中的状态。</param>
        /// <param term='commandText'>neededText 参数所要求的文本。</param>
        /// <seealso class='Exec' />
        public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText)
        {
            if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
            {
                switch (commandName)
                {
                    case "StarkingCommentTool.Connect.Add":
                        goto case "StarkingCommentTool.Connect.Chg";
                    case "StarkingCommentTool.Connect.Del":
                        goto case "StarkingCommentTool.Connect.Chg";
                    case "StarkingCommentTool.Connect.Chg":
                        status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
                        return;
                }
            }
        }
        private DTE2 _applicationObject;
        private AddIn _addInInstance;
        private string _add1 = "";//before add
        private string _add2 = "";//after add
        private string _del1 = "";//before del
        private string _del2 = "";//after del
        private string _changeBefore1 = "";//change before
        private string _changeAfter2 = "";//change after
        private string _changeDel = "";// comment ///
        private bool _autoCopy = true;//复制

        private void ReadConf()
        {
            try
            {
                string strPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Visual Studio 2012\Addins\StarkingCommentTool.conf";

                string strUserName = Environment.UserName;
                string strDateFormat = "yyyy.MM.dd";

                string strAddFormatA = "NES Add {0} {1}";
                string strAddFormatB = "NES Add {0} {1}";

                string strDelFormatA = "NES Del {0} {1}";
                string strDelFormatB = "NES Del {0} {1}";

                string strCgAFormatB = "NES Change After {0} {1}";

                string strCgBFormatA = "NES Change Before {0} {1}";
                string strAllA = "";
                string strAllB = "";

                string strAddA = "";
                string strAddB = "";

                string strDelA = "";
                string strDelB = "";

                string strCgAB = "";

                string strCgBA = "";

                string strComm = "";

                if (!File.Exists(strPath))
                {
                    System.Windows.Forms.MessageBox.Show("Starking_comment_tool can't read config file \rplease check it exist!", "Starking_comment_tool Error");
                }
                else
                {
                    XmlDocument xd = new XmlDocument();
                    xd.Load(strPath);
                    XmlNode root = xd.ChildNodes[1];
                    //取出XML配置文件里面的自定义内容
                    strAddFormatA = root.SelectSingleNode("/config/before/add").InnerText;

                    strDelFormatA = root.SelectSingleNode("/config/before/del").InnerText;

                    strCgBFormatA = root.SelectSingleNode("/config/before/changeBefore").InnerText;

                    strAddFormatB = root.SelectSingleNode("/config/after/add").InnerText;

                    strDelFormatB = root.SelectSingleNode("/config/after/del").InnerText;

                    strCgAFormatB = root.SelectSingleNode("/config/after/changeAfter").InnerText;

                    strComm = root.SelectSingleNode("/config/comment").InnerText + " ";
                    _changeDel = strComm;

                    strDateFormat = root.SelectSingleNode("/config/dateFormat").InnerText + " ";
                    //
                    //取出XML配置文件节点other里面的自定义内容
                    strAllA = root.SelectSingleNode("/config/other/before/all").InnerText;
                    strAddA = root.SelectSingleNode("/config/other/before/add").InnerText;
                    strDelA = root.SelectSingleNode("/config/other/before/del").InnerText;

                    strCgBA = root.SelectSingleNode("/config/other/before/changeBefore").InnerText;

                    strAllB = root.SelectSingleNode("/config/other/after/all").InnerText;
                    strAddB = root.SelectSingleNode("/config/other/after/add").InnerText;
                    strDelB = root.SelectSingleNode("/config/other/after/del").InnerText;
                    strCgAB = root.SelectSingleNode("/config/other/after/changeAfter").InnerText;


                    string strAutoCopy = root.SelectSingleNode("/config/changeAutoCopy").InnerText;
                    bool.TryParse(strAutoCopy, out _autoCopy);
                }

                string strDate = DateTime.Now.ToString(strDateFormat);
                //add
                _add1 = strComm + string.Format(strAddFormatA, strDate, strUserName) + strAddA + strAllA + Environment.NewLine;

                _add2 = strComm + string.Format(strAddFormatB, strDate, strUserName) + strAddB + strAllB + Environment.NewLine;

                //change
                _changeBefore1 = strComm + string.Format(strCgBFormatA, strDate, strUserName) + strCgBA + strAllA + Environment.NewLine;

                _changeAfter2 = strComm + string.Format(strCgAFormatB, strDate, strUserName) + strCgAB + strAllB + Environment.NewLine;

                //delete
                _del1 = strComm + string.Format(strDelFormatA, strDate, strUserName) + strDelA + strAllA + Environment.NewLine;
                _del2 = strComm + string.Format(strDelFormatB, strDate, strUserName) + strDelB + strAllB + Environment.NewLine;

            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }
        }
        /// <summary>实现 IDTCommandTarget 接口的 Exec 方法。此方法在调用该命令时调用。</summary>
        /// <param term='commandName'>要执行的命令的名称。</param>
        /// <param term='executeOption'>描述该命令应如何运行。</param>
        /// <param term='varIn'>从调用方传递到命令处理程序的参数。</param>
        /// <param term='varOut'>从命令处理程序传递到调用方的参数。</param>
        /// <param term='handled'>通知调用方此命令是否已被处理。</param>
        /// <seealso class='Exec' />
        public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
        {
            handled = false;
            if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
            {
                switch (commandName)
                {
                    #region//StarkingCommentTool.Connect.Add
                    case "StarkingCommentTool.Connect.Add":
                        TextSelection ts = _applicationObject.ActiveDocument.Selection as TextSelection;
                        int TopLine = ts.TopPoint.Line;
                        int ButtomLine = ts.BottomPoint.Line;
                        bool ButtomAtStartOfLine = ts.BottomPoint.AtStartOfLine;
                        int startLine = 0;
                        int endLine = 0;

                        if (TopLine == ButtomLine)
                        {
                            ts.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn, false);
                            ts.Insert(_add1, 0);
                            ts.SmartFormat();
                            ts.LineDown(false, 1);
                            ts.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn, false);
                            ts.Insert(_add2, 0);
                            ts.SmartFormat();
                            ts.GotoLine(TopLine + 2, false);
                        }
                        else
                        {

                            if (ts.TopPoint.AtEndOfLine)
                            {
                                ts.GotoLine(TopLine + 1, false);
                                ts.Insert(_add1, 0);
                            }
                            else
                            {
                                ts.GotoLine(TopLine, false);
                                ts.Insert(_add1, 0);
                            }
                            ts.SmartFormat();

                            if (ButtomAtStartOfLine)
                            {
                                ts.GotoLine(ButtomLine + 1, false);
                                ts.Insert(_add2, 0);
                            }
                            else
                            {
                                ts.GotoLine(ButtomLine + 2, false);
                                ts.Insert(_add2, 0);
                            }

                            ts.SmartFormat();
                        }

                        handled = true;
                        return;
                    #endregion

                    #region//StarkingCommentTool.Connect.Del
                    case "StarkingCommentTool.Connect.Del":
                        ts = _applicationObject.ActiveDocument.Selection as TextSelection;
                        TopLine = ts.TopPoint.Line;
                        ButtomLine = ts.BottomPoint.Line;
                        ButtomAtStartOfLine = ts.BottomPoint.AtStartOfLine;

                        // Area Delete
                        if (ts.TopPoint.AtEndOfLine)
                        {
                            ts.GotoLine(TopLine + 1, false);
                            startLine = TopLine + 2;
                            ts.Insert(_del1, 0);
                        }
                        else
                        {
                            ts.GotoLine(TopLine, false);
                            startLine = TopLine + 1;
                            ts.Insert(_del1, 0);
                        }
                        if (ButtomAtStartOfLine)
                        {
                            ts.GotoLine(ButtomLine + 1, false);
                            endLine = ButtomLine;
                            ts.Insert(_del2, 0);
                        }
                        else
                        {
                            ts.GotoLine(ButtomLine + 2, false);
                            endLine = ButtomLine + 1;
                            ts.Insert(_del2, 0);
                        }
                        for (int line = startLine; line < endLine + 1; line++)
                        {
                            ts.GotoLine(line, false);
                            ts.SelectLine();
                            if (ts.Text.Contains("	") || ts.Text.Contains("{") || ts.Text.Contains("}") || ts.Text.Contains("//") || !ts.Text.Contains("///") || !ts.Text.Contains("#region") || !ts.Text.Contains("#endregion"))
                            {
                                ts.Insert(ts.Text.TrimStart("	".ToCharArray()), 0);
                            }
                            if (ts.Text.Contains(" ") || ts.Text.Contains("{") || ts.Text.Contains("}") || ts.Text.Contains("//") || !ts.Text.Contains("///") || !ts.Text.Contains("#region") || !ts.Text.Contains("#endregion"))
                            {
                                ts.Insert(_changeDel + ts.Text.TrimStart(" ".ToCharArray()), 0);
                            }
                        }
                        //缩放注释到多少列 start==>end
                        ts.GotoLine(TopLine, false);
                        //计算选中的行数来进行注释
                        if (TopLine == ButtomLine)
                        {   //单行删除注释
                            ts.LineDown(true, ButtomLine + 3 - TopLine);
                        }
                        else
                        {   //多行删除注释
                            ts.LineDown(true, ButtomLine + (endLine + 1 - startLine) - TopLine);
                        }
                        ts.SmartFormat();

                        handled = true;
                        return;
                    #endregion

                    #region//StarkingCommentTool.Connect.Chg
                    case "StarkingCommentTool.Connect.Chg":
                        ts = _applicationObject.ActiveDocument.Selection as TextSelection;
                        TopLine = ts.TopPoint.Line;
                        ButtomLine = ts.BottomPoint.Line;
                        ButtomAtStartOfLine = ts.BottomPoint.AtStartOfLine;
                        int intCount = ts.TextRanges.Count;
                        if (TopLine == ButtomLine)//单行变更
                        {
                            ts.GotoLine(TopLine, false);
                            ts.SelectLine();
                            //选中要变化的内容
                            string strText = string.Empty;
                            if (ts.Text.Contains("	") || ts.Text.Contains("{") || ts.Text.Contains("}") || ts.Text.Contains("//") || !ts.Text.Contains("///") || !ts.Text.Contains("#region") || !ts.Text.Contains("#endregion"))
                            {
                                strText = ts.Text.TrimStart("	".ToCharArray());
                            }
                            if (ts.Text.Contains(" ") || ts.Text.Contains("{") || ts.Text.Contains("}") || ts.Text.Contains("//") || !ts.Text.Contains("///") || !ts.Text.Contains("#region") || !ts.Text.Contains("#endregion"))
                            {
                                strText = ts.Text.TrimStart(" ".ToCharArray());
                            }
                            string strText2 = _changeBefore1 + _changeDel + strText.Replace("	", "");
                            //复制要变更的内容
                            if (_autoCopy)
                                strText2 += strText;
                            else
                                strText2 += Environment.NewLine;
                            strText2 += _changeAfter2;
                            //插入变更内容
                            ts.Insert(strText2, 0);
                            ts.GotoLine(TopLine, false);
                            //单行删除注释
                            ts.LineDown(true, ButtomLine + 4 - TopLine);
                            ts.SmartFormat();
                        }
                        else//多行内容变更
                        {
                            if (!ts.TopPoint.AtEndOfLine && !ButtomAtStartOfLine)
                            {
                                intCount++;
                            }
                            if (ts.TopPoint.AtEndOfLine)
                            {
                                ts.GotoLine(TopLine + 1, false);
                                startLine = TopLine + 2;
                                ts.Insert(_changeBefore1, 0);
                            }
                            else
                            {
                                ts.GotoLine(TopLine, false);
                                startLine = TopLine + 1;
                                ts.Insert(_changeBefore1, 0);
                            }
                            if (ButtomAtStartOfLine)
                            {
                                ts.GotoLine(ButtomLine + 1, false);
                                endLine = ButtomLine;
                            }
                            else
                            {
                                ts.GotoLine(ButtomLine + 2, false);
                                endLine = ButtomLine + 1;
                            }
                            //存放选中变化的内容
                            StringBuilder sb = new StringBuilder();
                            if (_autoCopy)
                            {
                                for (int line = startLine; line < endLine + 1; line++)
                                {
                                    ts.GotoLine(line, false);
                                    ts.SelectLine();
                                    sb.Append(ts.Text);
                                }
                            }
                            //循环注释掉选中的内容
                            for (int line = startLine; line < endLine + 1; line++)
                            {
                                ts.GotoLine(line, false);
                                ts.SelectLine();
                                if (ts.Text.Contains("	") || ts.Text.Contains("{") || ts.Text.Contains("}") || ts.Text.Contains("//") || !ts.Text.Contains("///") || !ts.Text.Contains("#region") || !ts.Text.Contains("#endregion"))
                                {
                                    ts.Insert(ts.Text.TrimStart("	".ToCharArray()), 0);
                                }
                                if (ts.Text.Contains(" ") || ts.Text.Contains("{") || ts.Text.Contains("}") || ts.Text.Contains("//") || !ts.Text.Contains("///") || !ts.Text.Contains("#region") || !ts.Text.Contains("#endregion"))
                                {
                                    ts.Insert(_changeDel + ts.Text.TrimStart(" ".ToCharArray()), 0);
                                }
                            }
                            //复制选中的内容到计算好的行数中
                            ts.GotoLine(endLine + 1, false);
                            if (_autoCopy)
                            {
                                ts.Insert(sb.ToString(), 0);
                                ts.GotoLine(endLine + intCount, false);
                            }
                            else
                            {
                                ts.NewLine(1);
                            }
                            //插入注释结束语句
                            ts.Insert(_changeAfter2, 0);
                            ts.GotoLine(TopLine, false);
                            //计算行数光标停止在最后一行+1行
                            ts.LineDown(true, (endLine + intCount + 1) - TopLine);
                            ts.SmartFormat();
                        }
                        handled = true;
                        return;
                    #endregion
                }
            }
        }
    }

StarkingCommentTool.AddIn 创建自动产生的里面放外接程序的信息

<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<Extensibility xmlns="http://schemas.microsoft.com/AutomationExtensibility">
  <HostApplication>
    <Name>Microsoft Visual Studio</Name>
    <Version>11.0</Version>
  </HostApplication>
	<Addin>
		<FriendlyName>StarkingCommentTool</FriendlyName>
		<Description>C# VC VB.NET JS 注释工具</Description>
		<Assembly>StarkingCommentTool.dll</Assembly>
		<FullClassName>StarkingCommentTool.Connect</FullClassName>
		<LoadBehavior>5</LoadBehavior>
		<CommandPreload>1</CommandPreload>
		<CommandLineSafe>1</CommandLineSafe>
	</Addin>
</Extensibility>

StarkingCommentTool.conf  配置文件 你可以配置成自己想要的注释风格

<?xml version="1.0" encoding="utf-8" ?>
<config>
    <!--
  add: 追加
  del: 削除
  changeAfter: 変更前
  changeBefore: 変更後
  {0}: 时间格式化
  NOTE: 请不要删除此文件中的注释(重要都是配置节点)
  -->
    <before>
        <!-- 変更前的应用 -->
        <add>{0}1处System对应 INSERT START</add>
        <del>{0}1处System对应 DELETE START</del>
        <changeBefore>{0}1处System对应 UPDATE START</changeBefore>
    </before>

    <after>
        <!-- 変更后的应用 -->
        <add>{0}1处System对应 INSERT END</add>
        <del>{0}1处System对应 DELETE END</del>
        <changeAfter>{0}1处System对应 UPDATE END</changeAfter>
    </after>
   
    <!--   dateFormat:日期格式化
     例:
      日付 2012年12月25日 
      dateFormat: yyyy.MM.dd
      注释中的格式为中 2012.12.25 。
  -->
    <dateFormat>yy/MM/dd</dateFormat>
    <other>
        <!-- 
  all: 追加,削除,変更前,変更后等最后要追加的文字。
  add: 追加后最后要追加的文字。
  del: 削除后最后要追加的文字。
  changeAfter: 修改前最后要追加的文字。
  changeBefore: 修改后変最后要追加的文字。
  
  all的优先级是最低的
  例如 other中, 如果添加了all和add的場合, 那么注释中就会显示other.add other.all的样子。
    -->
        <before>
            <all></all>
            <add></add>
            <del></del>
            <changeBefore></changeBefore>
        </before>

        <after>
            <all></all>
            <add></add>
            <del></del>
            <changeAfter></changeAfter>
        </after>
    </other>

    <!-- 修改的内容是否复制-->
    <changeAutoCopy>True</changeAutoCopy>

    <!-- 注释 
       VB: ' 
       C#: ///
   -->
    <comment>///</comment>
</config>


先贴出代码,稍后我整理测试完毕打包上传。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值