## 蓝鲸 IPv6 选择器

#### 使用

##### service.js
```javascript
  export default {
    fetchTopologyWithCount: (params) => {
      return http.post('topology_width_count', { params })
    },
    fetchTopologyHost: (params) => {
      return http.post('topology_host', { params })
    },
    fetchTopologyHostIdsNodes: (params) => {
      return http.post('topology_host_ids_noodes', { params })
    },
    // ...
  }
```

##### 创建组件
``` javascript
import vue from 'vue';
import createFactory from '@blueking/ip-selector';
import Service from './service'

// 初始化配置，创建组件
const IpSelector = createFactory({
  // 组件版本（改变版本重置用户自定义配置）
  version: '',
  // 需要支持的面板
  // 主机相关：'staticTopo', 'dynamicTopo', 'dynamicGroup', 'serviceTemplate', 'setTemplate', 'serviceInstance', 'manualInput'
  // 容器相关：'containerStaticTopo'(容器静态拓扑), 'containerFilterList'(容器动态分组), 'containerManualInput'(容器手动输入)
  panelList: ['staticTopo', 'dynamicTopo', 'dynamicGroup', 'manualInput'],
  // 面板选项的值是否唯一
  unqiuePanelValue: false,
  // 字段命名风格（'camelCase', 'kebabCase'）
  nameStyle: 'camelCase',
  // 主机列表全选模式，false: 本页全选；true: 跨页全选
  hostTableDefaultSelectAllMode: false,
  // 主机列表开启仅显示可用过滤
  hostOnlyValid: false,
  // 主机列表开启仅显示可用过滤时，手动输入 tab 判断主机可用的回调
  manualInputHostValidMethod: () => true,
  // 自定义主机列表列
  hostTableCustomColumnList: [
    // {
    //   key: 'collectStatus',
    //   index: 5,
    //   width: '100px',
    //   label: '采集状态',
    //   renderHead: h => h('span', '采集状态'),
    //   field: 'collect_status',
    //   renderCell: (h, row) => h('span', row.collect_status || '--'),
    // }
  ],
  nodeTableCustomColumnList: [],  // 自定义动态拓扑列表列 同上
  serviceTemplateTableCustomColumnList: [],  // 自定义服务模板列表列 同上
  setTemplateCustomColumnList: [],  // 自定义集群模板列表列 同上
  hostMemuExtends: [
    // {
    //   name: '复制采集状态异常',
    //   action: () => {
    //     console.log('复制成功');
    //   },
    // },
  ],
  // 主机预览字段显示
  hostViewFieldRender: (host, primaryField) => host.host_id,
  // 主机列表显示列（默认值：['ip', 'ipv6', 'alive', 'osName']），按配置顺序显示列
  // 内置所有列的 key ['ip', 'ipv6', 'cloudArea', 'alive', 'hostName', 'osName', 'coludVerdor', 'osType', 'hostId', 'agentId']
  hostTableRenderColumnList: [],
  // 选择完成后结果展示时是否统计主机总数（支持 dynamicTopo，dynamicGroup 两种类型的主机统计）
  showViewFinalHostTotalCount: false,

  // 创建时是否提示 service 信息
  serviceConfigError: false,

  // 警告全选了业务的所有资源，提示用户确认是否继续操作
  warningSelectAllBusinesses: true,

  // 需要的数据源配置（返回 Promise）
  // 主机拓扑
  fetchTopologyHostCount: Service.fetchTopologyWithCount,
  fetchTopologyHostsNodes: Service.fetchTopologyHost, // (params: Object, hostOnlyValid: boolean) => Promise<any[]>
  fetchTopologyHostIdsNodes: Service.fetchTopogyHostIdList, // (params: Object, hostOnlyValid: boolean) => Promise<any[]>
  fetchHostsDetails: Service.fetchHostInfoByHostId,
  fetchHostCheck: Service.fetchInputParseHostList,
  fetchNodesQueryPath: Service.fetchNodePath,
  fetchHostAgentStatisticsNodes: Service.fetchBatchNodeAgentStatistics,
  // 动态分组
  fetchDynamicGroups: Service.fetchDynamicGroup,
  fetchHostsDynamicGroup: Service.fetchDynamicGroupHost,
  fetchHostAgentStatisticsDynamicGroups: Service.fetchBatchGroupAgentStatistics,
  // 服务模板
  fetchServiceTemplates: Service.fetchServiceTemplates,
  fetchNodesServiceTemplate: Service.fetchNodesServiceTemplate,
  fetchHostServiceTemplate: Service.fetchHostServiceTemplate,
  fetchHostAgentStatisticsServiceTemplate: Service.fetchHostAgentStatisticsServiceTemplate,
  // 集群模板
  fetchSetTemplates: Service.fetchSetTemplates,
  fetchNodesSetTemplate: Service.fetchNodesSetTemplate,
  fetchHostSetTemplate: Service.fetchHostSetTemplate,
  fetchHostAgentStatisticsSetTemplate: Service.fetchHostAgentStatisticsSetTemplate,
  // 服务实例
  fetchSeriviceInstanceList: Service.fetchSeriviceInstanceList,
  fetchSeriviceInstanceDetails: Service.fetchSeriviceInstanceDetails,
  // 容器静态拓扑（containerStaticTopo）
  // fetchTopologyContainerWithCount 同时被容器静态拓扑与容器动态分组面板复用
  fetchTopologyContainerWithCount: Service.fetchTopologyContainerWithCount,
  fetchTopologyContainerContainer: Service.fetchTopologyContainerContainer,
  fetchContainerTopologyContainerIdList: Service.fetchContainerTopologyContainerIdList,
  fetchContainerDetail: Service.fetchContainerDetail,
  // 容器手动输入（containerManualInput）
  fetchContainerCheck: Service.fetchContainerCheck,
  // 容器动态分组（containerFilterList）
  // 拉取动态筛选可用的自定义属性字段元数据
  fetchDynamicContainerFieldMetadata: Service.fetchDynamicContainerFieldMetadata,
  // 根据筛选条件预览命中的容器列表
  fetchDynamicContainerPreview: Service.fetchDynamicContainerPreview,
  // 批量查询容器拓扑节点名称（查看态根据节点 id 生成筛选条件名称）
  fetchContainerNodesNames: Service.fetchContainerNodesNames,
  // DBM 白名单
  fetchDBMWhitelist: Service.fetchDBMWhitelist,
  // 自定义配置
  fetchCustomSettings: Service.fetchAll,
  updateCustomSettings: Service.update,
  // 系统相关配置
  fetchConfig: () => Promise.resolve()
    .then(() => ({
      // CMDB 动态分组链接
      bk_cmdb_dynamic_group_url: `http:xx.yy.zz.com/#/business/1/custom-query`,
      // CMDB 拓扑节点链接
      bk_cmdb_static_topo_url: `http:xx.yy.zz.com/#/business/1/custom-query`,
      // DBM 创建白名单链接
      bk_dbm_whitelist: `http:xx.yy.zz.com/#/business/1/custom-query`,
      // CMDB 服务模版链接
      bk_cmdb_service_template_url: `http:xx.yy.zz.com/#/business/1/custom-query`,
      // 人员选择器组件 HOST
      bk_user_selector_host: `http://xx.yy.com`
    })),
})

// 全局注册
vue.component('IpSelector', IpSelector)
```

#### 组件 Props
<table style='width:100%'>
  <thead>
    <tr>
      <th>属性名</th>
      <th>说明</th>
      <th>类型</th>
      <th>可选值</th>
      <th>默认值</th>
      <th>必填</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>mode</td>
      <td>IP 选择的交互模式</td>
      <td>String</td>
      <td>
        <code>dialog</code>
        |
        <code>section</code>
      </td>
      <td>
        <code>dialog</code>
      </td>
      <td>true</td>
    </tr>
    <tr>
      <td>showDialog</td>
      <td>mode 为 dialog 时弹出 dialog</td>
      <td>Boolean</td>
      <td>
          <code>true</code>
          |
          <code>false</code>
        </td>
      <td>
          <code>false</code>
        </td>
      <td>true</td>
    </tr>
    <tr>
      <td>showView</td>
      <td>IP 选择完成后是否显示结果</td>
      <td>Boolean</td>
      <td>
        <code>true</code>
        |
        <code>false</code>
      </td>
      <td>
        <code>false</code>
      </td>
      <td>true</td>
    </tr>
    <tr>
      <td>value</td>
      <td>默认结果</td>
      <td>Object</td>
      <td>--</td>
      <td>
<pre>
<code>
{
  hostList: [],
  nodeList: [],
  dynamicGroupList: [],
  serviceTemplateList: [],
  setTemplateList: [],
  containerList: [],
  containerFilterList: []
}
</code>
</pre>
        </td>
      <td>false</td>
    </tr>
    <tr>
      <td>originalValue</td>
      <td>编辑状态的初始值，用于和最新选择的值进行对比</td>
      <td>Object</td>
      <td>--</td>
      <td>
<pre>
<code>
{
  hostList: [],
  nodeList: [],
  dynamicGroupList: [],
  serviceTemplateList: [],
  setTemplateList: [],
  containerList: [],
  containerFilterList: []
}
</code>
</pre>
        </td>
      <td>false</td>
    </tr>
    <tr>
      <td>showViewDiff</td>
      <td>是否在选择结果面板显示数据对比</td>
      <td>Boolean</td>
      <td>
        <code>true</code>
        |
        <code>false</code>
        </td>
      <td>
        <code>false</code>
        </td>
      <td>false</td>
    </tr>
    <tr>
      <td>viewSearchKey</td>
      <td>在选择结果面板搜索主机</td>
      <td>String</td>
      <td>--</td>
      <td>--</td>
      <td>false</td>
    </tr>
    <tr>
      <td>defaultOutputFieldList</td>
      <td>
        蓝鲸监控场景下的默认主机输出字段
      </td>
      <td>Array</td>
      <td>--</td>
      <td>['ip', 'ipv6']</td>
      <td>--</td>
    </tr>
    <tr>
      <td>outputFieldOptionalHostTableColumn</td>
      <td>
        蓝鲸监控场景下主机输出备选字段列表
        （为空则可选所有主机字段，值为主机 column 的 key）
      </td>
      <td>Array</td>
      <td>--</td>
      <td>--</td>
      <td>--</td>
    </tr>
    <tr>
      <td>outputFieldList</td>
      <td>
        <p>蓝鲸监控场景下的配置主机输出字段</p>
        <p>（如果要开启该功能则值不能为空）</p>
      </td>
      <td>Array</td>
      <td>--</td>
      <td>--</td>
      <td>--</td>
    </tr>
    <tr>
      <td>readonly</td>
      <td>只读</td>
      <td>Boolean</td>
      <td>
        <code>true</code>
        |
        <code>false</code>
        </td>
      <td>--</td>
      <td>false</td>
    </tr>
    <tr>
      <td>singleHostSelect</td>
      <td>静态拓扑主机单选</td>
      <td>Boolean</td>
      <td>
        <code>true</code>
        |
        <code>false</code>
        </td>
      <td>--</td>
      <td>false</td>
    </tr>
    <tr>
      <td>disableDialogSubmitMethod</td>
      <td>
        <pre>Dialog 确定按钮是否禁用</pre>
        <pre>返回 Boolean 表示禁用状态，返回 String 表示禁用状态的 Tips</pre>
      </td>
      <td>Function</td>
      <td>--</td>
      <td>--</td>
      <td>false</td>
    </tr>
    <tr>
      <td>disableHostMethod</td>
      <td>
        <pre>静态拓扑主机是否禁用</pre>
        <pre>params: (hostData, hostList)</pre>
        <pre>返回 Boolean 表示禁用状态，返回 String 表示禁用状态的 Tips</pre>
      </td>
      <td>Function</td>
      <td>--</td>
      <td>--</td>
      <td>false</td>
    </tr>
    <tr>
      <td>allowHostListMissHostId</td>
      <td>允许 hostList 中缺少 hostId，通过 ip + cloudArea.id 回填选中主机</td>
      <td>Boolean</td>
      <td>--</td>
      <td>--</td>
      <td>false</td>
    </tr>
    <tr>
      <td>allowHostListOnlyIpv4</td>
      <td>允许 hostList 中缺少 hostId，通过 ip 回填选中主机</td>
      <td>Boolean</td>
      <td>--</td>
      <td>--</td>
      <td>false</td>
    </tr>
    <tr>
      <td>keepHostFieldOutput</td>
      <td>change 事件回调时输出完整的主机字段</td>
      <td>Boolean</td>
      <td>--</td>
      <td>--</td>
      <td>false</td>
    </tr>
    <tr>
      <td>completeHostList</td>
      <td>完整的主机列表，主机结果预览时直接渲染传入的值（部分使用场景只需要渲染提供的数据，不需要选择功能）</td>
      <td>Array</td>
      <td>--</td>
      <td>--</td>
      <td>false</td>
    </tr>
    <tr>
      <td>completeContainerList</td>
      <td>完整的容器列表，主机结果预览时直接渲染传入的值（部分使用场景只需要渲染提供的数据，不需要选择功能）</td>
      <td>Array</td>
      <td>--</td>
      <td>--</td>
      <td>false</td>
    </tr>
    <tr>
      <td>renderHostTag</td>
      <td>在主机 ip 后进行 tag 标记</td>
      <td>
<pre>
<code>
function(h, params: {data: HostInfo, source: 'select'|'selectPreview'|'view'}) {
  return h('bk-tag', '主机')
}
</code>
</pre>
      </td>
      <td>--</td>
      <td>--</td>
      <td>false</td>
    </tr>
    <tr>
      <td>renderNodeTag</td>
      <td>在动态节点后进行 tag 标记</td>
      <td>
<pre>
<code>
function(h, params: {data: NodeInfo, source: 'select'|'selectPreview'|'view'}) {
  return h('bk-tag', ' 节点')
}
</code>
</pre>
      </td>
      <td>--</td>
      <td>--</td>
      <td>false</td>
    </tr>
    <tr>
      <td>service</td>
      <td>覆盖组件初始的数据源配置</td>
      <td>Object</td>
      <td>
<pre>
<code>
{
  fetchTopologyHostCount:
  () => Promise.resolve([]),
  fetchTopologyHostsNodes:
  () => Promise.resolve([]),
  fetchTopologyHostIdsNodes:
  () => Promise.resolve([]),
}
</code>
</pre>
      </td>
      <td>--</td>
      <td>false</td>
    </tr>
    <tr>
      <td>config</td>
      <td>覆盖组件初始的基础配置</td>
      <td>Object</td>
      <td>
<pre>
<code>
{
  panelList: [
  'staticTopo',
  'dynamicTopo',
  'dynamicGroup',
  'manualInput'
  ],
  unqiuePanelValue: true,
  nameStyle: 'camelCase',
}
</code>
</pre>
      </td>
      <td>--</td>
      <td>false</td>
    </tr>
  </tbody>
</table>

#### 组件 Events
<table style='width:100%'>
    <thead>
    <tr>
        <th>事件名</th>
        <th>说明</th>
        <th>参数</th>
    </tr>
    </thead>
    <tbody>
    <tr>
      <td>change</td>
      <td>选择完成</td>
      <td>
<pre>
<code>
// 根据配置的 panelList 返回关联结果字段
{
  hostList: [
    {
      hostId: 1111,
      ip: 'x.x.x.x',
      cloudArea: {
        id: 2222,
        name: 'default area',
      }
    }
  ],
  nodeList: [
    {
      objectId: 'module',
      instanceId: 3333
    }
  ],
  dynamicGroupList: [
    {
      id: 4444
    }
  ],
  serviceTemplateList: [
    {
      id: 5555
    }
  ],
  setTemplateList: [
    {
      id: 666
    }
  ],
  // 容器动态分组（containerFilterList 面板）筛选条件
  containerFilterList: [
    {
      // kube 拓扑分组，多个分组为 OR 关系
      kubeTopoList: [
        {
          cluster: { id: 'BCS-K8S-00000' },
          namespace: { id: 'namespace-id' },
          workloads: [
            { id: 'workload-id', kind: 'Deployment' }
          ]
        }
      ],
      // 自定义属性条件，为空时为 null
      propConditions: [
        {
          field: 'pod_name',
          operator: 'contains',
          value: 'nginx'
        }
      ]
    }
  ]
}
</code>
</pre>
        </td>
    </tr>
    <tr>
      <td>close-dialog</td>
      <td>关闭 dialog</td>
      <td>--</td>
    </tr>
    <tr>
      <td>panel-change</td>
      <td>面板切换</td>
      <td>--</td>
    </tr>
    <tr>
      <td>output-field-change</td>
      <td>输出字段更改</td>
      <td>['ip', 'ipv6']</td>
    </tr>
  </tbody>
</table>

#### 实例方法
<table style='width:100%'>
  <thead>
    <tr>
        <th>方法名</th>
        <th>说明</th>
        <th>参数</th>
        <th>返回值</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>getHostList</td>
      <td>获取选择结果的主机列表</td>
      <td>--</td>
      <td>
<pre>
<code>
  Promise.resolve([
    {
      host_id: 1,
      ip: '1.1.1.1',
      ...
    }
  ])
</code>
</pre>
      </td>
    </tr>
    <tr>
      <td>getHostIpv4List</td>
      <td><pre>获取选择结果的主机 IPv4 列表</pre></td>
      <td>--</td>
      <td>
<pre>
<code>
[
  1.1.1.1,
  ...
]
</code>
</pre>
      </td>
    </tr>
    <tr>
      <td>getHostIpv6List</td>
      <td>获取选择结果的主机 IPv6 列表</td>
      <td>--</td>
      <td>
<pre>
<code>
[
  ee.fff.ddd.111,
  ...
]
</code>
</pre>
      </td>
    </tr>
    <tr>
      <td>getAbnormalHostIpv4List</td>
      <td>获取选择结果的异常主机 IPv4 列表</td>
      <td>--</td>
      <td>
<pre>
<code>
[
  2.2.2.2,
  ...
]
</code>
</pre>
      </td>
    </tr>
    <tr>
      <td>getAbnormalHostIpv6List</td>
      <td>获取选择结果的异常主机 IPv6 列表</td>
      <td>--</td>
      <td>
<pre>
<code>
[
  ee.fff.ddd.222,
  ...
]
</code>
</pre>
      </td>
    </tr>
    <tr>
      <td>resetValue</td>
      <td>重置选择结果</td>
      <td>--</td>
      <td>--</td>
    </tr>
    <tr>
      <td>refresh</td>
      <td>刷新结果展示面板的主机状态</td>
      <td>--</td>
      <td>--</td>
    </tr>
    <tr>
      <td>collapseToggle</td>
      <td>切换结果展示面板的折叠状态</td>
      <td>
<pre>
<code>
true // 展开所有
false // 收起所有
[ // 展开指定
  'host',
  'node',
  'dynamicGroup',
  'setTemplate',
  'serviceTemplate'
]
</code>
</pre>
      </td>
      <td>--</td>
    </tr>
    <tr>
      <td>removeInvalidData</td>
      <td>移除 view 面板无效数据</td>
      <td>--</td>
      <td>--</td>
    </tr>
  </tbody>
</table>

#### 容器动态分组（containerFilterList）

在 `panelList` 中加入 `containerFilterList` 即可开启「容器 - 动态分组」面板。用户可以按 kube 拓扑（集群 / 命名空间 / 工作负载）与自定义属性组合出动态筛选条件，选择结果不落到具体容器 id，而是以筛选条件的形式保存，预览时实时命中容器列表。

##### 数据结构

`value.containerFilterList` 与 `change` 事件回调中的 `containerFilterList` 结构一致。选择器只编辑单个筛选组，因此列表最多包含一项（取第一项进行回填），整体清空时值为 `[]`。

```javascript
containerFilterList: [
  {
    // kube 拓扑分组列表，多个分组之间为 OR 关系
    kubeTopoList: [
      {
        // 集群，单选，为空时为 null
        cluster: { id: 'BCS-K8S-00000' },
        // 命名空间，单选，依赖 cluster，为空时为 null
        namespace: { id: 'namespace-id' },
        // 工作负载，多选，依赖 namespace，为空时为 []
        workloads: [
          { id: 'workload-id', kind: 'Deployment' }
        ]
      }
    ],
    // 自定义属性条件列表，为空时为 null
    propConditions: [
      {
        field: 'pod_name',       // 字段名，来自 fetchDynamicContainerFieldMetadata
        operator: 'contains',    // 字段名包含 id 时为 'equal'，否则为 'contains'
        value: 'nginx,web'       // 多个值以英文逗号拼接
      }
    ]
  }
]
```

> 说明：当 `kubeTopoList` 中所有分组的 `cluster` / `namespace` / `workloads` 均为空，且 `propConditions` 为空时，整个筛选条件视为无效，`containerFilterList` 会被置为 `[]`。

##### 数据源配置

`containerFilterList` 面板依赖以下 service 方法（返回 Promise）：

<table style='width:100%'>
  <thead>
    <tr>
      <th>方法名</th>
      <th>说明</th>
      <th>参数</th>
      <th>返回值</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>fetchTopologyContainerWithCount</td>
      <td>拉取容器拓扑树（与 containerStaticTopo 面板共用），用于生成集群 / 命名空间 / 工作负载的下拉选项</td>
      <td>拓扑查询参数</td>
      <td>容器拓扑树数据</td>
    </tr>
    <tr>
      <td>fetchDynamicContainerFieldMetadata</td>
      <td>拉取自定义属性动态筛选的可选字段元数据</td>
      <td>--</td>
      <td>
<pre>
<code>
{
  fields: [
    {
      field: 'pod_name',
      display_name: 'Pod 名称',
      allowed_operators: ['contains', 'equal']
    }
  ]
}
</code>
</pre>
      </td>
    </tr>
    <tr>
      <td>fetchDynamicContainerPreview</td>
      <td>根据筛选条件预览命中的容器列表（点击「预览」或结果面板行时调用）</td>
      <td>
<pre>
<code>
{
  filter: {
    kubeTopoList: [...],
    propConditions: [...] | null
  },
  pageSize: 10,
  start: 0
}
</code>
</pre>
      </td>
      <td>
<pre>
<code>
{
  total: 100,
  page_size: 10,
  start: 0,
  data: [
    {
      id: 1,
      name: 'pod-name',
      pod_name: 'pod-name',
      pod_labels: {},
      uid: 'xxxx',
      node_host_id: 111,
      node_ip: 'x.x.x.x'
    }
  ]
}
</code>
</pre>
      </td>
    </tr>
    <tr>
      <td>fetchContainerNodesNames</td>
      <td>批量查询容器拓扑节点名称（结果查看态根据节点 id 生成筛选条件名称）</td>
      <td>
<pre>
<code>
{
  nodes: [
    // type: 'cluster' | 'namespace' | 工作负载 kind（如 'Deployment'）
    { type: 'cluster', id: 'BCS-K8S-00000' },
    { type: 'namespace', id: 'namespace-id' },
    { type: 'Deployment', id: 'workload-id' }
  ]
}
</code>
</pre>
      </td>
      <td>
<pre>
<code>
{
  nodes: [
    { type: 'cluster', id: 'BCS-K8S-00000', name: '集群名称' },
    { type: 'namespace', id: 'namespace-id', name: '命名空间名称' },
    { type: 'Deployment', id: 'workload-id', name: '工作负载名称' }
  ]
}
</code>
</pre>
      </td>
    </tr>
  </tbody>
</table>