fix: 移除eslint功能
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"printWidth": 100,
|
||||
"printWidth": 120,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
|
||||
1
package-lock.json
generated
1
package-lock.json
generated
@@ -8883,7 +8883,6 @@
|
||||
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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}'`);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
42
src/renderer/components/base/LucideIcon.vue
Normal file
42
src/renderer/components/base/LucideIcon.vue
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user