feat: CutThenThink v3.0 初始版本
完整实现 Tauri + Vanilla JS 轻量级截图工具 Phase 1 - 项目搭建 - Tauri 2.x 项目初始化 - Vite 前端项目搭建 - 基础 UI 框架(CSS 变量、组件库) - 构建配置优化 Phase 2 - 核心截图功能 - 全屏/区域/窗口截图 - 截图预览和管理 - 文件命名和缩略图 - 全局快捷键集成 Phase 3 - 上传与存储 - 多图床上传(GitHub/Imgur/自定义) - 配置管理系统 - SQLite 数据库 Phase 4 - OCR 集成 - 云端 OCR(百度/腾讯云) - 插件管理系统 - 本地 OCR 插件(Go) - OCR 结果处理 Phase 5 - AI 分类系统 - Claude/OpenAI API 集成 - Prompt 模板引擎 - 模板管理界面 - 自动分类流程 Phase 6 - 历史记录与管理 - 图库视图(网格/列表) - 搜索与筛选 - 批量操作 - 导出功能(JSON/CSV/ZIP) Phase 7 - 打包与发布 - 多平台构建配置 - CI/CD 工作流 - 图标和资源 - 安装包配置 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
379
main.js
Normal file
379
main.js
Normal file
@@ -0,0 +1,379 @@
|
||||
// CutThink Lite - 主入口文件
|
||||
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
|
||||
// 应用状态管理
|
||||
const store = {
|
||||
currentView: 'screenshot',
|
||||
screenshots: [],
|
||||
settings: {
|
||||
theme: 'system',
|
||||
language: 'zh-CN',
|
||||
format: 'png',
|
||||
hideWindow: true,
|
||||
soundEffect: true,
|
||||
},
|
||||
}
|
||||
|
||||
// DOM 元素引用
|
||||
const elements = {
|
||||
navItems: null,
|
||||
views: null,
|
||||
btnCapture: null,
|
||||
btnCaptureArea: null,
|
||||
btnCaptureWindow: null,
|
||||
galleryGrid: null,
|
||||
uploadArea: null,
|
||||
}
|
||||
|
||||
// 初始化应用
|
||||
async function init() {
|
||||
console.log('CutThink Lite 初始化中...')
|
||||
|
||||
// 获取 DOM 元素
|
||||
cacheElements()
|
||||
|
||||
// 绑定事件
|
||||
bindEvents()
|
||||
|
||||
// 加载设置
|
||||
await loadSettings()
|
||||
|
||||
// 加载截图列表
|
||||
await loadScreenshots()
|
||||
|
||||
// 设置默认视图
|
||||
switchView('screenshot')
|
||||
|
||||
console.log('CutThink Lite 初始化完成')
|
||||
}
|
||||
|
||||
// 缓存 DOM 元素
|
||||
function cacheElements() {
|
||||
elements.navItems = document.querySelectorAll('.nav-item')
|
||||
elements.views = document.querySelectorAll('.view')
|
||||
elements.btnCapture = document.getElementById('btn-capture')
|
||||
elements.btnCaptureArea = document.getElementById('btn-capture-area')
|
||||
elements.btnCaptureWindow = document.getElementById('btn-capture-window')
|
||||
elements.galleryGrid = document.getElementById('gallery-grid')
|
||||
elements.uploadArea = document.getElementById('upload-area')
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
function bindEvents() {
|
||||
// 导航切换
|
||||
elements.navItems.forEach(item => {
|
||||
item.addEventListener('click', () => {
|
||||
const view = item.dataset.view
|
||||
switchView(view)
|
||||
})
|
||||
})
|
||||
|
||||
// 截图按钮
|
||||
if (elements.btnCapture) {
|
||||
elements.btnCapture.addEventListener('click', () => {
|
||||
console.log('触发全屏截图')
|
||||
handleCapture('full')
|
||||
})
|
||||
}
|
||||
|
||||
if (elements.btnCaptureArea) {
|
||||
elements.btnCaptureArea.addEventListener('click', () => {
|
||||
console.log('触发区域截图')
|
||||
handleCapture('area')
|
||||
})
|
||||
}
|
||||
|
||||
if (elements.btnCaptureWindow) {
|
||||
elements.btnCaptureWindow.addEventListener('click', () => {
|
||||
console.log('触发窗口截图')
|
||||
handleCapture('window')
|
||||
})
|
||||
}
|
||||
|
||||
// 上传区域拖拽
|
||||
if (elements.uploadArea) {
|
||||
setupDragAndDrop()
|
||||
}
|
||||
|
||||
// 监听 Tauri 事件
|
||||
setupTauriListeners()
|
||||
}
|
||||
|
||||
// 视图切换
|
||||
function switchView(viewName) {
|
||||
console.log('切换视图:', viewName)
|
||||
|
||||
// 更新导航状态
|
||||
elements.navItems.forEach(item => {
|
||||
if (item.dataset.view === viewName) {
|
||||
item.classList.add('active')
|
||||
} else {
|
||||
item.classList.remove('active')
|
||||
}
|
||||
})
|
||||
|
||||
// 更新视图显示
|
||||
elements.views.forEach(view => {
|
||||
if (view.id === `view-${viewName}`) {
|
||||
view.classList.add('active')
|
||||
} else {
|
||||
view.classList.remove('active')
|
||||
}
|
||||
})
|
||||
|
||||
store.currentView = viewName
|
||||
}
|
||||
|
||||
// 处理截图
|
||||
async function handleCapture(type) {
|
||||
try {
|
||||
console.log('开始截图:', type)
|
||||
|
||||
// 调用 Tauri 后端截图功能
|
||||
// 注意: 这需要在 Tauri 后端实现相应的命令
|
||||
const result = await invoke('capture_screenshot', {
|
||||
type,
|
||||
})
|
||||
|
||||
console.log('截图成功:', result)
|
||||
|
||||
// 刷新截图列表
|
||||
await loadScreenshots()
|
||||
|
||||
// 显示成功提示
|
||||
showNotification('截图成功', 'success')
|
||||
} catch (error) {
|
||||
console.error('截图失败:', error)
|
||||
showNotification('截图失败: ' + error.message, 'error')
|
||||
}
|
||||
}
|
||||
|
||||
// 加载截图列表
|
||||
async function loadScreenshots() {
|
||||
try {
|
||||
console.log('加载截图列表...')
|
||||
|
||||
// 调用 Tauri 后端获取截图列表
|
||||
// 注意: 这需要在 Tauri 后端实现相应的命令
|
||||
const screenshots = await invoke('get_screenshots', {})
|
||||
|
||||
store.screenshots = screenshots
|
||||
renderGallery()
|
||||
} catch (error) {
|
||||
console.error('加载截图失败:', error)
|
||||
store.screenshots = []
|
||||
renderGallery()
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染画廊
|
||||
function renderGallery() {
|
||||
if (!elements.galleryGrid) return
|
||||
|
||||
if (store.screenshots.length === 0) {
|
||||
elements.galleryGrid.innerHTML = `
|
||||
<div class="placeholder">
|
||||
<p>暂无截图</p>
|
||||
<small>截图后会自动显示在这里</small>
|
||||
</div>
|
||||
`
|
||||
return
|
||||
}
|
||||
|
||||
elements.galleryGrid.innerHTML = store.screenshots
|
||||
.map(
|
||||
screenshot => `
|
||||
<div class="gallery-item">
|
||||
<img src="${screenshot.path}" alt="${screenshot.name}">
|
||||
<div class="gallery-item-info">
|
||||
<span class="gallery-item-name">${screenshot.name}</span>
|
||||
<span class="gallery-item-date">${formatDate(screenshot.date)}</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
)
|
||||
.join('')
|
||||
}
|
||||
|
||||
// 设置拖拽上传
|
||||
function setupDragAndDrop() {
|
||||
const area = elements.uploadArea
|
||||
|
||||
area.addEventListener('dragover', e => {
|
||||
e.preventDefault()
|
||||
area.classList.add('dragover')
|
||||
})
|
||||
|
||||
area.addEventListener('dragleave', () => {
|
||||
area.classList.remove('dragover')
|
||||
})
|
||||
|
||||
area.addEventListener('drop', e => {
|
||||
e.preventDefault()
|
||||
area.classList.remove('dragover')
|
||||
|
||||
const files = Array.from(e.dataTransfer.files)
|
||||
handleFileUpload(files)
|
||||
})
|
||||
|
||||
area.addEventListener('click', () => {
|
||||
const input = document.createElement('input')
|
||||
input.type = 'file'
|
||||
input.accept = 'image/png,image/jpeg,image/jpg'
|
||||
input.multiple = true
|
||||
|
||||
input.addEventListener('change', e => {
|
||||
const files = Array.from(e.target.files)
|
||||
handleFileUpload(files)
|
||||
})
|
||||
|
||||
input.click()
|
||||
})
|
||||
}
|
||||
|
||||
// 处理文件上传
|
||||
async function handleFileUpload(files) {
|
||||
console.log('上传文件:', files)
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
// 调用 Tauri 后端上传文件
|
||||
// 注意: 这需要在 Tauri 后端实现相应的命令
|
||||
const result = await invoke('upload_screenshot', {
|
||||
path: file.path,
|
||||
})
|
||||
|
||||
console.log('上传成功:', result)
|
||||
showNotification('上传成功', 'success')
|
||||
} catch (error) {
|
||||
console.error('上传失败:', error)
|
||||
showNotification('上传失败: ' + error.message, 'error')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 加载设置
|
||||
async function loadSettings() {
|
||||
try {
|
||||
// 调用 Tauri 后端加载设置
|
||||
// 注意: 这需要在 Tauri 后端实现相应的命令
|
||||
const settings = await invoke('get_settings', {})
|
||||
|
||||
store.settings = { ...store.settings, ...settings }
|
||||
applySettings()
|
||||
} catch (error) {
|
||||
console.error('加载设置失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 应用设置
|
||||
function applySettings() {
|
||||
// 应用主题
|
||||
document.documentElement.setAttribute('data-theme', store.settings.theme)
|
||||
|
||||
// 应用其他设置
|
||||
console.log('应用设置:', store.settings)
|
||||
}
|
||||
|
||||
// 设置 Tauri 事件监听
|
||||
function setupTauriListeners() {
|
||||
// 监听截图完成事件
|
||||
listen('screenshot-taken', event => {
|
||||
console.log('收到截图事件:', event.payload)
|
||||
loadScreenshots()
|
||||
})
|
||||
|
||||
// 监听上传完成事件
|
||||
listen('upload-complete', event => {
|
||||
console.log('收到上传完成事件:', event.payload)
|
||||
showNotification('上传成功', 'success')
|
||||
})
|
||||
}
|
||||
|
||||
// 显示通知
|
||||
function showNotification(message, type = 'info') {
|
||||
console.log(`[${type}] ${message}`)
|
||||
|
||||
// TODO: 实现更完善的通知 UI
|
||||
const notification = document.createElement('div')
|
||||
notification.className = `notification notification-${type}`
|
||||
notification.textContent = message
|
||||
notification.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
padding: 12px 24px;
|
||||
background: ${type === 'success' ? 'var(--success-color)' : type === 'error' ? 'var(--danger-color)' : 'var(--primary-color)'};
|
||||
color: white;
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-lg);
|
||||
z-index: 1000;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
`
|
||||
|
||||
document.body.appendChild(notification)
|
||||
|
||||
setTimeout(() => {
|
||||
notification.style.animation = 'slideOut 0.3s ease-out'
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(notification)
|
||||
}, 300)
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
function formatDate(dateString) {
|
||||
const date = new Date(dateString)
|
||||
const now = new Date()
|
||||
const diff = now - date
|
||||
|
||||
if (diff < 60000) {
|
||||
return '刚刚'
|
||||
} else if (diff < 3600000) {
|
||||
return `${Math.floor(diff / 60000)} 分钟前`
|
||||
} else if (diff < 86400000) {
|
||||
return `${Math.floor(diff / 3600000)} 小时前`
|
||||
} else {
|
||||
return date.toLocaleDateString('zh-CN')
|
||||
}
|
||||
}
|
||||
|
||||
// 添加动画样式
|
||||
const style = document.createElement('style')
|
||||
style.textContent = `
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOut {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-area.dragover {
|
||||
border-color: var(--primary-color);
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
`
|
||||
document.head.appendChild(style)
|
||||
|
||||
// 初始化应用
|
||||
document.addEventListener('DOMContentLoaded', init)
|
||||
|
||||
// 导出 API 供其他模块使用
|
||||
export { store, switchView, handleCapture, loadScreenshots, showNotification }
|
||||
Reference in New Issue
Block a user