javascript:(function() {
const TARGET_URL = 'TARGET_ORIGIN/api.php?action=add_bookmark'
, TOKEN = "YOUR_TOKEN_HERE";
const o = document.createElement("div");
Object.assign(o.style, {
position: "fixed",
top: "0",
left: "0",
width: "100%",
height: "100%",
backgroundColor: "rgba(0, 0, 0, 0.6)",
backdropFilter: "blur(8px)",
display: "flex",
justifyContent: "center",
alignItems: "center",
zIndex: "9999990",
transition: "opacity 0.3s ease"
});
const e = document.createElement("div");
Object.assign(e.style, {
backgroundColor: "rgba(255, 255, 255, 0.75)",
backdropFilter: "blur(12px)",
borderRadius: "12px",
padding: "25px",
width: "400px",
boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
border: "1px solid rgba(255, 255, 255, 0.18)",
transform: "translateY(-20px)",
opacity: "0",
transition: "all 0.3s ease",
maxHeight: "80vh",
overflowY: "auto"
});
setTimeout( () => {
e.style.transform = "translateY(0)",
e.style.opacity = "1"
}
, 10);
const t = document.createElement("h3");
t.textContent = "保存书签",
Object.assign(t.style, {
margin: "0 0 20px 0",
color: "#333",
fontSize: "20px",
fontWeight: "500",
textAlign: "center"
});
const urlContainer = document.createElement("div");
Object.assign(urlContainer.style, {
display: "flex",
alignItems: "center",
marginBottom: "15px"
});
const n = document.createElement("label");
n.textContent = "URL:",
Object.assign(n.style, {
marginRight: "10px",
color: "#555",
fontSize: "14px",
minWidth: "80px"
});
const i = document.createElement("textarea");
i.value = window.location.href,
Object.assign(i.style, {
flex: "1",
padding: "10px",
borderRadius: "6px",
border: "1px solid rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 255, 255, 0.5)",
resize: "none",
minHeight: "60px",
fontFamily: "inherit",
fontSize: "14px",
transition: "border 0.3s ease",
maxWidth: "100%",
boxSizing: "border-box"
}),
i.addEventListener("focus", () => {
i.style.border = "1px solid rgba(100, 149, 237, 0.8)"
}
),
i.addEventListener("blur", () => {
i.style.border = "1px solid rgba(0, 0, 0, 0.1)"
}
);
urlContainer.appendChild(n);
urlContainer.appendChild(i);
const titleContainer = document.createElement("div");
Object.assign(titleContainer.style, {
display: "flex",
alignItems: "center",
marginBottom: "15px"
});
const d = document.createElement("label");
d.textContent = "标题:",
Object.assign(d.style, {
marginRight: "10px",
color: "#555",
fontSize: "14px",
minWidth: "80px"
});
const l = document.createElement("input");
l.type = "text",
l.value = document.title,
Object.assign(l.style, {
flex: "1",
padding: "10px",
borderRadius: "6px",
border: "1px solid rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 255, 255, 0.5)",
fontFamily: "inherit",
fontSize: "14px",
transition: "border 0.3s ease",
maxWidth: "100%",
boxSizing: "border-box"
}),
l.addEventListener("focus", () => {
l.style.border = "1px solid rgba(100, 149, 237, 0.8)"
}
),
l.addEventListener("blur", () => {
l.style.border = "1px solid rgba(0, 0, 0, 0.1)"
}
);
titleContainer.appendChild(d);
titleContainer.appendChild(l);
const descContainer = document.createElement("div");
Object.assign(descContainer.style, {
display: "flex",
alignItems: "center",
marginBottom: "15px"
});
const descLabel = document.createElement("label");
descLabel.textContent = "描述:",
Object.assign(descLabel.style, {
marginRight: "10px",
color: "#555",
fontSize: "14px",
minWidth: "80px"
});
const descInput = document.createElement("textarea");
descInput.placeholder = "请输入描述",
Object.assign(descInput.style, {
flex: "1",
padding: "10px",
borderRadius: "6px",
border: "1px solid rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 255, 255, 0.5)",
minHeight: "60px",
fontFamily: "inherit",
fontSize: "14px",
transition: "border 0.3s ease",
maxWidth: "100%",
boxSizing: "border-box"
}),
descInput.addEventListener("focus", () => {
descInput.style.border = "1px solid rgba(100, 149, 237, 0.8)"
}),
descInput.addEventListener("blur", () => {
descInput.style.border = "1px solid rgba(0, 0, 0, 0.1)"
});
descContainer.appendChild(descLabel);
descContainer.appendChild(descInput);
const logoContainer = document.createElement("div");
Object.assign(logoContainer.style, {
display: "flex",
alignItems: "center",
marginBottom: "15px"
});
const logoLabel = document.createElement("label");
logoLabel.textContent = "图标:",
Object.assign(logoLabel.style, {
marginRight: "10px",
color: "#555",
fontSize: "14px",
minWidth: "80px"
});
const logoInput = document.createElement("input");
logoInput.type = "text",
logoInput.placeholder = "支持URL或者base64格式的图标",
Object.assign(logoInput.style, {
flex: "1",
padding: "10px",
borderRadius: "6px",
border: "1px solid rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 255, 255, 0.5)",
fontFamily: "inherit",
fontSize: "14px",
transition: "border 0.3s ease",
maxWidth: "100%",
boxSizing: "border-box"
}),
logoInput.addEventListener("focus", () => {
logoInput.style.border = "1px solid rgba(100, 149, 237, 0.8)"
}),
logoInput.addEventListener("blur", () => {
logoInput.style.border = "1px solid rgba(0, 0, 0, 0.1)"
});
logoContainer.appendChild(logoLabel);
logoContainer.appendChild(logoInput);
const nameContainer = document.createElement("div");
Object.assign(nameContainer.style, {
display: "flex",
alignItems: "center",
marginBottom: "15px"
});
const nameLabel = document.createElement("label");
nameLabel.textContent = "一级分类:",
Object.assign(nameLabel.style, {
marginRight: "10px",
color: "#555",
fontSize: "14px",
minWidth: "80px"
});
const nameInput = document.createElement("input");
nameInput.type = "text",
nameInput.value = "插件保存",
Object.assign(nameInput.style, {
flex: "1",
padding: "10px",
borderRadius: "6px",
border: "1px solid rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 255, 255, 0.5)",
fontFamily: "inherit",
fontSize: "14px",
transition: "border 0.3s ease",
maxWidth: "100%",
boxSizing: "border-box"
}),
nameInput.addEventListener("focus", () => {
nameInput.style.border = "1px solid rgba(100, 149, 237, 0.8)"
}),
nameInput.addEventListener("blur", () => {
nameInput.style.border = "1px solid rgba(0, 0, 0, 0.1)"
});
nameContainer.appendChild(nameLabel);
nameContainer.appendChild(nameInput);
const childrenContainer = document.createElement("div");
Object.assign(childrenContainer.style, {
display: "flex",
alignItems: "center",
marginBottom: "15px"
});
const childrenLabel = document.createElement("label");
childrenLabel.textContent = "二级分类:",
Object.assign(childrenLabel.style, {
marginRight: "10px",
color: "#555",
fontSize: "14px",
minWidth: "80px"
});
const childrenInput = document.createElement("input");
childrenInput.type = "text",
childrenInput.placeholder = "请输入二级分类",
Object.assign(childrenInput.style, {
flex: "1",
padding: "10px",
borderRadius: "6px",
border: "1px solid rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 255, 255, 0.5)",
fontFamily: "inherit",
fontSize: "14px",
transition: "border 0.3s ease",
maxWidth: "100%",
boxSizing: "border-box"
}),
childrenInput.addEventListener("focus", () => {
childrenInput.style.border = "1px solid rgba(100, 149, 237, 0.8)"
}),
childrenInput.addEventListener("blur", () => {
childrenInput.style.border = "1px solid rgba(0, 0, 0, 0.1)"
});
childrenContainer.appendChild(childrenLabel);
childrenContainer.appendChild(childrenInput);
const optionsContainer = document.createElement("div");
Object.assign(optionsContainer.style, {
display: "flex",
justifyContent: "space-between",
marginBottom: "15px"
});
const showUrlGuestContainer = document.createElement("div");
Object.assign(showUrlGuestContainer.style, {
display: "flex",
alignItems: "center",
width: "48%"
});
const showUrlGuestLabel = document.createElement("label");
showUrlGuestLabel.textContent = "对访客显示URL:",
Object.assign(showUrlGuestLabel.style, {
marginRight: "10px",
color: "#555",
fontSize: "14px"
});
const showUrlGuestInput = document.createElement("select");
Object.assign(showUrlGuestInput.style, {
flex: "1",
padding: "10px",
borderRadius: "6px",
border: "1px solid rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 255, 255, 0.5)",
fontFamily: "inherit",
fontSize: "14px",
transition: "border 0.3s ease",
boxSizing: "border-box"
});
const showUrlGuestOption1 = document.createElement("option");
showUrlGuestOption1.value = "true";
showUrlGuestOption1.textContent = "是";
showUrlGuestOption1.selected = true;
const showUrlGuestOption2 = document.createElement("option");
showUrlGuestOption2.value = "false";
showUrlGuestOption2.textContent = "否";
showUrlGuestInput.appendChild(showUrlGuestOption1);
showUrlGuestInput.appendChild(showUrlGuestOption2);
showUrlGuestContainer.appendChild(showUrlGuestLabel);
showUrlGuestContainer.appendChild(showUrlGuestInput);
const isShowContainer = document.createElement("div");
Object.assign(isShowContainer.style, {
display: "flex",
alignItems: "center",
width: "46%"
});
const isShowLabel = document.createElement("label");
isShowLabel.textContent = "是否公开书签:",
Object.assign(isShowLabel.style, {
marginRight: "10px",
color: "#555",
fontSize: "14px"
});
const isShowInput = document.createElement("select");
Object.assign(isShowInput.style, {
flex: "1",
padding: "10px",
borderRadius: "6px",
border: "1px solid rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 255, 255, 0.5)",
fontFamily: "inherit",
fontSize: "14px",
transition: "border 0.3s ease",
boxSizing: "border-box"
});
const isShowOption1 = document.createElement("option");
isShowOption1.value = "true";
isShowOption1.textContent = "是";
isShowOption1.selected = true;
const isShowOption2 = document.createElement("option");
isShowOption2.value = "false";
isShowOption2.textContent = "否";
isShowInput.appendChild(isShowOption1);
isShowInput.appendChild(isShowOption2);
isShowContainer.appendChild(isShowLabel);
isShowContainer.appendChild(isShowInput);
optionsContainer.appendChild(showUrlGuestContainer);
optionsContainer.appendChild(isShowContainer);
const c = document.createElement("div");
Object.assign(c.style, {
display: "flex",
justifyContent: "center",
gap: "15px"
});
const s = document.createElement("button");
s.textContent = "保存",
Object.assign(s.style, {
padding: "10px 25px",
borderRadius: "6px",
border: "none",
backgroundColor: "#4a6cf7",
color: "white",
cursor: "pointer",
fontSize: "14px",
fontWeight: "500",
transition: "all 0.2s ease",
boxShadow: "0 2px 10px rgba(74, 108, 247, 0.3)"
}),
s.addEventListener("mouseenter", () => {
s.style.backgroundColor = "#3a5ce4",
s.style.transform = "translateY(-2px)"
}
),
s.addEventListener("mouseleave", () => {
s.style.backgroundColor = "#4a6cf7",
s.style.transform = "translateY(0)"
}
);
const a = document.createElement("button");
a.textContent = "取消",
Object.assign(a.style, {
padding: "10px 25px",
borderRadius: "6px",
border: "1px solid rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 255, 255, 0.7)",
color: "#555",
cursor: "pointer",
fontSize: "14px",
fontWeight: "500",
transition: "all 0.2s ease"
}),
a.addEventListener("mouseenter", () => {
a.style.backgroundColor = "rgba(0, 0, 0, 0.05)",
a.style.transform = "translateY(-2px)"
}
),
a.addEventListener("mouseleave", () => {
a.style.backgroundColor = "rgba(255, 255, 255, 0.7)",
a.style.transform = "translateY(0)"
}
);
const jumpBtn = document.createElement("button");
jumpBtn.textContent = "跳转添加",
Object.assign(jumpBtn.style, {
padding: "10px 25px",
borderRadius: "6px",
border: "1px solid rgba(0, 0, 0, 0.1)",
backgroundColor: "rgba(255, 193, 7, 0.7)",
color: "#555",
cursor: "pointer",
fontSize: "14px",
fontWeight: "500",
transition: "all 0.2s ease",
display: "none"
}),
jumpBtn.addEventListener("mouseenter", () => {
jumpBtn.style.backgroundColor = "rgba(255, 193, 7, 0.9)",
jumpBtn.style.transform = "translateY(-2px)"
}),
jumpBtn.addEventListener("mouseleave", () => {
jumpBtn.style.backgroundColor = "rgba(255, 193, 7, 0.7)",
jumpBtn.style.transform = "translateY(0)"
});
c.appendChild(a),
c.appendChild(jumpBtn),
c.appendChild(s),
e.appendChild(t),
e.appendChild(urlContainer),
e.appendChild(titleContainer),
e.appendChild(descContainer),
e.appendChild(logoContainer),
e.appendChild(nameContainer),
e.appendChild(childrenContainer),
e.appendChild(optionsContainer),
e.appendChild(c),
o.appendChild(e),
document.body.appendChild(o),
document.body.style.overflow = "hidden";
const r = () => {
e.style.transform = "translateY(-20px)",
e.style.opacity = "0",
o.style.opacity = "0",
setTimeout( () => {
document.body.contains(o) && (document.body.removeChild(o),
document.body.style.overflow = "")
}
, 300)
}
, u = (e, t=!0) => {
const n = document.createElement("div");
Object.assign(n.style, {
position: "fixed",
top: "20px",
left: "50%",
transform: "translateX(-50%)",
backgroundColor: t ? "rgba(76, 175, 80, 0.9)" : "rgba(244, 67, 54, 0.9)",
backdropFilter: "blur(10px)",
color: "white",
padding: "12px 24px",
borderRadius: "6px",
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
zIndex: "9999991",
opacity: "0",
transition: "all 0.3s ease",
display: "flex",
alignItems: "center",
justifyContent: "center",
maxWidth: "80%",
wordBreak: "break-word"
});
const i = document.createElement("span");
i.textContent = t ? "✓" : "✕",
Object.assign(i.style, {
marginRight: "8px",
fontSize: "18px"
});
const d = document.createElement("span");
d.textContent = e,
n.appendChild(i),
n.appendChild(d),
document.body.appendChild(n),
setTimeout( () => {
n.style.opacity = "1",
n.style.top = "30px"
}
, 10),
setTimeout( () => {
n.style.opacity = "0",
n.style.top = "20px",
setTimeout( () => {
document.body.contains(n) && document.body.removeChild(n)
}
, 300)
}
, t ? 8000 : 3000)
};
const getFavicon = () => {
return new Promise((resolve) => {
const links = document.querySelectorAll('link[rel*="icon"]');
let faviconUrl = '';
if (links.length > 0) {
const sortedLinks = Array.from(links).sort((a, b) => {
const aSize = a.getAttribute('sizes') ? parseInt(a.getAttribute('sizes').split('x')[0]) : 0;
const bSize = b.getAttribute('sizes') ? parseInt(b.getAttribute('sizes').split('x')[0]) : 0;
return bSize - aSize;
});
faviconUrl = sortedLinks[0].href;
} else {
faviconUrl = new URL('/favicon.ico', window.location.origin).href;
}
if (!faviconUrl) {
console.error('未找到网站图标');
resolve(null);
return;
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function() {
const targetWidth = 80;
const scale = targetWidth / img.width;
const targetHeight = img.height * scale;
canvas.width = targetWidth;
canvas.height = targetHeight;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img,0, 0, img.width, img.height,0, 0, targetWidth, targetHeight);
try {
const dataURL = canvas.toDataURL('image/png');
resolve(dataURL);
} catch (e) {
console.error('转换图标为base64失败:', e);
resolve(null);
}
};
img.onerror = function() {
console.error('加载网站图标失败:', faviconUrl);
resolve(null);
};
img.src = faviconUrl;
});
};
getFavicon().then(base64Icon => {
if (base64Icon) {
logoInput.value = base64Icon;
console.log('已自动获取网站图标并转换为base64');
} else {
console.log('未能获取网站图标,请手动输入图标URL');
}
});
const buildJumpUrl = (formData) => {
const params = new URLSearchParams();
params.append('url', formData.url);
params.append('title', formData.title);
params.append('desc', formData.desc);
params.append('logo', formData.logo);
params.append('name', formData.name);
params.append('children', formData.children);
params.append('show_url_guest', formData.show_url_guest ? 'true' : 'false');
params.append('is_show', formData.is_show ? 'true' : 'false');
return `TARGET_ORIGIN/add.html?${params.toString()}`;
};
const p = async () => {
const t = i.value.trim()
, n = l.value.trim();
if (!t)
return void u("URL不能为空", !1);
if (!n)
return void u("标题不能为空", !1);
s.disabled = !0,
s.textContent = "保存中...",
s.style.backgroundColor = "#999";
const formData = {
url: t,
title: n,
desc: descInput.value.trim(),
logo: logoInput.value.trim(),
name: nameInput.value.trim(),
children: childrenInput.value.trim(),
show_url_guest: showUrlGuestInput.value === "true",
is_show: isShowInput.value === "true"
};
try {
const o = await fetch(TARGET_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
token: TOKEN
},
body: JSON.stringify(formData)
})
, c = await o.json();
if (c.success) {
r(),
u("保存成功");
} else {
u(`保存失败: ${c.message}`, !1);
jumpBtn.style.display = "block";
jumpBtn.onclick = () => {
window.location.href = buildJumpUrl(formData);
};
}
} catch (e) {
u(`请求出错,可以点击下方【跳转添加】按钮进行添加: ${e.message}`, !1);
jumpBtn.style.display = "block";
jumpBtn.onclick = () => {
window.location.href = buildJumpUrl(formData);
};
} finally {
s.disabled = !1,
s.textContent = "保存",
s.style.backgroundColor = "#4a6cf7";
}
};
s.addEventListener("click", p),
a.addEventListener("click", r),
o.addEventListener("click", e => {
e.target === o && r()
});
})();