init
This commit is contained in:
137
sidepanel.js
Normal file
137
sidepanel.js
Normal file
@@ -0,0 +1,137 @@
|
||||
// DOM Elements
|
||||
const apiKeyInput = document.getElementById('apiKey');
|
||||
const scriptUrlInput = document.getElementById('scriptUrl');
|
||||
const saveConfigBtn = document.getElementById('saveConfig');
|
||||
const extractLaptopBtn = document.getElementById('extractLaptop');
|
||||
const extractPeripheralBtn = document.getElementById('extractPeripheral');
|
||||
const customInput = document.getElementById('customInput');
|
||||
const sendCustomBtn = document.getElementById('sendCustom');
|
||||
const statusBadge = document.getElementById('statusBadge');
|
||||
const resultsArea = document.getElementById('results');
|
||||
|
||||
// Load settings on startup
|
||||
chrome.storage.local.get(['geminiApiKey', 'googleScriptUrl'], (data) => {
|
||||
if (data.geminiApiKey) apiKeyInput.value = data.geminiApiKey;
|
||||
if (data.googleScriptUrl) scriptUrlInput.value = data.googleScriptUrl;
|
||||
});
|
||||
|
||||
// Save settings
|
||||
saveConfigBtn.addEventListener('click', () => {
|
||||
const apiKey = apiKeyInput.value.trim();
|
||||
const scriptUrl = scriptUrlInput.value.trim();
|
||||
chrome.storage.local.set({ geminiApiKey: apiKey, googleScriptUrl: scriptUrl }, () => {
|
||||
updateStatus('已保存', 'bg-green-500 text-white');
|
||||
setTimeout(() => updateStatus('待机', 'bg-gray-200 text-gray-600'), 2000);
|
||||
});
|
||||
});
|
||||
|
||||
// Action Handlers
|
||||
extractLaptopBtn.addEventListener('click', () => handleExtraction('laptop'));
|
||||
extractPeripheralBtn.addEventListener('click', () => handleExtraction('peripheral'));
|
||||
sendCustomBtn.addEventListener('click', () => handleExtraction('custom', customInput.value));
|
||||
|
||||
async function handleExtraction(type, customText = '') {
|
||||
const { geminiApiKey, googleScriptUrl } = await chrome.storage.local.get(['geminiApiKey', 'googleScriptUrl']);
|
||||
|
||||
if (!geminiApiKey) {
|
||||
alert('请先输入 Gemini API 密钥。');
|
||||
return;
|
||||
}
|
||||
|
||||
updateStatus('提取中...', 'bg-blue-500 text-white');
|
||||
resultsArea.textContent = '正在读取页面内容...';
|
||||
|
||||
try {
|
||||
// 1. Get current tab content
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
const [{ result: pageData }] = await chrome.scripting.executeScript({
|
||||
target: { tabId: tab.id },
|
||||
files: ['content.js']
|
||||
});
|
||||
|
||||
resultsArea.textContent = '正在发送至 Gemini...';
|
||||
|
||||
// 2. Prepare Prompt
|
||||
const systemPrompt = `你是一个专业的采购助手。
|
||||
请从以下文本中提取产品数据。
|
||||
将技术术语翻译成中文。
|
||||
仅返回有效的 JSON 格式。
|
||||
如果可能,将本地货币转换为美元,或者保留原始符号。
|
||||
URL: ${pageData.url}
|
||||
Title: ${pageData.title}`;
|
||||
|
||||
let userPrompt = "";
|
||||
if (type === 'laptop') {
|
||||
userPrompt = "提取笔记本详情:品牌、CPU、内存、存储、价格、URL。仅返回 JSON。";
|
||||
} else if (type === 'peripheral') {
|
||||
userPrompt = "提取外设详情:品牌、型号、连接方式、电池寿命、价格、URL。仅返回 JSON。";
|
||||
} else {
|
||||
userPrompt = `提取以下信息:${customText}。仅返回 JSON。`;
|
||||
}
|
||||
|
||||
// 3. Call Gemini API
|
||||
const geminiResult = await callGemini(geminiApiKey, systemPrompt, pageData.text, userPrompt);
|
||||
const cleanedJson = parseGeminiJson(geminiResult);
|
||||
|
||||
resultsArea.textContent = JSON.stringify(cleanedJson, null, 2);
|
||||
|
||||
// 4. Send to Google Apps Script
|
||||
if (googleScriptUrl) {
|
||||
updateStatus('保存至表格...', 'bg-purple-500 text-white');
|
||||
await sendToGoogleScript(googleScriptUrl, { ...cleanedJson, source_url: pageData.url, timestamp: new Date().toISOString() });
|
||||
updateStatus('成功', 'bg-green-500 text-white');
|
||||
} else {
|
||||
updateStatus('完成 (未配置脚本链接)', 'bg-yellow-500 text-white');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
updateStatus('错误', 'bg-red-500 text-white');
|
||||
resultsArea.textContent = `错误: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
async function callGemini(apiKey, systemPrompt, contextText, userPrompt) {
|
||||
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${apiKey}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
contents: [{
|
||||
parts: [{
|
||||
text: `${systemPrompt}\n\n上下文:\n${contextText}\n\n任务: ${userPrompt}`
|
||||
}]
|
||||
}]
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.error) throw new Error(data.error.message);
|
||||
return data.candidates[0].content.parts[0].text;
|
||||
}
|
||||
|
||||
function parseGeminiJson(text) {
|
||||
try {
|
||||
// Remove markdown code blocks if any
|
||||
const jsonStr = text.replace(/```json/g, '').replace(/```/g, '').trim();
|
||||
return JSON.parse(jsonStr);
|
||||
} catch (e) {
|
||||
throw new Error('AI 返回了无效的 JSON: ' + text);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendToGoogleScript(url, payload) {
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
mode: 'no-cors', // Apps Script often requires no-cors for simple POST
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
function updateStatus(text, classes) {
|
||||
statusBadge.textContent = text;
|
||||
statusBadge.className = `status-badge ${classes}`;
|
||||
}
|
||||
Reference in New Issue
Block a user