You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJs开发类JIRA高级搜索遇问题,求Atlassian官方参考

JIRA高级搜索组件实现方案(对齐Atlassian官方设计)

核心问题拆解

你的问题根源在于没有给搜索标签建立统一的数据模型,每个标签单独处理导致状态和查询逻辑分散。Atlassian的JIRA高级搜索核心是用标准化的元数据模型统一管理所有搜索条件,实现标签、交互、查询字符串的双向同步。

官方实现思路提炼

1. 统一标签数据模型

所有搜索标签基于同一结构定义,包含核心属性:

  • type: 对应JQL字段(如assignee/project/status)
  • value: 字段值(如用户名/项目名)
  • expandable: 是否可展开关联列表
  • relatedTypes: 关联的其他字段类型(如用户关联project/status)

示例TypeScript接口:

interface SearchTag {
  type: 'assignee' | 'project' | 'status' | string;
  value: string;
  expandable: boolean;
  relatedTypes: string[];
}

2. 集中式状态管理

用React状态(或状态管理工具)统一维护以下核心状态:

  • 已选择的标签列表
  • 当前展开的标签(用于展示关联列表)
  • 选中的关联类型(用于加载对应数据)
  • 实时生成的JQL查询字符串

3. 统一交互处理逻辑

不再为每个标签单独写点击事件,而是用一个通用函数处理所有标签交互:

  • 点击可展开标签时,触发关联类型选择面板
  • 选择关联类型后,加载对应关联数据(如用户的项目列表)
  • 选择关联数据后,生成新的标签并添加到列表,同时更新JQL

4. 双向同步查询字符串

所有标签操作直接映射为JQL片段,确保标签集合和查询字符串完全同步:

  • 添加标签:拼接对应的JQL条件
  • 删除标签:移除对应条件
  • 修改标签:更新对应条件

Next.js 实现示例

'use client';
import { useState } from 'react';

interface SearchTag {
  type: 'assignee' | 'project' | 'status';
  value: string;
  expandable: boolean;
  relatedTypes: string[];
}

export default function AdvancedSearch() {
  const [tags, setTags] = useState<SearchTag[]>([]);
  const [expandedTag, setExpandedTag] = useState<SearchTag | null>(null);
  const [activeRelatedType, setActiveRelatedType] = useState<string | null>(null);

  // 初始用户选择生成标签
  const handleUserSelection = (username: string) => {
    const userTag: SearchTag = {
      type: 'assignee',
      value: username,
      expandable: true,
      relatedTypes: ['project', 'status']
    };
    setTags(prev => [...prev, userTag]);
  };

  // 通用标签点击处理
  const handleTagClick = (tag: SearchTag) => {
    if (tag.expandable) {
      setExpandedTag(tag);
      setActiveRelatedType(null);
    }
  };

  // 选择关联类型加载对应列表
  const handleRelatedTypePick = (type: string) => {
    setActiveRelatedType(type);
    // 这里可调用API加载该标签对应的关联数据,比如用户的项目列表
  };

  // 生成实时JQL查询
  const buildJQL = () => {
    return tags.map(tag => `${tag.type} = "${tag.value}"`).join(' AND ');
  };

  return (
    <div className="p-4 border rounded-lg">
      {/* 初始用户选择面板 */}
      <div className="mb-4">
        <h3 className="font-medium mb-2">选择经办人</h3>
        <button onClick={() => handleUserSelection('john_doe')} className="mr-2 px-3 py-1 bg-blue-500 text-white rounded">
          John Doe
        </button>
        <button onClick={() => handleUserSelection('jane_smith')} className="px-3 py-1 bg-blue-500 text-white rounded">
          Jane Smith
        </button>
      </div>

      {/* 已选标签栏 */}
      <div className="flex flex-wrap gap-2 mb-4">
        {tags.map((tag, idx) => (
          <div
            key={idx}
            className="px-3 py-1 bg-gray-100 rounded flex items-center cursor-pointer"
            onClick={() => handleTagClick(tag)}
          >
            <span className="mr-1">{tag.type}:</span>
            <span>{tag.value}</span>
            {tag.expandable && <span className="ml-1">▼</span>}
          </div>
        ))}
      </div>

      {/* 关联类型选择面板 */}
      {expandedTag && !activeRelatedType && (
        <div className="mb-4 p-2 border rounded">
          <p className="font-medium mb-2">查看{expandedTag.value}的关联项:</p>
          {expandedTag.relatedTypes.map(type => (
            <button
              key={type}
              onClick={() => handleRelatedTypePick(type)}
              className="mr-2 px-2 py-1 bg-gray-200 rounded"
            >
              {type === 'project' ? '所属项目' : '工单状态'}
            </button>
          ))}
        </div>
      )}

      {/* 关联列表展示 */}
      {activeRelatedType && (
        <div className="mb-4 p-2 border rounded">
          <p className="font-medium mb-2">{expandedTag?.value}的{activeRelatedType}列表:</p>
          {/* 这里渲染加载后的关联数据,比如项目列表 */}
          <div className="space-y-1">
            <div className="px-2 py-1 bg-gray-50 rounded">项目A</div>
            <div className="px-2 py-1 bg-gray-50 rounded">项目B</div>
          </div>
        </div>
      )}

      {/* JQL预览 */}
      <div className="p-2 bg-gray-50 rounded">
        <span className="font-medium">当前JQL:</span>
        <code className="ml-2">{buildJQL() || '无查询条件'}</code>
      </div>
    </div>
  );
}

关键优化点

  • 所有标签基于统一模型,避免逻辑分散
  • 用通用函数处理交互,减少重复代码
  • 标签与JQL双向同步,确保查询逻辑一致
  • 关联列表的展开依赖标签的relatedTypes配置,可扩展性强

内容的提问来源于stack exchange,提问作者fullsnackdev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 01:57:50