feat(用户): 实现用户信息获取及配置写入功能
添加获取用户信息的API接口和Pinia存储 在登录流程中增加用户信息获取和配置写入操作 新增opencode配置写入的IPC通信功能
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { app, BrowserWindow, shell, ipcMain, Menu } from 'electron';
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import net from 'node:net';
|
||||
import { spawn } from 'node:child_process';
|
||||
import started from 'electron-squirrel-startup';
|
||||
@@ -225,6 +226,48 @@ function registerIpcHandlers() {
|
||||
// Bonjour
|
||||
ipcMain.handle('bonjour:get-services', () => getDiscoveredServices());
|
||||
|
||||
// opencode 配置写入
|
||||
ipcMain.handle('opencode:write-config', async (_e, { modelInfo, deviceHost, devicePort }) => {
|
||||
const configDir = path.join(os.homedir(), '.config', 'opencode');
|
||||
const configPath = path.join(configDir, 'opencode.json');
|
||||
|
||||
await fs.promises.mkdir(configDir, { recursive: true });
|
||||
|
||||
const config = {
|
||||
$schema: 'https://opencode.ai/config.json',
|
||||
provider: {
|
||||
zhiju: {
|
||||
name: 'Zhiju AI',
|
||||
env: ['ZHIJU_API_KEY'],
|
||||
options: {
|
||||
baseURL: `http://${deviceHost}:${modelInfo.port}/v1`,
|
||||
apiKey: `${modelInfo.apiKey}`,
|
||||
},
|
||||
models: {
|
||||
[modelInfo.model_name]: {
|
||||
name: modelInfo.model_name,
|
||||
family: 'openai',
|
||||
status: modelInfo.status || 'beta',
|
||||
capabilities: modelInfo.capabilities || {
|
||||
reasoning: false,
|
||||
attachment: true,
|
||||
toolcall: true,
|
||||
input: { text: true, audio: false, image: true, video: false, pdf: true },
|
||||
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||
},
|
||||
limit: modelInfo.limit || { context: 128000, output: 4096 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
model: `zhiju/${modelInfo.model_name}`,
|
||||
};
|
||||
|
||||
await fs.promises.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
||||
console.log('[opencode] config written to:', configPath);
|
||||
return true;
|
||||
});
|
||||
|
||||
// 窗口控制
|
||||
ipcMain.on('window:minimize', (event) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender);
|
||||
|
||||
@@ -26,3 +26,7 @@ contextBridge.exposeInMainWorld('bonjour', {
|
||||
return () => ipcRenderer.removeListener('bonjour:services-updated', listener);
|
||||
},
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld('opencodeConfig', {
|
||||
write: (params) => ipcRenderer.invoke('opencode:write-config', params),
|
||||
});
|
||||
|
||||
@@ -67,9 +67,11 @@ import { ref, watch } from 'vue';
|
||||
import { User } from '@element-plus/icons-vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { useSparkStore } from '@/stores/spark';
|
||||
import { loginAction } from '@/http/api.js';
|
||||
import { loginAction, getUserInfoAction } from '@/http/api.js';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
|
||||
const sparkStore = useSparkStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@@ -115,13 +117,38 @@ async function handleLogin() {
|
||||
const device = sparkStore.devices.find((d) => d.name === form.value.sparkDevice);
|
||||
if (device) sparkStore.selectDevice(device);
|
||||
|
||||
const url = sparkStore.selectedDeviceUrl;
|
||||
console.log('[Login] spark device:', device);
|
||||
console.log('[Login] target url:', url);
|
||||
const selectedDevice = sparkStore.selectedDevice;
|
||||
console.log('[Login] spark device:', selectedDevice);
|
||||
console.log('[Login] target url:', sparkStore.selectedDeviceUrl);
|
||||
|
||||
await loginAction({ email: form.value.username, password: form.value.password });
|
||||
ElMessage.success(`登录成功 | ${url ?? '未选择设备'}`);
|
||||
emit('login-success', { username: form.value.username, device });
|
||||
|
||||
// 登录成功后获取用户信息并保存
|
||||
const userRes = await getUserInfoAction();
|
||||
const modelInfo = userRes.data?.xuanjian_model_info;
|
||||
userStore.setUserInfo({ nickname: userRes.data?.nickname, email: userRes.data?.email });
|
||||
|
||||
// 写入 opencode 配置文件
|
||||
if (modelInfo && selectedDevice) {
|
||||
try {
|
||||
const deviceHost = selectedDevice.host;
|
||||
console.log('[Config] modelInfo:', modelInfo);
|
||||
console.log('[Config] deviceHost:', deviceHost, 'port:', selectedDevice.port);
|
||||
await window.opencodeConfig.write({
|
||||
modelInfo,
|
||||
deviceHost: selectedDevice.host,
|
||||
devicePort: selectedDevice.port,
|
||||
});
|
||||
console.log('[Config] 写入成功');
|
||||
} catch (configErr) {
|
||||
console.error('[Config] 写入失败:', configErr);
|
||||
}
|
||||
} else {
|
||||
console.warn('[Config] 跳过写入,modelInfo:', modelInfo, 'selectedDevice:', selectedDevice);
|
||||
}
|
||||
|
||||
ElMessage.success(`登录成功 | ${sparkStore.selectedDeviceUrl ?? '未选择设备'}`);
|
||||
emit('login-success', { username: form.value.username, device: selectedDevice });
|
||||
visible.value = false;
|
||||
} catch (err) {
|
||||
ElMessage.error('登录失败,请重试');
|
||||
|
||||
@@ -8,6 +8,9 @@ export const getHealthAction = () => getAction(url.health);
|
||||
// 用户登录
|
||||
export const loginAction = (data) => postAction(url.user.login, { email: data.email, password: encryptPassword(data.password) });
|
||||
|
||||
// 获取用户信息
|
||||
export const getUserInfoAction = () => getAction(url.user.getUserInfo);
|
||||
|
||||
// 会话
|
||||
export const createSessionAction = (data) => postAction(url.session.create, data);
|
||||
export const getSessionAction = (id) => getAction(url.session.detail(id));
|
||||
|
||||
@@ -25,6 +25,8 @@ const url = {
|
||||
// 用户
|
||||
user: {
|
||||
login: '/v1/user/login',
|
||||
// 获取用户信息接口
|
||||
getUserInfo: '/v1/user/info',
|
||||
},
|
||||
|
||||
// SSE 事件流
|
||||
|
||||
25
src/renderer/stores/user.js
Normal file
25
src/renderer/stores/user.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
const STORAGE_KEY = 'user_info';
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
const userInfo = ref(JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null'));
|
||||
|
||||
const nickname = computed(() => userInfo.value?.nickname || '');
|
||||
const email = computed(() => userInfo.value?.email || '');
|
||||
const isLoggedIn = computed(() => !!userInfo.value);
|
||||
|
||||
function setUserInfo(info) {
|
||||
userInfo.value = info;
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(info));
|
||||
}
|
||||
|
||||
function clearUserInfo() {
|
||||
userInfo.value = null;
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
localStorage.removeItem('Authorization');
|
||||
}
|
||||
|
||||
return { userInfo, nickname, email, isLoggedIn, setUserInfo, clearUserInfo };
|
||||
});
|
||||
Reference in New Issue
Block a user