support custom sheet
This commit is contained in:
@@ -11,24 +11,40 @@
|
||||
function doPost(e) {
|
||||
try {
|
||||
var data = JSON.parse(e.postData.contents);
|
||||
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
|
||||
var ss = SpreadsheetApp.getActiveSpreadsheet();
|
||||
var sheet;
|
||||
|
||||
// 1. Get or Create the target sheet
|
||||
if (data.sheetName) {
|
||||
sheet = ss.getSheetByName(data.sheetName);
|
||||
if (!sheet) {
|
||||
sheet = ss.insertSheet(data.sheetName);
|
||||
}
|
||||
} else {
|
||||
sheet = ss.getActiveSheet();
|
||||
}
|
||||
|
||||
// Get headers
|
||||
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
|
||||
if (headers[0] === "" && sheet.getLastColumn() === 1) {
|
||||
// New sheet, set headers from JSON keys
|
||||
// 2. Get headers
|
||||
var lastCol = sheet.getLastColumn();
|
||||
var headers = [];
|
||||
if (lastCol > 0) {
|
||||
headers = sheet.getRange(1, 1, 1, lastCol).getValues()[0];
|
||||
}
|
||||
|
||||
// 3. If sheet is empty, set headers from JSON keys
|
||||
if (headers.length === 0 || headers[0] === "") {
|
||||
headers = Object.keys(data);
|
||||
sheet.appendRow(headers);
|
||||
}
|
||||
|
||||
// Map data to headers
|
||||
// 4. Map data to headers
|
||||
var row = headers.map(function(header) {
|
||||
return data[header] || "N/A";
|
||||
return data[header] !== undefined ? data[header] : "N/A";
|
||||
});
|
||||
|
||||
sheet.appendRow(row);
|
||||
|
||||
return ContentService.createTextOutput(JSON.stringify({ status: "success" }))
|
||||
return ContentService.createTextOutput(JSON.stringify({ status: "success", sheet: sheet.getName() }))
|
||||
.setMimeType(ContentService.MimeType.JSON);
|
||||
|
||||
} catch (error) {
|
||||
@@ -37,7 +53,6 @@ function doPost(e) {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle preflight (for some environments, though 'no-cors' usually avoids this)
|
||||
function doOptions(e) {
|
||||
return ContentService.createTextOutput("")
|
||||
.setMimeType(ContentService.MimeType.TEXT);
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
<div class="pt-4 border-t border-gray-100 space-y-3">
|
||||
<label class="block text-xs font-bold text-gray-600 mb-1">预设管理</label>
|
||||
<div class="space-y-2.5">
|
||||
<input type="text" id="newPresetName" class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm outline-none" placeholder="预设名称 (如:手机)">
|
||||
<input type="text" id="newPresetName" class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm outline-none" placeholder="预设名称 (如:提取笔记本)">
|
||||
<input type="text" id="newPresetSheet" class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm outline-none" placeholder="目标工作表名称 (如:笔记本清单)">
|
||||
<textarea id="newPresetFields" class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm outline-none h-16" placeholder="提取字段 (用逗号分隔,如:品牌, 型号, 价格)"></textarea>
|
||||
<button id="addPreset" class="w-full bg-indigo-50 text-indigo-700 text-xs py-2 rounded hover:bg-indigo-100 transition-colors font-semibold border border-indigo-200">添加新预设</button>
|
||||
</div>
|
||||
|
||||
21
sidepanel.js
21
sidepanel.js
@@ -10,6 +10,7 @@ const resultsArea = document.getElementById('results');
|
||||
|
||||
// Preset Management Elements
|
||||
const newPresetName = document.getElementById('newPresetName');
|
||||
const newPresetSheet = document.getElementById('newPresetSheet');
|
||||
const newPresetFields = document.getElementById('newPresetFields');
|
||||
const addPresetBtn = document.getElementById('addPreset');
|
||||
const presetList = document.getElementById('presetList');
|
||||
@@ -22,8 +23,8 @@ const configContent = document.getElementById('configContent');
|
||||
const configChevron = document.getElementById('configChevron');
|
||||
|
||||
const DEFAULT_PRESETS = [
|
||||
{ id: 'p1', name: '提取笔记本', fields: '品牌, CPU, 内存, 存储, 价格, URL' },
|
||||
{ id: 'p2', name: '提取外设', fields: '品牌, 型号, 连接方式, 电池寿命, 价格, URL' }
|
||||
{ id: 'p1', name: '提取笔记本', sheetName: '笔记本', fields: '品牌, CPU, 内存, 存储, 价格, URL' },
|
||||
{ id: 'p2', name: '提取外设', sheetName: '外设', fields: '品牌, 型号, 连接方式, 电池寿命, 价格, URL' }
|
||||
];
|
||||
|
||||
let currentPresets = [];
|
||||
@@ -68,15 +69,17 @@ saveConfigBtn.addEventListener('click', () => {
|
||||
// Preset Management Logic
|
||||
addPresetBtn.addEventListener('click', async () => {
|
||||
const name = newPresetName.value.trim();
|
||||
const sheetName = newPresetSheet.value.trim();
|
||||
const fields = newPresetFields.value.trim();
|
||||
if (!name || !fields) return alert('请输入名称和字段');
|
||||
|
||||
const { userPresets = DEFAULT_PRESETS } = await chrome.storage.local.get('userPresets');
|
||||
const newPreset = { id: Date.now().toString(), name, fields };
|
||||
const newPreset = { id: Date.now().toString(), name, sheetName, fields };
|
||||
currentPresets = [...userPresets, newPreset];
|
||||
|
||||
await chrome.storage.local.set({ userPresets: currentPresets });
|
||||
newPresetName.value = '';
|
||||
newPresetSheet.value = '';
|
||||
newPresetFields.value = '';
|
||||
renderPresets(currentPresets);
|
||||
});
|
||||
@@ -99,7 +102,7 @@ function renderPresets(presets) {
|
||||
item.className = 'flex items-center justify-between bg-white p-3 mb-2 rounded-lg border border-gray-200 shadow-sm transition-all hover:border-indigo-200';
|
||||
item.innerHTML = `
|
||||
<div class="flex-1 overflow-hidden mr-3">
|
||||
<div class="text-xs font-bold text-gray-800 truncate">${preset.name}</div>
|
||||
<div class="text-xs font-bold text-gray-800 truncate">${preset.name} ${preset.sheetName ? `<span class="text-indigo-500 ml-1">#${preset.sheetName}</span>` : ''}</div>
|
||||
<div class="text-[11px] text-gray-400 truncate mt-1 leading-tight">${preset.fields}</div>
|
||||
</div>
|
||||
<button class="flex items-center justify-center w-7 h-7 rounded-full text-gray-300 hover:text-red-500 hover:bg-red-50 transition-all focus:outline-none border-none bg-transparent cursor-pointer" title="删除预设">
|
||||
@@ -130,7 +133,7 @@ runPresetBtn.addEventListener('click', () => {
|
||||
if (preset) handleExtraction('preset', preset);
|
||||
});
|
||||
|
||||
sendCustomBtn.addEventListener('click', () => handleExtraction('custom', { fields: customInput.value }));
|
||||
sendCustomBtn.addEventListener('click', () => handleExtraction('custom', { fields: customInput.value, sheetName: '' }));
|
||||
|
||||
async function handleExtraction(type, presetObj) {
|
||||
const { geminiApiKey, googleScriptUrl, geminiModel } = await chrome.storage.local.get(['geminiApiKey', 'googleScriptUrl', 'geminiModel']);
|
||||
@@ -170,7 +173,13 @@ Title: ${pageData.title}`;
|
||||
|
||||
if (googleScriptUrl) {
|
||||
updateStatus('保存至表格...', 'bg-purple-500 text-white');
|
||||
await sendToGoogleScript(googleScriptUrl, { ...cleanedJson, preset_type: presetObj.name || 'custom', source_url: pageData.url, timestamp: new Date().toISOString() });
|
||||
await sendToGoogleScript(googleScriptUrl, {
|
||||
...cleanedJson,
|
||||
preset_type: presetObj.name || 'custom',
|
||||
sheetName: presetObj.sheetName || '',
|
||||
source_url: pageData.url,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
updateStatus('成功', 'bg-green-500 text-white');
|
||||
} else {
|
||||
updateStatus('完成 (未配置脚本链接)', 'bg-yellow-500 text-white');
|
||||
|
||||
Reference in New Issue
Block a user