let fetchedData = null; let extractedSprintId = null; let extractedSprintName = null; let sprintMappings = []; let currentRepoName = null; let existingMappings = null; //默认的Gitea标签建议 const defaultLabels = { 'High': 'high', 'Highest': 'highest', 'Medium': 'medium', 'Low': 'low', 'Lowest': 'lowest', 'Bug': 'bug', 'Story': 'story', 'Task': 'task', 'New Feature': 'feature' }; //为Jira字段添加新的Gitea标签输入框 function addLabel(type, jiraId, jiraName) { const containerClass = type === 'priority' ? 'priority-labels-container' : 'type-labels-container'; const dataType = type === 'priority' ? 'priority' : 'issuetype'; const removeFunc = type === 'priority' ? 'removePriorityLabelInput' : 'removeTypeLabelInput'; const placeholder = type === 'priority' ? 'urgent' : 'defect'; const container = document.querySelector(`.${containerClass}[data-jira-id="${jiraId}"]`); if (!container) return; const newInput = document.createElement('div'); newInput.className = 'flex items-center gap-2'; newInput.innerHTML = ` Gitea标签: `; container.appendChild(newInput); //为新输入框绑定实时更新事件 const input = newInput.querySelector('input'); if (input) { input.addEventListener('input', updatePreview); } updateLabelDeleteButtons(type, jiraId); updatePreview(); } //删除标签输入框 function removeLabelInput(type, btn) { const containerClass = type === 'priority' ? 'priority-labels-container' : 'type-labels-container'; const container = btn.closest(`.${containerClass}`); const jiraId = container.dataset.jiraId; btn.closest('.flex').remove(); updateLabelDeleteButtons(type, jiraId); updatePreview(); } //更新标签删除按钮的可见性 function updateLabelDeleteButtons(type, jiraId) { const containerClass = type === 'priority' ? 'priority-labels-container' : 'type-labels-container'; const removeFunc = type === 'priority' ? 'removePriorityLabelInput' : 'removeTypeLabelInput'; const container = document.querySelector(`.${containerClass}[data-jira-id="${jiraId}"]`); if (!container) return; const inputs = container.querySelectorAll('.flex'); inputs.forEach(input => { const deleteBtn = input.querySelector(`button[onclick*="${removeFunc}"]`); if (deleteBtn) { if (inputs.length > 1) { deleteBtn.classList.remove('opacity-0', 'pointer-events-none'); } else { deleteBtn.classList.add('opacity-0', 'pointer-events-none'); } } }); } //包装函数:保持HTML兼容性 function addPriorityLabel(jiraId, jiraName) { addLabel('priority', jiraId, jiraName); } function addTypeLabel(jiraId, jiraName) { addLabel('type', jiraId, jiraName); } function removePriorityLabelInput(btn) { removeLabelInput('priority', btn); } function removeTypeLabelInput(btn) { removeLabelInput('type', btn); } function updatePriorityLabelDeleteButtons(jiraId) { updateLabelDeleteButtons('priority', jiraId); } function updateTypeLabelDeleteButtons(jiraId) { updateLabelDeleteButtons('type', jiraId); } //页面加载时恢复缓存的配置 window.addEventListener('DOMContentLoaded', () => { loadGlobalSettings(); }); //加载全局设置 function loadGlobalSettings() { const savedConfig = localStorage.getItem('jiraGlobalConfig'); if (savedConfig) { try { const config = JSON.parse(savedConfig); if (config.jiraUrl) document.getElementById('settingsJiraUrl').value = config.jiraUrl; if (config.jiraUser) document.getElementById('settingsJiraUser').value = config.jiraUser; if (config.jiraToken) document.getElementById('settingsJiraToken').value = config.jiraToken; } catch (e) { console.error('Failed to load saved config:', e); } } } //打开设置窗口 function openSettings() { loadGlobalSettings(); document.getElementById('settingsModal').classList.remove('hidden'); } //关闭设置窗口 function closeSettings() { document.getElementById('settingsModal').classList.add('hidden'); } //保存全局设置 function saveSettings() { const config = { jiraUrl: document.getElementById('settingsJiraUrl').value.trim(), jiraUser: document.getElementById('settingsJiraUser').value.trim(), jiraToken: document.getElementById('settingsJiraToken').value.trim() }; if (!config.jiraUrl) { alert('请填写Jira地址'); return; } localStorage.setItem('jiraGlobalConfig', JSON.stringify(config)); closeSettings(); alert('全局设置已保存'); } //获取全局设置 function getGlobalSettings() { const savedConfig = localStorage.getItem('jiraGlobalConfig'); if (savedConfig) { try { return JSON.parse(savedConfig); } catch (e) { return {}; } } return {}; } //加载现有映射配置 async function loadExistingMappings() { const btn = document.getElementById('loadBtn'); const errBox = document.getElementById('step0Error'); btn.disabled = true; btn.innerText = '加载中...'; errBox.classList.add('hidden'); try { const res = await fetch('/editor/api/mappings'); const data = await res.json(); if (!data.success) { throw new Error(data.error); } existingMappings = data.data; const repos = Object.keys(existingMappings.repositories || {}); if (repos.length === 0) { throw new Error('没有找到现有配置,请新建仓库配置'); } //显示仓库列表 const container = document.getElementById('repoListContainer'); const list = document.getElementById('repoList'); list.innerHTML = repos.map(repo => `
${repo} (点击编辑)
`).join(''); container.classList.remove('hidden'); } catch (e) { showStep0Error(e.message); } finally { btn.disabled = false; btn.innerText = '加载现有配置'; } } //选择要编辑的仓库 async function selectRepo(repoName) { currentRepoName = repoName; const config = existingMappings.repositories[repoName]; //预填充基本字段 document.getElementById('currentRepoName').innerText = repoName; document.getElementById('projectKey').value = config.jira?.projectKey || ''; //预填充 Sprint 映射 if (config.sprints) { sprintMappings = Object.entries(config.sprints).map(([milestone, sprintId]) => ({ milestone, sprintId, sprintName: '' })); renderSprintList(); } //隐藏 Step 0,显示 Step 1 document.getElementById('step0').classList.add('hidden'); document.getElementById('step1').classList.remove('hidden'); document.getElementById('step1').classList.add('step-active'); //如果有项目Key,自动扫描并预填充配置 if (config.jira?.projectKey) { const settings = getGlobalSettings(); if (settings.jiraUrl && (settings.jiraToken || settings.jiraUser)) { await scanJiraAndLoadConfig(config); } } } //新建仓库配置 function createNewMapping() { //清空之前的状态,避免残留数据 sprintMappings = []; fetchedData = null; document.getElementById('repoListContainer').classList.add('hidden'); document.getElementById('newRepoContainer').classList.remove('hidden'); } //确认新仓库名称 function confirmNewRepo() { const repoName = document.getElementById('newRepoName').value.trim(); if (!repoName) { showStep0Error('请输入仓库名称'); return; } if (!/^[\w-]+\/[\w-]+$/.test(repoName)) { showStep0Error('仓库名称格式不正确,应为: owner/repo'); return; } //清空之前的状态 sprintMappings = []; fetchedData = null; currentRepoName = repoName; document.getElementById('currentRepoName').innerText = repoName; //隐藏Step 0,显示Step 1 document.getElementById('step0').classList.add('hidden'); document.getElementById('step1').classList.remove('hidden'); document.getElementById('step1').classList.add('step-active'); } //显示错误信息 function showErrorMsg(elementId, msg) { const el = document.getElementById(elementId); el.innerText = msg; el.classList.remove('hidden'); } function showStep0Error(msg) { showErrorMsg('step0Error', msg); } function backToStart() { //重置状态 currentRepoName = null; fetchedData = null; sprintMappings = []; //清空UI显示 document.getElementById('sprintListContainer').classList.add('hidden'); document.getElementById('sprintList').innerHTML = ''; //隐藏所有步骤 document.getElementById('step1').classList.add('hidden'); document.getElementById('step2').classList.add('hidden'); document.getElementById('step3').classList.add('hidden'); //显示 Step 0 document.getElementById('step0').classList.remove('hidden'); document.getElementById('step0').classList.add('step-active'); //重置输入框 document.getElementById('repoListContainer').classList.add('hidden'); document.getElementById('newRepoContainer').classList.add('hidden'); document.getElementById('newRepoName').value = ''; } //删除仓库 async function deleteRepo(repoName) { if (!confirm(`确定要删除仓库 "${repoName}" 的配置吗?\n\n此操作不可恢复!`)) { return; } try { const res = await fetch('/editor/api/mappings/' + encodeURIComponent(repoName), { method: 'DELETE' }); const data = await res.json(); if (!data.success) { throw new Error(data.error); } alert('仓库配置已删除'); //重新加载仓库列表 await loadExistingMappings(); } catch (e) { alert('删除失败: ' + e.message); } } //打开改名模态窗口 function openRenameModal(repoName) { document.getElementById('renameOldName').value = repoName; document.getElementById('renameNewName').value = repoName; document.getElementById('renameError').classList.add('hidden'); document.getElementById('renameModal').classList.remove('hidden'); //聚焦到新名称输入框并选中文本 setTimeout(() => { const input = document.getElementById('renameNewName'); input.focus(); input.select(); }, 100); } //关闭改名模态窗口 function closeRenameModal() { document.getElementById('renameModal').classList.add('hidden'); } function showRenameError(msg) { showErrorMsg('renameError', msg); } //确认改名 async function confirmRename() { const oldName = document.getElementById('renameOldName').value.trim(); const newName = document.getElementById('renameNewName').value.trim(); const btn = document.getElementById('renameConfirmBtn'); //验证 if (!newName) { showRenameError('请输入新名称'); return; } if (newName === oldName) { showRenameError('新名称与当前名称相同'); return; } if (!/^[\w-]+\/[\w-]+$/.test(newName)) { showRenameError('仓库名称格式不正确,应为: owner/repo'); return; } //检查新名称是否已存在 if (existingMappings && existingMappings.repositories && existingMappings.repositories[newName]) { showRenameError('新名称已存在,请使用其他名称'); return; } btn.disabled = true; btn.innerText = '改名中...'; try { const res = await fetch('/editor/api/mappings/rename', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ oldName: oldName, newName: newName }) }); const data = await res.json(); if (!data.success) { throw new Error(data.error); } alert('仓库已成功改名'); closeRenameModal(); //重新加载仓库列表 await loadExistingMappings(); } catch (e) { showRenameError('改名失败: ' + e.message); } finally { btn.disabled = false; btn.innerText = '确认改名'; } } //scanJiraAndLoadConfig 已合并到 scanJira(existingConfig) async function scanJiraAndLoadConfig(existingConfig) { return scanJira(existingConfig); } //恢复标签配置到UI function restoreLabelConfig(config, type) { if (!config) return; const containerClass = type === 'priority' ? 'priority-labels-container' : 'type-labels-container'; const dataType = type === 'priority' ? 'priority' : 'issuetype'; const removeFunc = type === 'priority' ? 'removePriorityLabelInput' : 'removeTypeLabelInput'; //按JiraID 分组Gitea标签 const byJiraId = {}; Object.entries(config).forEach(([label, jiraId]) => { if (!byJiraId[jiraId]) byJiraId[jiraId] = []; byJiraId[jiraId].push(label); }); //为每个Jira字段填充标签 Object.entries(byJiraId).forEach(([jiraId, labels]) => { const container = document.querySelector(`.${containerClass}[data-jira-id="${jiraId}"]`); if (!container) return; container.innerHTML = ''; labels.forEach(label => { const inputDiv = document.createElement('div'); inputDiv.className = 'flex items-center gap-2'; inputDiv.innerHTML = ` Gitea标签: `; container.appendChild(inputDiv); }); }); } //将现有配置恢复到UI function restoreConfigToUI(config) { restoreLabelConfig(config.priorities, 'priority'); restoreLabelConfig(config.types, 'type'); if (config.jira?.defaultType) { document.getElementById('defaultTypeSelect').value = config.jira.defaultType; } if (config.transitions) { if (config.transitions.close) document.getElementById('transClose').value = config.transitions.close; if (config.transitions.reopen) document.getElementById('transReopen').value = config.transitions.reopen; if (config.transitions.in_progress) document.getElementById('transProgress').value = config.transitions.in_progress; } updatePreview(); } async function fetchSprintId() { const btn = document.getElementById('fetchSprintBtn'); const resultBox = document.getElementById('sprintResult'); const errBox = document.getElementById('sprintError'); const milestoneBox = document.getElementById('sprintMilestone'); const settings = getGlobalSettings(); const baseUrl = settings.jiraUrl; const username = settings.jiraUser; const token = settings.jiraToken; const issueKey = document.getElementById('issueKey').value.trim().toUpperCase(); if (!baseUrl) { showSprintError("请先在全局设置中配置Jira地址"); return; } if (!issueKey) { showSprintError("请填写工单 Key"); return; } btn.disabled = true; btn.innerText = "提取中..."; errBox.classList.add('hidden'); resultBox.innerHTML = ''; try { //构造认证头(支持 PAT 和 Basic Auth) let authHeader; if (token && !username) { //如果只有 token 没有 username,使用 Bearer 认证(PAT) authHeader = 'Bearer ' + token; } else if (username && token) { //如果有 username 和 token/password,使用 Basic 认证 authHeader = 'Basic ' + btoa(username + ':' + token); } else { throw new Error('请填写认证信息(PAT 或 用户名+密码)'); } //调用JiraAPI const apiUrl = `${baseUrl}/rest/api/2/issue/${issueKey}`; const res = await fetch('/editor/api/proxy-jira', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: apiUrl, auth: authHeader }) }); const data = await res.json(); if (!data.success) { throw new Error(data.error || '无法获取工单信息'); } const issue = data.data; const sprintField = issue.fields?.customfield_10105; if (!sprintField || sprintField.length === 0) { throw new Error('该工单没有关联 Sprint (customfield_10105 为空)'); } //解析 Sprint 字符串,提取 id const sprintStr = sprintField[0]; const idMatch = sprintStr.match(/id=(\d+)/); const nameMatch = sprintStr.match(/name=([^,\]]+)/); if (!idMatch) { throw new Error('无法从 Sprint 字段中解析 ID'); } extractedSprintId = idMatch[1]; const sprintName = nameMatch ? nameMatch[1] : 'Unknown'; extractedSprintName = sprintName; //显示成功结果 resultBox.innerHTML = `
Sprint ID: ${extractedSprintId} (${sprintName})
`; //显示里程碑名称输入框,并预填充Sprint名称 milestoneBox.classList.remove('hidden'); document.getElementById('milestoneName').value = sprintName; } catch (e) { showSprintError(e.message); extractedSprintId = null; milestoneBox.classList.add('hidden'); } finally { btn.disabled = false; btn.innerText = "提取 Sprint"; } } function showSprintError(msg) { showErrorMsg('sprintError', msg); } function addSprintMapping() { const milestoneName = document.getElementById('milestoneName').value.trim(); if (!milestoneName) { showSprintError('请填写里程碑名称'); return; } if (!extractedSprintId) { showSprintError('请先提取 Sprint ID'); return; } //检查是否已存在相同的里程碑名称 const existing = sprintMappings.find(m => m.milestone === milestoneName); if (existing) { if (!confirm(`里程碑 "${milestoneName}" 已存在(Sprint ID: ${existing.sprintId}),是否覆盖?`)) { return; } //移除旧的 sprintMappings = sprintMappings.filter(m => m.milestone !== milestoneName); } //添加新映射 sprintMappings.push({ milestone: milestoneName, sprintId: parseInt(extractedSprintId), sprintName: extractedSprintName }); //更新显示 renderSprintList(); //清空输入 document.getElementById('issueKey').value = ''; document.getElementById('milestoneName').value = ''; document.getElementById('sprintResult').innerHTML = ''; document.getElementById('sprintMilestone').classList.add('hidden'); document.getElementById('sprintError').classList.add('hidden'); extractedSprintId = null; extractedSprintName = null; //更新预览 updatePreview(); } function renderSprintList() { const container = document.getElementById('sprintListContainer'); const list = document.getElementById('sprintList'); if (sprintMappings.length === 0) { container.classList.add('hidden'); return; } container.classList.remove('hidden'); list.innerHTML = sprintMappings.map((m, index) => `
${m.milestone} Sprint ID: ${m.sprintId} (${m.sprintName})
`).join(''); } function removeSprintMapping(index) { sprintMappings.splice(index, 1); renderSprintList(); updatePreview(); } async function saveToFile() { if (!currentRepoName) { alert('错误:未设置仓库名称'); return; } if (!fetchedData) { alert('请先扫描Jira项目信息'); return; } //尝试从JSON预览读取配置 const jsonPreview = document.getElementById('jsonPreview'); const jsonError = document.getElementById('jsonError'); let config; if (jsonPreview.value.trim()) { try { config = JSON.parse(jsonPreview.value); jsonError.classList.add('hidden'); } catch (e) { jsonError.textContent = `JSON格式错误: ${e.message}`; jsonError.classList.remove('hidden'); return; } } else { //如果JSON预览为空,从表单收集配置 config = buildConfigFromForm(); } //更新JSON预览 updatePreview(); //保存到服务器 try { const res = await fetch('/editor/api/mappings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ repoName: currentRepoName, config: config }) }); const data = await res.json(); if (!data.success) { throw new Error(data.error); } //显示成功信息 document.getElementById('saveResult').innerHTML = `

配置已成功保存!

仓库: ${currentRepoName}

文件: mappings.json

`; document.getElementById('step2').classList.add('hidden'); document.getElementById('step3').classList.remove('hidden'); document.getElementById('step3').scrollIntoView({ behavior: "smooth" }); } catch (e) { alert('保存失败: ' + e.message); } } //从表单构建配置对象 function buildConfigFromForm() { const projectId = fetchedData.project.id; const projectKey = fetchedData.project.key; const defaultType = document.getElementById('defaultTypeSelect').value; //收集优先级(支持多个Gitea标签映射到同一个Jira优先级) const priorities = {}; document.querySelectorAll('input[data-type="priority"]').forEach(input => { const val = input.value.trim(); if (val) { //检查是否已存在相同的Gitea标签 if (priorities[val] && priorities[val] !== input.dataset.jiraId) { console.warn(`警告:Gitea标签 "${val}" 被映射到多个Jira优先级,将使用最后一个`); } priorities[val] = input.dataset.jiraId; } }); //收集类型(支持多个Gitea标签映射到同一个Jira类型) const types = {}; document.querySelectorAll('input[data-type="issuetype"]').forEach(input => { const val = input.value.trim(); if (val) { //检查是否已存在相同的Gitea标签 if (types[val] && types[val] !== input.dataset.jiraId) { console.warn(`警告:Gitea标签 "${val}" 被映射到多个Jira类型,将使用最后一个`); } types[val] = input.dataset.jiraId; } }); //收集流转 const transitions = {}; const closeId = document.getElementById('transClose').value; const reopenId = document.getElementById('transReopen').value; const progressId = document.getElementById('transProgress').value; if (closeId) transitions['close'] = closeId; if (reopenId) transitions['reopen'] = reopenId; if (progressId) transitions['in_progress'] = progressId; //生成 Sprint 配置对象 const sprints = {}; sprintMappings.forEach(m => { sprints[m.milestone] = m.sprintId; }); //构建配置对象 const jiraConfig = { projectId: projectId, projectKey: projectKey, sprintField: "customfield_10105" }; //只在有值时添加defaultType if (defaultType) { jiraConfig.defaultType = defaultType; } return { jira: jiraConfig, priorities: priorities, types: types, transitions: transitions, sprints: sprints }; } //更新JSON预览 function updatePreview() { if (!fetchedData) return; const config = buildConfigFromForm(); const jsonPreview = document.getElementById('jsonPreview'); const jsonError = document.getElementById('jsonError'); try { jsonPreview.value = JSON.stringify(config, null, 2); jsonError.classList.add('hidden'); } catch (e) { jsonError.textContent = `生成预览失败: ${e.message}`; jsonError.classList.remove('hidden'); } } async function scanJira(existingConfig = null) { const btn = document.getElementById('scanBtn'); const errBox = document.getElementById('scanError'); const step2 = document.getElementById('step2'); const settings = getGlobalSettings(); const baseUrl = settings.jiraUrl; const username = settings.jiraUser; const token = settings.jiraToken; const projectKey = document.getElementById('projectKey').value.trim(); if (!baseUrl) { showError("请先在全局设置中配置Jira连接信息"); return; } if (!projectKey) { showError("请输入项目 Key"); return; } btn.disabled = true; btn.classList.add('opacity-75'); document.getElementById('scanBtnText').innerText = existingConfig ? "加载中..." : "扫描中..."; errBox.classList.add('hidden'); if (!existingConfig) step2.classList.add('hidden'); try { const res = await fetch('/editor/api/scan', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ baseUrl, projectKey, auth: { username, password: token, token: token.length > 30 ? token : null } }) }); const json = await res.json(); if (!json.success) { throw new Error(json.error); } fetchedData = json.data; renderMappingUI(json.data); if (existingConfig) { restoreConfigToUI(existingConfig); } step2.classList.remove('hidden'); setTimeout(() => step2.scrollIntoView({ behavior: "smooth" }), 100); } catch (e) { showError(e.message); } finally { btn.disabled = false; btn.classList.remove('opacity-75'); document.getElementById('scanBtnText').innerText = "开始扫描"; } } function showError(msg) { showErrorMsg('scanError', msg); } //手动扫描工单的 Transition async function scanTransitions() { const btn = document.getElementById('scanTransBtn'); const resultBox = document.getElementById('transResult'); const errBox = document.getElementById('transError'); const issueKey = document.getElementById('transIssueKey').value.trim().toUpperCase(); const settings = getGlobalSettings(); const baseUrl = settings.jiraUrl; const username = settings.jiraUser; const token = settings.jiraToken; if (!baseUrl) { showTransError("请先在全局设置中配置Jira地址"); return; } if (!issueKey) { showTransError("请输入工单 Key"); return; } btn.disabled = true; btn.innerText = "扫描中..."; errBox.classList.add('hidden'); resultBox.innerHTML = ''; try { //构造认证头 let authHeader; if (token && !username) { authHeader = 'Bearer ' + token; } else if (username && token) { authHeader = 'Basic ' + btoa(username + ':' + token); } else { throw new Error('请填写认证信息(PAT 或 用户名+密码)'); } //调用 Jira API 获取 transitions const apiUrl = `${baseUrl}/rest/api/2/issue/${issueKey}/transitions`; const res = await fetch('/editor/api/proxy-jira', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: apiUrl, auth: authHeader }) }); const data = await res.json(); if (!data.success) { throw new Error(data.error || '无法获取流转信息'); } const transitions = data.data.transitions || []; if (transitions.length === 0) { resultBox.innerHTML = '该工单当前状态没有可用的流转'; return; } //追加到下拉菜单(去重) const transSelects = ['transClose', 'transReopen', 'transProgress']; let addedCount = 0; transitions.forEach(t => { transSelects.forEach(selectId => { const sel = document.getElementById(selectId); //检查是否已存在(按 ID 去重) const exists = Array.from(sel.options).some(opt => opt.value === t.id); if (!exists) { const opt = document.createElement('option'); opt.value = t.id; opt.text = `${t.name} (To: ${t.to?.name || 'Unknown'})`; sel.add(opt); addedCount++; } }); }); //显示结果 const newTransNames = transitions.map(t => `${t.name} → ${t.to?.name || '?'}`).join(', '); resultBox.innerHTML = `
扫描到 ${transitions.length} 个流转: ${newTransNames}
`; //同时更新 fetchedData 以保持一致 if (fetchedData) { transitions.forEach(t => { const exists = fetchedData.transitions.some(existing => existing.id === t.id); if (!exists) { fetchedData.transitions.push({ id: t.id, name: t.name, to: t.to?.name || 'Unknown' }); } }); } } catch (e) { showTransError(e.message); } finally { btn.disabled = false; btn.innerText = "扫描流转"; } } function showTransError(msg) { showErrorMsg('transError', msg); } function renderMappingUI(data) { //渲染优先级 const pContainer = document.getElementById('priorityContainer'); pContainer.innerHTML = ''; data.priorities.forEach(p => { let guess = ""; for (let k in defaultLabels) { if (p.name.includes(k)) guess = defaultLabels[k]; } const itemDiv = document.createElement('div'); itemDiv.className = 'bg-gray-50 p-3 rounded border'; itemDiv.dataset.jiraId = p.id; itemDiv.innerHTML = `
${p.name}
Gitea标签:
`; pContainer.appendChild(itemDiv); }); //渲染类型 const tContainer = document.getElementById('typeContainer'); const defaultSelect = document.getElementById('defaultTypeSelect'); tContainer.innerHTML = ''; defaultSelect.innerHTML = ''; //添加"不设置"选项 const emptyOption = document.createElement('option'); emptyOption.value = ''; emptyOption.text = '(不设置默认类型)'; defaultSelect.add(emptyOption); data.types.forEach((t, index) => { let guess = ""; for (let k in defaultLabels) { if (t.name.includes(k)) guess = defaultLabels[k]; } const itemDiv = document.createElement('div'); itemDiv.className = 'bg-gray-50 p-3 rounded border'; itemDiv.dataset.jiraId = t.id; itemDiv.innerHTML = `
${t.name}
Gitea标签:
`; tContainer.appendChild(itemDiv); const option = document.createElement('option'); option.value = t.id; option.text = t.name; defaultSelect.add(option); if (t.name.toLowerCase() === 'bug' || t.name.toLowerCase() === 'task') defaultSelect.value = t.id; }); //渲染流转 const transSelects = ['transClose', 'transReopen', 'transProgress']; const warningBox = document.getElementById('transWarning'); if (data.warning) { warningBox.innerText = data.warning; warningBox.classList.remove('hidden'); } else { warningBox.classList.add('hidden'); } transSelects.forEach(id => { const sel = document.getElementById(id); sel.innerHTML = ''; data.transitions.forEach(t => { const opt = document.createElement('option'); opt.value = t.id; opt.text = `${t.name} (To: ${t.to})`; sel.add(opt); const lowerName = t.name.toLowerCase(); const lowerTo = t.to.toLowerCase(); if (id === 'transClose' && (lowerName.includes('close') || lowerName.includes('done') || lowerName.includes('complete'))) sel.value = t.id; if (id === 'transReopen' && (lowerName.includes('reopen') || lowerName.includes('open'))) sel.value = t.id; if (id === 'transProgress' && (lowerName.includes('progress') || lowerName.includes('process') || lowerTo.includes('progress'))) sel.value = t.id; }); }); //为所有输入框添加input事件,自动更新预览(使用input替代change以实现实时更新) document.querySelectorAll('input[data-type]').forEach(el => { el.addEventListener('input', updatePreview); }); document.querySelectorAll('select').forEach(el => { el.addEventListener('change', updatePreview); }); //初始化预览 updatePreview(); }