fix: 移除eslint功能

This commit is contained in:
2026-04-10 15:50:05 +08:00
parent c07f2c8dbe
commit 5607d07586
10 changed files with 223 additions and 91 deletions

View File

@@ -1,5 +1,5 @@
{
"printWidth": 100,
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": true,

1
package-lock.json generated
View File

@@ -8883,7 +8883,6 @@
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},

View File

@@ -1,8 +1,8 @@
{
"name": "my-app",
"productName": "my-app",
"name": "Zhiju",
"productName": "Zhiju",
"version": "1.0.0",
"description": "My Electron application description",
"description": "Zhiju Ai Assistant",
"main": ".vite/build/main.js",
"private": true,
"scripts": {
@@ -16,7 +16,7 @@
"format:check": "prettier --check ."
},
"keywords": [],
"author": "houakang",
"author": "zhiju.com.cn",
"license": "MIT",
"devDependencies": {
"@commitlint/cli": "^20.5.0",

View File

@@ -1,4 +1,4 @@
import { app, BrowserWindow, shell, ipcMain } from 'electron';
import { app, BrowserWindow, shell, ipcMain, Menu } from 'electron';
import path from 'node:path';
import fs from 'node:fs';
import net from 'node:net';
@@ -129,9 +129,7 @@ async function startOpencode() {
});
opencodeProcess.stdout?.on('data', (d) => console.log(`[opencode] ${d.toString().trim()}`));
opencodeProcess.stderr?.on('data', (d) =>
console.error(`[opencode error] ${d.toString().trim()}`)
);
opencodeProcess.stderr?.on('data', (d) => console.error(`[opencode error] ${d.toString().trim()}`));
opencodeProcess.once('error', (e) => console.error('[opencode spawn error]', e));
opencodeProcess.once('close', (code) => {
console.log(`[opencode exited] code=${code}`);
@@ -203,10 +201,31 @@ function registerIpcHandlers() {
// Bonjour
ipcMain.handle('bonjour:get-services', () => getDiscoveredServices());
// 窗口控制
ipcMain.on('window:minimize', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
win?.minimize();
});
ipcMain.on('window:maximize', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win?.isMaximized()) {
win.unmaximize();
} else {
win?.maximize();
}
});
ipcMain.on('window:close', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
win?.close();
});
}
// ========== 窗口 ==========
const createWindow = () => {
// 移除菜单栏,保留窗口边框和原生按钮
Menu.setApplicationMenu(null);
const mainWindow = new BrowserWindow({
width: 1280,
height: 800,
@@ -217,10 +236,11 @@ const createWindow = () => {
contextIsolation: true,
nodeIntegration: false,
},
titleBarStyle: 'hiddenInset',
frame: false,
show: false,
});
mainWindow.webContents.openDevTools();
mainWindow.once('ready-to-show', () => mainWindow.show());
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
@@ -231,9 +251,7 @@ const createWindow = () => {
// 注入 baseUrl让渲染进程的 getBaseUrl() 能拿到正确端口
mainWindow.webContents.on('did-finish-load', () => {
if (opencodePort) {
mainWindow.webContents.executeJavaScript(
`window.__opencodeBaseUrl = 'http://127.0.0.1:${opencodePort}'`
);
mainWindow.webContents.executeJavaScript(`window.__opencodeBaseUrl = 'http://127.0.0.1:${opencodePort}'`);
}
});

View File

@@ -0,0 +1,42 @@
<script setup>
import { computed } from 'vue';
import * as LucideIcons from 'lucide-vue-next';
const props = defineProps({
name: {
type: String,
required: true,
},
size: {
type: [Number, String],
default: 24,
},
color: {
type: String,
default: 'currentColor',
},
strokeWidth: {
type: [Number, String],
default: 2,
},
});
const icon = computed(() => {
// Lucide 图标通常是 PascalCase用户输入可能是 kebab-case
const pascalName = props.name
.split('-')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join('');
return LucideIcons[pascalName] || LucideIcons[props.name] || null;
});
</script>
<template>
<component :is="icon" v-if="icon" :size="size" :color="color" :stroke-width="strokeWidth" class="lucide-icon" />
<span
v-else
class="lucide-icon-fallback"
:style="{ width: size + 'px', height: size + 'px', display: 'inline-block' }"
></span>
</template>

View File

@@ -1,18 +1,58 @@
<template>
<div class="flex h-screen w-screen overflow-hidden bg-gray-50">
<div class="flex flex-col h-screen w-screen overflow-hidden">
<!-- 自定义标题栏 -->
<nav class="flex items-center justify-between h-8 bg-white border-b border-gray-100 shrink-0 select-none z-50">
<!-- 左侧缩放菜单按钮 -->
<div class="flex items-center px-4 no-drag">
<el-button link size="small" class="p-1! hover:bg-gray-100 rounded" @click="appStore.toggleSidebar">
<LucideIcon v-show="appStore.collapsed" name="panel-left-dashed" size="16" color="#000"></LucideIcon>
<LucideIcon v-show="!appStore.collapsed" name="panel-right-dashed" size="16" color="#000"></LucideIcon>
</el-button>
<span class="ml-4 text-sm font-medium pointer-events-none">{{ appStore.title }}</span>
</div>
<!-- 中间拖拽区域 -->
<div class="flex-1 h-full"></div>
<!-- 右侧窗口控制按钮 -->
<div class="flex items-center h-full no-drag">
<button
class="flex items-center justify-center w-10 h-full hover:bg-gray-100 transition-colors"
@click="handleMinimize"
>
<el-icon :size="14"><SemiSelect /></el-icon>
</button>
<button
class="flex items-center justify-center w-10 h-full hover:bg-gray-100 transition-colors"
@click="handleMaximize"
>
<el-icon :size="12"><CopyDocument /></el-icon>
</button>
<button
class="flex items-center justify-center w-10 h-full hover:bg-red-500 hover:text-white transition-colors"
@click="handleClose"
>
<el-icon :size="14"><Close /></el-icon>
</button>
</div>
</nav>
<div class="flex flex-1 overflow-hidden">
<!-- 侧边栏 -->
<aside
:class="[
'flex flex-col bg-white border-r border-gray-200 transition-all duration-300',
'flex flex-col bg-white border-r border-gray-200 transition-all duration-300 no-drag',
appStore.collapsed ? 'w-16' : 'w-56',
]"
>
<!-- Logo -->
<div class="flex items-center h-14 px-4 border-b border-gray-200 shrink-0">
<el-icon class="text-blue-500 text-xl shrink-0"><Monitor /></el-icon>
<span v-if="!appStore.collapsed" class="ml-2 font-semibold text-gray-800 truncate">
<template v-if="!appStore.collapsed">
<el-button class="ml-2 font-semibold text-gray-800 truncate" link @click="showAppInfo = true">
{{ appStore.title }}
</span>
</el-button>
</template>
</div>
<!-- 导航菜单 -->
@@ -39,21 +79,14 @@
<!-- 折叠按钮 -->
<div class="p-3 border-t border-gray-200">
<el-button
:icon="appStore.collapsed ? Expand : Fold"
circle
size="small"
@click="appStore.toggleSidebar"
/>
<el-button :icon="appStore.collapsed ? Expand : Fold" circle size="small" @click="appStore.toggleSidebar" />
</div>
</aside>
<!-- 主内容区 -->
<div class="flex flex-col flex-1 overflow-hidden">
<!-- 顶部栏 -->
<header
class="flex items-center justify-between h-14 px-6 bg-white border-b border-gray-200 shrink-0"
>
<header class="flex items-center justify-between h-14 px-6 bg-white border-b border-gray-200 shrink-0">
<h1 class="text-base font-medium text-gray-700">{{ currentTitle }}</h1>
<div class="flex items-center gap-2">
<el-avatar :size="32" class="bg-blue-500">U</el-avatar>
@@ -68,16 +101,56 @@
</main>
</div>
</div>
<!-- 应用信息弹窗 -->
<el-dialog v-model="showAppInfo" :title="appStore.title" width="400">
<div class="p-4 text-center">
<p class="text-lg font-medium mb-2">欢迎使用 {{ appStore.title }}</p>
<p class="text-sm text-gray-500">这是一个基于 Electron Vue 3 的应用示例</p>
</div>
<template #footer>
<div class="flex justify-end">
<el-button type="primary" @click="showAppInfo = false">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { computed } from 'vue';
import { computed, ref } from 'vue';
import { useRoute } from 'vue-router';
import { useAppStore } from '@/stores/app';
import { House, Monitor, Expand, Fold, Edit, ChatDotRound, Search } from '@element-plus/icons-vue';
import {
House,
Monitor,
Expand,
Fold,
Edit,
ChatDotRound,
Search,
SemiSelect,
CopyDocument,
Close,
} from '@element-plus/icons-vue';
import LucideIcon from '../components/base/LucideIcon.vue';
const route = useRoute();
const appStore = useAppStore();
const showAppInfo = ref(false);
const currentTitle = computed(() => route.meta?.title || appStore.title);
const handleMinimize = () => {
window.electronAPI.send('window:minimize');
};
const handleMaximize = () => {
window.electronAPI.send('window:maximize');
};
const handleClose = () => {
window.electronAPI.send('window:close');
};
</script>

View File

@@ -7,6 +7,7 @@ import router from './router';
import App from './App.vue';
import './style.css';
import AppIcon from './components/base/AppIcon.vue';
import LucideIcon from './components/base/LucideIcon.vue';
const app = createApp(App);
// 注册所有 Element Plus 图标
@@ -16,6 +17,7 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
// 注册自定义图标组件
app.component('AppIcon', AppIcon);
app.component('LucideIcon', LucideIcon);
app.use(createPinia());
app.use(router);
app.use(ElementPlus);

View File

@@ -2,7 +2,7 @@ import { defineStore } from 'pinia';
import { ref } from 'vue';
export const useAppStore = defineStore('app', () => {
const title = ref('My App');
const title = ref('智聚超脑');
const collapsed = ref(false);
function toggleSidebar() {

View File

@@ -111,6 +111,15 @@ body,
height: 100%;
width: 100%;
overflow: hidden;
-webkit-app-region: drag;
}
button,
input,
textarea,
a,
.no-drag {
-webkit-app-region: no-drag;
}
body {

View File

@@ -8,8 +8,8 @@
<span class="emoji">👋</span>
</h1>
<p class="welcome-subtitle">准备好开始今天的创作了吗</p>
<AppIcon :icon="User" color="red" />
<AppIcon :icon="Rocket" :size="30" class="hover:text-red-500" />
<LucideIcon name="user" color="red" />
<LucideIcon name="rocket" :size="30" class="hover:text-red-500" />
</div>
</div>
@@ -48,12 +48,7 @@
</div>
</template>
<div class="action-grid">
<div
v-for="action in actions"
:key="action.label"
class="action-item"
@click="action.onClick"
>
<div v-for="action in actions" :key="action.label" class="action-item" @click="action.onClick">
<div class="action-icon" :style="{ background: action.color + '20' }">
<el-icon :size="28" :color="action.color">
<component :is="action.icon" />
@@ -76,12 +71,7 @@
</template>
<el-scrollbar height="280px">
<div class="recent-list">
<div
v-for="(item, index) in recents"
:key="index"
class="recent-item"
@click="handleFileClick(item)"
>
<div v-for="(item, index) in recents" :key="index" class="recent-item" @click="handleFileClick(item)">
<div class="file-icon">
<el-icon :size="20"><Document /></el-icon>
</div>
@@ -101,7 +91,6 @@
<script setup>
import { useRouter } from 'vue-router';
import { User, Rocket } from 'lucide-vue-next';
import {
Document,
Plus,