当前位置:   article > 正文

idea插件开发教程

idea插件开发教程

主要的插件类型

  • UI主题插件
  • 自定义语言支持插件
  • 框架集成插件
  • 工具集成插件

开发插件的三种方式

  • Using Gradle
  • Using GitHub Template
    在github上选择一个模板,会自动根据模板生成一个配置好的插件项目,可以直接clone到本地开发
  • Using DevKit

使用DevKit开发插件

设置开发环境

准备工作

确认启用Plugin DevKit插件,该插件是IDEA自带的插件

为项目配置IntelliJ Platform SDK

  • 创建一个ntelliJ Platform SDK
    在这里插入图片描述
  • 指定sdk的位置
    在这里插入图片描述
  • 配置sdk源码路径,如果不需要debug此步可以跳过
    在这里插入图片描述
    在这里插入图片描述
  • 设置沙箱目录, 插件项目的setting信息会保存在这个目录, 可以指定任意目录。
    在这里插入图片描述

创建插件项目

  • 创建项目
    在这里插入图片描述

  • 创建具体的插件模块, 如果该项目只开发一个插件则不用创建模块。
    在这里插入图片描述

  • 设置插件的sdk

启动配置

在Run Configuration菜单下配置启动配置, 配置jre。 点击run按钮,会启动一个ieda实例,并且启用当前开发的插件。

部署插件

  • build项目或模块
  • Prepare your plugin for deployment.
    如果插件没有其他依赖,则会创建一个jar包,否则会创建一个zip压缩包。
    在这里插入图片描述

发布插件

可以发布到自定义仓库或者发布到idea公共仓库

插件架构

插件jar包含的文件

  1. 插件配置文件plugin.xml
  2. 实现插件功能的类
  3. 插件依赖的jar包
    在这里插入图片描述

插件ClassLoader

每个插件都使用独立的ClassLoader, 所以每个插件可以使用不同版本的依赖,不会相互影响。如果插件的classLoader没有发现某个类,则默认由IDEA的ckassLoader加载。但是可以在plugin.xml中的元素下声明依赖其它插件,那么就会使用其它插件的classLoader加载未找到的类。

Actions

action官方文档
Action是实现插件功能的类, 一个Action类需要继承AnAction并且实现actionPerformed方法。当用户点击菜单或者工具栏按钮, 按快捷键,或者通过Help | Find Action点击时, IntelliJ Platform系统会回调对应Action的actionPerformed方法。
Action会被分组, 一个组可以包含actions也可以包含子组,一个action组就是一个菜单或者工具栏,子组就是子菜单。

注册Action

通过plugin.xml注册action。

<actions>

  <!--
   - "id" (required) - specifies a unique identifier for the action
   - "class" (required) - specifies the FQN of the class implementing
     the action
   - "text" (required) - specifies the default long-version text to be
     displayed for the action (tooltip for toolbar button or text for
     menu item)
   - "use-shortcut-of" (optional) - specifies the ID of the action
     whose keyboard shortcut this action will use
   - "description" (optional) - specifies the text which is displayed
     in the status bar when the action is focused
   - "icon" (optional) - specifies the icon which is displayed on
     the toolbar button or next to the menu item
  -->
  <action
      id="VssIntegration.GarbageCollection"
      class="com.example.impl.CollectGarbage"
      text="Garbage Collector: Collect _Garbage"
      description="Run garbage collector"
      icon="icons/garbage.png">

    <!--
    The <override-text> element defines an alternate version of the text
    for the menu action. Attributes:
     - "text" (required) - defines the text to be displayed for the action
     - "place" (required) - declares where the alternate text should
        be used. In this example, any time the action is displayed in
        the IDE main menu (and submenus), the override-text version should
        be used.
    The second <override-text> element uses the alternate attribute
    "use-text-of-place" to define a location (EditorPopup) to use the same
    text as is used in MainMenu. It is a way to specify the use of
    an alternate menu text in multiple discrete menu groups.
    -->
    <override-text place="MainMenu" text="Collect _Garbage"/>
    <override-text place="EditorPopup" use-text-of-place="MainMenu"/>

    <!-- Provide alternative names for searching action by name -->
    <synonym text="GC"/>

    <!--
    The <add-to-group> node specifies that the action should be added to an
    existing group. An action can be added to several groups. Attributes:
     - "group-id" (required) - specifies the ID of the group to which the
       action is added. The group must be an implementation of the
       DefaultActionGroup class.
     - "anchor" (required) - specifies the position of the action in
       the relative to other actions. Allowed values: "first", "last",
       "before", and "after".
     - "relative-to-action" (mandatory if "anchor" is "before" or "after") -
       specifies the action before or after which the current action is
       inserted.
    -->
    <add-to-group
        group-id="ToolsMenu"
        relative-to-action="GenerateJavadoc"
        anchor="after"/>

    <!--
    The <keyboard-shortcut> node specifies the keyboard shortcut for
    the action. An action can have several keyboard shortcuts. Attributes:
     - "first-keystroke" (required) - specifies the first keystroke of
       the action. The keystrokes are specified according to the regular
       Swing rules.
     - "second-keystroke" (optional) - specifies the second keystroke of
       the action.
     - "keymap" (required) - specifies the keymap for which the action
       is active. IDs of the standard keymaps are defined as constants
       in the com.intellij.openapi.keymap.KeymapManager class.
     - "remove" (optional) - shortcut should be removed from the specified
       action. See the second <keyboard-shortcut> example element below.
     - "replace-all" (optional) - removes all keyboard and mouse shortcuts
       from the specified action before adding the specified shortcut.
       See the third <keyboard-shortcut> example element below.
    -->

    <!-- Add the first and second keystrokes to all keymaps... -->
    <keyboard-shortcut
        keymap="$default"
        first-keystroke="control alt G"
        second-keystroke="C"/>

    <!-- ...except the "Mac OS X" keymap and its children. -->
    <keyboard-shortcut
        keymap="Mac OS X"
        first-keystroke="control alt G"
        second-keystroke="C"
        remove="true"/>

    <!-- The "Mac OS X 10.5+" keymap and its children will have only this
    keyboard shortcut for this action. -->
    <keyboard-shortcut
        keymap="Mac OS X 10.5+"
        first-keystroke="control alt G"
        second-keystroke="C"
        replace-all="true"/>

    <!--
    The <mouse-shortcut> node specifies the mouse shortcut for the action.
    An action can have several mouse shortcuts. Attributes:
    - "keystroke" (required) - specifies the clicks and modifiers for
      the action. It is defined as a sequence of words separated by spaces:
       * mouse buttons: "button1", "button2", "button3"
       * modifier keys: "shift", "control", "meta", "alt", "altGraph"
       * button double-click: "doubleClick"
    - "keymap" (required) - specifies the keymap for which the action is
      active. IDs of the standard keymaps are defined as constants in
      the com.intellij.openapi.keymap.KeymapManager class.
    The <mouse-shortcut> element can also specify "remove" and "replace-all"
    attributes. See <keyboard-shortcut> description above for details.
    -->
    <mouse-shortcut
        keymap="$default"
        keystroke="control button3 doubleClick"/>
  </action>

  <!--
  This action declares neither a text nor a description attribute. If it
  has a resource bundle declared, the text and descriptions will be
  retrieved based on the action-id incorporated in the key for a translated
  string.
  -->
  <action
      id="sdk.action.PopupDialogAction"
      class="sdk.action.PopupDialogAction"
      icon="SdkIcons.Sdk_default_icon"/>

  <!--
  The <group> element defines an action group. The <action>, <group>
  and <separator> elements defined within it are automatically included
  in the group. Attributes:
   - "id" (required) - specifies a unique identifier for the group.
   - "class" (optional) - specifies the FQN of the class implementing
     the group. If not specified,
     com.intellij.openapi.actionSystem.DefaultActionGroup is used.
   - "text" (optional) - specifies the text of the group (text for the menu
     item showing the submenu).
   - "description" (optional) specifies the text which is displayed in the
     status bar when the group has focus.
   - "icon" (optional) - specifies the icon which is displayed on
     the toolbar button or next to the menu group.
   - "popup" (optional) - specifies how the group is presented in the menu.
      * "true" - group actions are placed in a submenu
      * "false" - actions are displayed as a section of the same menu
        delimited by separators.
   - "compact" (optional) - specifies whether an action within that group
     is visible when disabled. Setting compact="true" specifies an action
     in the group isn't visible unless the action is enabled.
  -->
  <group
      class="com.example.impl.MyActionGroup"
      id="TestActionGroup"
      text="Test Group"
      description="Group with test actions"
      icon="icons/testgroup.png"
      popup="true"
      compact="true">

    <action
        id="VssIntegration.TestAction"
        class="com.example.impl.TestAction"
        text="My Test Action"
        description="My test action"/>

    <!-- The <separator> element defines a separator between actions.
    It can also have an <add-to-group> child element. -->
    <separator/>

    <group id="TestActionSubGroup"/>

    <!-- The <reference> element allows adding an existing action to
    the group. The mandatory "ref" attribute specifies the ID of
    the action to add. -->
    <reference ref="EditorCopy"/>

    <add-to-group
        group-id="MainMenu"
        relative-to-action="HelpMenu"
        anchor="before"/>
  </group>
</actions>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183

扩展(Extensions)

扩展是插件最常用的一种扩展IDEA功能的方式, 只是这种方式没有直接把Action加到菜单或者工具栏那么直接。扩展功能是通过IDEA自带的或者其它插件提供的一些扩展点实现的。
声明扩展要在plugin.xml中添加标签。

<!--
defaultExtensionNs属性只有两种值:
1. com.intellij 使用 IntelliJ Platform核心扩展点
2. {ID of a plugin} 使用其它插件的扩展点,必须要配置其它插件的依赖

子标签的名称必须和扩展点的名称相同
扩展点有两种类型
3. 扩展点使用interface 属性声明, 则设置扩展点标签的implementation属性, 属性值设置实现了interface对应接口的实现类的全限定名
4. 扩展点使用beanClass属性声明,则可以设置beanClass对应的扩展点类中@Attribute注解标注的属性。

可以使用view | quick document 查看扩展点帮助  
 -->
<extensions defaultExtensionNs="com.intellij">
  <appStarter implementation="com.example.MyAppStarter"/>
  <projectTemplatesFactory implementation="com.example.MyProjectTemplatesFactory"/>
</extensions>

<!--
  Declare extensions to access extension points in a custom plugin "another.plugin".
  The "myExtensionPoint" extension point has been declared using "beanClass"
  and exposes custom properties "key" and "implementationClass".
-->
<extensions defaultExtensionNs="another.plugin">
  <myExtensionPoint
      key="keyValue"
      implementationClass="com.example.MyExtensionPointImpl"/>
</extensions>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

Service

Service是插件的一个组件, 是为了把公共的逻辑放到一起,Service的实例是单例的。
有三种类型的Service, 应用级别的,项目级别的,模块级别的。

声明Service有两种方式

  1. 类上加@Service注解,service不需要被重写的时候可以使用这种方式

  2. 在plugin.xml中声明,通过applicationService,projectService扩展点声明

<extensions defaultExtensionNs="com.intellij">
  <!-- Declare the application-level service -->
  <applicationService
      serviceInterface="mypackage.MyApplicationService"
      serviceImplementation="mypackage.MyApplicationServiceImpl"/>

  <!-- Declare the project-level service -->
  <projectService
      serviceInterface="mypackage.MyProjectService"
      serviceImplementation="mypackage.MyProjectServiceImpl"/>
</extensions>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

获取Service

MyApplicationService applicationService = ApplicationManager.getApplication()
  .getService(MyApplicationService.class);

MyProjectService projectService = project.getService(MyProjectService.class);
  • 1
  • 2
  • 3
  • 4

实现类可以封装静态方法,方便获取

MyApplicationService applicationService = MyApplicationService.getInstance();

MyProjectService projectService = MyProjectService.getInstance(project);
  • 1
  • 2
  • 3

和其它Service交互

@Service
public final class ProjectService {

  private final Project myProject;

  public ProjectService(Project project) {
    myProject = project;
  }

  public void someServiceMethod(String parameter) {
    AnotherService anotherService = myProject.getService(AnotherService.class);
    String result = anotherService.anotherServiceMethod(parameter, false);
    // do some more stuff
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Extension Points

声明扩展点
有两种类型的扩展点

  1. Interface 其它插件提供该接口的实现类
  2. Bean 其它插件提供子类, 这种方式其它插件可以设置一些扩展点类的属性值
<idea-plugin>
  <id>my.plugin</id>

  <extensionPoints>
    <extensionPoint
        name="myExtensionPoint1"
        beanClass="com.example.MyBeanClass"/>

    <extensionPoint
        name="myExtensionPoint2"
        interface="com.example.MyInterface"/>
  </extensionPoints>

</idea-plugin>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

扩展点类

public class MyBeanClass extends AbstractExtensionPointBean {

  @Attribute("key")
  public String key;

  @Attribute("implementationClass")
  public String implementationClass;

  public String getKey() {
    return key;
  }

  public String getClass() {
    return implementationClass;
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

其它插件使用该扩展点

<idea-plugin>
  <id>another.plugin</id>

  <!-- Declare dependency on plugin defining extension point: -->
  <depends>my.plugin</depends>

  <!-- Use "my.plugin" namespace: -->
  <extensions defaultExtensionNs="my.plugin">
    <myExtensionPoint1
        key="someKey"
        implementationClass="another.some.implementation.class"/>

    <myExtensionPoint2
        implementation="another.MyInterfaceImpl"/>
  </extension>

</idea-plugin>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

声明扩展点的类使用扩展点

public class MyExtensionUsingService {

  private static final ExtensionPointName<MyBeanClass> EP_NAME =
    ExtensionPointName.create("my.plugin.myExtensionPoint1");

  public void useExtensions() {
    for (MyBeanClass extension : EP_NAME.getExtensionList()) {
      String key = extension.getKey();
      String clazz = extension.getClass();
      // ...
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

plugin.xml

这个示例配置文件除了Action的配置,其余配置全部都配置了。

<!--
An optional "url" attribute specifies the link to the plugin homepage.
It is displayed on the plugin page in the Marketplace.
-->
<idea-plugin url="https://example.com/my-plugin-site">

  <!--
  Unique identifier of the plugin. It should be a fully qualified name
  including namespace to not collide with existing plugins. It cannot be
  changed between the plugin versions. If not specified, <name> will be
  used (not recommended).
  -->
  <id>com.example.myplugin</id>

  <!-- Public plugin name. It should use Title Cases. Guidelines:
  https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-name
  -->
  <name>My Framework Support</name>

  <!--
  Plugin version. Plugins uploaded to the Marketplace must follow
  the semantic versioning: https://plugins.jetbrains.com/docs/marketplace/semver.html.
  It is displayed in the "Plugins" settings dialog and in the Marketplace
  plugin page.
  -->
  <version>1.0.0</version>

  <!--
  Vendor name or Organization ID (if you have one created).
  Attributes:
   - "url" (optional) - specifies the link to the vendor's homepage
   - "email" (optional) - specifies the vendor's email address
  Displayed on the Plugins Page.
  -->
  <vendor
      url="https://plugins.jetbrains.com/my-company"
      email="contact@example.com">My Company</vendor>

  <!--
  IMPORTANT: This tag should not be used in free plugins.
  If you decide to make your plugin paid, you will need to define
  the parameters in the <product-descriptor> tag.
  You can also enable free functionality in a paid plugin. Learn more in
  a guide to selling plugin: https://plugins.jetbrains.com/build-and-market
  -->
  <product-descriptor
      code="PMYPLUGIN"
      release-date="20210901"
      release-version="20211"
      optional="true"/>

  <!-- Minimum and maximum IDE build versions compatible with the plugin. -->
  <idea-version since-build="193" until-build="193.*"/>

  <!--
  Plugin description displayed on the Marketplace plugin page and in
  the IDE Plugin Manager.
  Simple HTML elements (text formatting, paragraphs, lists, etc.) can be
  added inside of <![CDATA[ ]]> tag. Guidelines:
  https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-description
  -->
  <description>
  <![CDATA[
    Provides support for <a href="https://example.com/my-framework">My
    Framework</a>.
    <p>Includes support for:
    <ul>
      <li>code completion</li>
      <li>references</li>
      <li>refactoring</li>
    </ul>
    </p>
  ]]>
  </description>

  <!--
  Short summary of new features and bugfixes in the latest plugin version.
  Displayed on the Marketplace plugin page and in the IDE Plugin Manager.
  Simple HTML elements can be included between <![CDATA[  ]]> tags.
  -->
  <change-notes>Initial release of the plugin.</change-notes>

  <!--
  Product and plugin compatibility requirements. Read more:
  https://plugins.jetbrains.com/docs/intellij/plugin-compatibility.html
  -->
  <depends>com.intellij.modules.platform</depends>
  <depends>com.example.third-party-plugin</depends>

  <!--
  Optional dependency on another plugin. If the plugin with the
  "com.example.my-second-plugin" ID is installed, the contents
  of "mysecondplugin.xml" (the format of this file conforms to
  the format of plugin.xml) will be loaded.
  -->
  <depends
      optional="true"
      config-file="mysecondplugin.xml">com.example.my-second-plugin</depends>

  <!--
  Resource bundle (/messages/MyPluginBundle.properties) to be used with
  "key" attributes in extension points and implicit keys like
  "action.[ActionID].text|description".
  -->
  <resource-bundle>messages.MyPluginBundle</resource-bundle>

  <!--
  Extension points defined by the plugin. Extension points are registered
  by a plugin so that other plugins can provide this plugin with certain
  data. Read more:
  https://plugins.jetbrains.com/docs/intellij/plugin-extension-points.html
  -->
  <extensionPoints>
    <extensionPoint
        name="testExtensionPoint"
        beanClass="com.example.impl.MyExtensionBean"/>
    <applicationService
        serviceImplementation="com.example.impl.MyApplicationService"/>
    <projectService
        serviceImplementation="com.example.impl.MyProjectService"/>
  </extensionPoints>

  <!--
  Application-level listeners. For more information, see:
  https://plugins.jetbrains.com/docs/intellij/plugin-listeners.html#defining-application-level-listeners
  -->
  <applicationListeners>
    <listener
        class="com.example.impl.MyListener"
        topic="com.intellij.openapi.vfs.newvfs.BulkFileListener"/>
  </applicationListeners>

  <!--
  Project-level listeners. For more information, see:
  https://plugins.jetbrains.com/docs/intellij/plugin-listeners.html#defining-project-level-listeners
  -->
  <projectListeners>
    <listener
        class="com.example.impl.MyToolwindowListener"
        topic="com.intellij.openapi.wm.ex.ToolWindowManagerListener"/>
  </projectListeners>

  <!--
  Actions. For more information, see:
  https://plugins.jetbrains.com/docs/intellij/basic-action-system.html
  -->
  <actions>
    <action
        id="VssIntegration.GarbageCollection"
        class="com.example.impl.CollectGarbage"
        text="Collect _Garbage"
        description="Run garbage collector">
      <keyboard-shortcut
          first-keystroke="control alt G"
          second-keystroke="C"
          keymap="$default"/>
    </action>
  </actions>

  <!-- Custom extensions declaration. For more information, see:
  https://plugins.jetbrains.com/docs/intellij/plugin-extensions.html#declaring-extensions
  -->
  <extensions defaultExtensionNs="VssIntegration">
    <myExtensionPoint implementation="com.example.impl.MyExtensionImpl"/>
  </extensions>


  <!--
  DEPRECATED: Do not use in new plugins!
  Plugin's application components.
  See https://plugins.jetbrains.com/docs/intellij/plugin-components.html
  for migration steps.
  -->
  <application-components>
    <component>
      <!-- Component's interface class -->
      <interface-class>com.example.Component1Interface</interface-class>
      <!-- Component's implementation class -->
      <implementation-class>com.example.impl.Component1Impl</implementation-class>
    </component>
  </application-components>

  <!--
  DEPRECATED: Do not use in new plugins!
  Plugin's project components.
  See https://plugins.jetbrains.com/docs/intellij/plugin-components.html
  for migration steps.
  -->
  <project-components>
    <component>
      <!-- Interface and implementation classes are the same -->
      <implementation-class>com.example.Component2</implementation-class>

      <!--
      If the "workspace" option is set "true", the component saves its state
      to the .iws file instead of the .ipr file. Note that the <option>
      element is used only if the component implements the
      JDOMExternalizable interface. Otherwise, the use of the <option>
      element takes no effect.
      -->
      <option name="workspace" value="true"/>

      <!--
      If the "loadForDefaultProject" tag is present, the project component
      is instantiated also for the default project.
      -->
      <loadForDefaultProject/>
    </component>
  </project-components>

  <!--
  DEPRECATED: Do not use in new plugins!
  Plugin's module components.
  See https://plugins.jetbrains.com/docs/intellij/plugin-components.html
  for migration steps.
  -->
  <module-components>
    <component>
      <implementation-class>com.example.Component3</implementation-class>
    </component>
  </module-components>
</idea-plugin>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/112159?site
推荐阅读
相关标签
  

闽ICP备14008679号