django索引
from django.db import models
class MyModel(models.Model):
field1 = models.CharField(max_length=100)
field2 = models.IntegerField()
class Meta:
unique_together = ('field1', 'field2') # 联合唯一索引
from django.db import models
class MyModel(models.Model):
field1 = models.CharField(max_length=100)
field2 = models.IntegerField()
class Meta:
unique_together = ('field1', 'field2') # 联合唯一索引
登录错误 ERROR 1524 (HY000): Plugin 'mysql_native_plugin' is not loaded
mysql -uroot -proot执行
use mysql;
ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'root';跳过权限
mysqld --skip-grant-tables --skip-networkinguse mysql;
ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'root';或者
use mysql;
UPDATE user SET plugin='caching_sha2_password' WHERE user='root' AND host='localhost';
FLUSH PRIVILEGES; const dragAndDrop = async (elem, startX, startY, distance) => {
// 初始化鼠标事件
const dispatchMouseEvent = (type, x, y) => {
const event = new MouseEvent(type, {
view: window,
bubbles: true,
cancelable: true,
clientX: Math.floor(x),
clientY: Math.floor(y),
screenX: Math.floor(x + window.screenX),
screenY: Math.floor(y + window.screenY),
button: 0,
// 当是 mousedown 或 mousemove 时,保持左键按下状态
buttons: type === 'mouseup' ? 0 : 1,
});
elem.dispatchEvent(event);
};
// 贝塞尔曲线运动参数
const duration = 800 + Math.random() * 400; // 随机持续时间
const startTime = performance.now();
const humanizeFactor = 0.3; // 人类操作随机因子
// 触发初始按下事件
dispatchMouseEvent('mousedown', startX, startY);
// 使用requestAnimationFrame实现平滑动画
return new Promise(resolve => {
const animate = (currentTime) => {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// 应用缓动函数(三次贝塞尔曲线)
const easedProgress = Math.sin(progress * Math.PI / 2);
// 添加人类操作随机扰动
const randomOffset = Math.random() * humanizeFactor * distance;
const currentX = startX + (distance * easedProgress) + randomOffset;
const currentY = startY + Math.sin(progress * Math.PI) * 5; // 自然Y轴波动
dispatchMouseEvent('mousemove', currentX, currentY);
if (progress < 1) {
requestAnimationFrame(animate);
} else {
// 结束操作
dispatchMouseEvent('mouseup', currentX, currentY);
resolve();
}
};
requestAnimationFrame(animate);
});
};
// 优化后的启动函数
const simulateHorizontalDrag = async (slide, distance) => {
const elem = document.querySelector(slide);
if (!elem) {
logger.error('selector:%s not found', slide);
return;
}
const rect = elem.getBoundingClientRect();
const startX = rect.left + rect.width * 0.3 + Math.random() * rect.width * 0.4;
const startY = rect.top + rect.height * 0.3 + Math.random() * rect.height * 0.4;
await dragAndDrop(elem, startX, startY, distance);
};
function dragandDrop(id, clientX, clientY, distance) {
const elem = document.querySelector(id);
let k = 0;
iME(elem, "mousedown", 0, 0, clientX, clientY);
const interval = setInterval(function () {
k++;
iME(elem, "mousemove", clientX + k, clientY, clientX + k, clientY);
if (k >= distance) {
clearInterval(interval);
iME(elem, "mouseup", clientX + k, clientY, clientX + k, clientY);
}
}, 8);
function iME(obj, event, screenXArg, screenYArg, clientXArg, clientYArg) {
const mousemove = document.createEvent("MouseEvent");
mousemove.initMouseEvent(event, true, true, window, 0,
screenXArg, screenYArg, clientXArg, clientYArg, 0, 0, 0, 0, 0, null);
obj.dispatchEvent(mousemove);
}
}
function simulateHorizontalDrag(slide, distance) {
const obj = document.querySelector(slide);
obj.target = '_self';
const _owh = obj.getBoundingClientRect();
let _ox = _owh.width / 2, _oh = _owh.height / 2;
_ox = Math.floor(Math.random() * _ox + 60);
_oh = Math.floor(Math.random() * _oh + 60);
_ox = _ox + _owh.x;
_oh = _oh + _owh.y;
dragandDrop(slide, _ox, _oh, distance);
} content js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log('got message: ', message)
if (message.type === 'get_slider_position') {
// 获取滑块元素(需替换为实际选择器)
const slider = document.querySelector('.tc-slider-normal');
let rect = slider.getBoundingClientRect();
sendResponse({rect})
} else if (message.type === 'get_captcha_position') {
console.log('got message: ', 'get_captcha_position')
// 获取滑块元素(需替换为实际选择器)
const slider = document.querySelector('#slideBg');
let rect = slider.getBoundingClientRect();
sendResponse({rect})
} else if (message.type === 'devicePixelRatio') {
console.log('devicePixelRatio', window.devicePixelRatio)
sendResponse({devicePixelRatio: window.devicePixelRatio});
}
});background js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log('got message: ', message)
if (message.type === 'TAKE_ELEMENT_SCREENSHOT') {
get_captcha_screen(message, sender, sendResponse)
return true
} else if (message.type === 'download_image') {
chrome.downloads.download(message.data);
}
});
const get_captcha_screen = (message, sender, sendResponse) => {
// 获取当前标签页的 windowId
const windowId = sender.tab.windowId;
chrome.tabs.captureVisibleTab(windowId, {format: 'png'}, async (dataUrl) => {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
return;
}
// chrome.downloads.download({
// url: dataUrl,
// filename: 'page.png'
// });
try {
// 将 dataUrl 转为 Blob 并创建 ImageBitmap 便于裁剪
const blob = await fetch(dataUrl).then(r => r.blob());
const imageBitmap = await createImageBitmap(blob);
const rect = message.rect
console.log('rect', rect)
let iframe = await get_element_position(sender.tab.id, 'iframe').then(result => result.rect)
console.log('iframe', iframe)
// 创建 OffscreenCanvas,其尺寸与目标元素一致
// const canvas = new OffscreenCanvas(iframe.width, iframe.height);
const radio = await chrome.tabs.sendMessage(sender.tab.id, {type: "devicePixelRatio"}).then(r => r.devicePixelRatio)
const canvas = new OffscreenCanvas(rect.width, rect.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(
imageBitmap,
(iframe.left + rect.left) * radio, //源图像的裁剪起点 X 坐标。
(iframe.top + rect.top) * radio,//iframe.top, //源图像的裁剪起点 Y 坐标。
rect.width * radio,//iframe.width, //源图像的裁剪宽度。
rect.height * radio,//iframe.height, //源图像的裁剪高度。
0, //目标 Canvas 上的 X 坐标。
0, //目标 Canvas 上的 Y 坐标。
rect.width, //绘制到 Canvas 上的宽度(可以缩放图像)。
rect.height,//绘制到 Canvas 上的高度(可以缩放图像)
);
// 将裁剪结果转换为 Blob 或 dataURL
const croppedBlob = await canvas.convertToBlob();
const reader = new FileReader();
reader.onload = () => {
const croppedDataUrl = reader.result;
console.log('元素截图完成,dataURL:', croppedDataUrl);
chrome.downloads.download({
url: croppedDataUrl,
filename: 'screenshot.png'
});
sendResponse({url: croppedDataUrl})
// 此处可以将截图 dataURL 发送到 popup 显示,或者打开新窗口展示
};
reader.readAsDataURL(croppedBlob);
} catch (error) {
console.error('截图过程中出错:', error);
}
});
}
async function get_element_position(tabId, element_type) {
const type = `get_${element_type}_position`
return await chrome.tabs.sendMessage(tabId, {type})
} // 引入 html2canvas
import html2canvas from 'html2canvas';
async function captureElement(elementSelector, image_name = 'screenshot_from_html2canvas') {
const element = document.querySelector(elementSelector);
// 获取元素的实际渲染尺寸
const rect = element.getBoundingClientRect();
const width = rect.width;
const height = rect.height;
// 创建临时容器(避免截取时元素被缩放影响布局)
const container = document.createElement('div');
Object.assign(container.style, {
position: 'fixed',
left: '-9999px', // 隐藏到屏幕外
width: `${width}px`, // 固定容器尺寸
height: `${height}px`
});
document.body.appendChild(container);
// 克隆元素到临时容器(保持样式)
const clone = element.cloneNode(true);
container.appendChild(clone);
// width和height是为了和屏幕上显示的大小一致,不加也可以
const canvas = await html2canvas(element, {
useCORS: true, // 允许跨域资源
logging: false, // 关闭日志
scale: 1, // 必须设为 1,否则尺寸会缩放
width: width, // 显式设置宽高
height: height,
backgroundColor: null // 透明背景
});
// 转换为图片 URL
const url = canvas.toDataURL('image/png', 1.0);
const data = {
url: url,
filename: `${image_name}.png`
}
// downloadImg(data);
postMessage({type: "download_image", data})
return url
}