diff --git a/.github/workflows/build-app.yml b/.github/workflows/build-app.yml index c7a22a8a..eed3e0aa 100644 --- a/.github/workflows/build-app.yml +++ b/.github/workflows/build-app.yml @@ -61,7 +61,7 @@ jobs: - name: Load Node.js uses: actions/setup-node@v3 with: - node-version: 14.x + node-version: 16.x # 更新依赖 - name: Install run: yarn @@ -104,7 +104,7 @@ jobs: - name: Load Node.js uses: actions/setup-node@v3 with: - node-version: 14.x + node-version: 16.x # 更新依赖 - name: Install run: yarn diff --git a/package.json b/package.json index 0e6f6e81..5155d9b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stapxs-qq-lite", - "version": "2.6.6", + "version": "2.6.7", "private": false, "author": "Stapx Steve [林槐]", "description": "一个兼容 oicq-http 的非官方网页版 QQ 客户端,使用 Vue 重制的全新版本。", @@ -13,7 +13,6 @@ "postinstall": "electron-builder install-app-deps", "postuninstall": "electron-builder install-app-deps" }, - "main": "background.js", "dependencies": { "@bitprojects/umami-logger-typescript": "^1.0.10", "@jakejarrett/gtk-theme": "^2.0.1", @@ -59,11 +58,11 @@ "@vue/cli-service": "~5.0.0", "@vue/eslint-config-typescript": "^9.1.0", "child_process": "^1.0.2", - "electron": "^20.0.0", - "electron-devtools-installer": "^3.1.0", + "electron": "^27.0.0", + "electron-devtools-installer": "^3.2.0", "eslint": "^7.32.0", "eslint-plugin-vue": "^8.0.3", "typescript": "~4.5.5", - "vue-cli-plugin-electron-builder": "~2.1.1" + "vue-cli-plugin-electron-builder": "^3.0.0-alpha.4" } } diff --git a/src/App.vue b/src/App.vue index c4cb1bda..bf531c39 100644 --- a/src/App.vue +++ b/src/App.vue @@ -43,7 +43,7 @@ d="M464 64C490.5 64 512 85.49 512 112C512 127.1 504.9 141.3 492.8 150.4L275.2 313.6C263.8 322.1 248.2 322.1 236.8 313.6L19.2 150.4C7.113 141.3 0 127.1 0 112C0 85.49 21.49 64 48 64H464zM217.6 339.2C240.4 356.3 271.6 356.3 294.4 339.2L512 176V384C512 419.3 483.3 448 448 448H64C28.65 448 0 419.3 0 384V176L217.6 339.2z" /> -
  • +
  • @@ -166,7 +166,7 @@
    -
    { return 'undefined' } - // MacOS:初始化菜单 - const electron = (process.env.IS_ELECTRON as any) === true ? window.require('electron') : null - const reader = electron ? electron.ipcRenderer : null - if (reader) { - reader.send('sys:updateMenu') - } // 页面加载完成后 window.onload = () => { + createMenu() + createIpc() + // 加载开发者相关 + if (process.env.NODE_ENV == 'development') { + document.title = 'Stapxs QQ Lite (Dev)' + // 布局检查工具 + Spacing.start() + } app.config.globalProperties.$viewer = this.viewerBody // 初始化波浪动画 runtimeData.tags.loginWaveTimer = this.waveAnimation(document.getElementById('login-wave')) // 加载设置项 runtimeData.sysConfig = Option.load() // PS:重新再应用部分需要加载完成后才能应用的设置 - Option.runAS('opt_dark', Option.get('opt_dark')) - Option.runAS('opt_auto_dark', Option.get('opt_auto_dark')) - Option.runAS('theme_color', Option.get('theme_color')) - Option.runAS('opt_auto_win_color', Option.get('opt_auto_win_color')) + Option.run('opt_dark', Option.get('opt_dark')) + Option.run('opt_auto_dark', Option.get('opt_auto_dark')) + Option.run('theme_color', Option.get('theme_color')) + Option.run('opt_auto_win_color', Option.get('opt_auto_win_color')) if(Option.get('opt_no_window') == true) { const app = document.getElementById('base-app') if(app) app.classList.add('withBar') @@ -467,12 +470,6 @@ export default defineComponent({ // 初始化完成 logger.debug(this.$t('log_welcome')) logger.debug(this.$t('log_runtime') + ': ' + process.env.NODE_ENV) - // 加载开发者相关 - if (process.env.NODE_ENV == 'development') { - document.title = 'Stapxs QQ Lite (Dev)' - // 布局检查工具 - Spacing.start() - } // UM:加载 Umami 统计功能 if (!Option.get('close_ga') && process.env.NODE_ENV == 'production') { Umami.initialize({ diff --git a/src/assets/css/view.css b/src/assets/css/view.css index 9e8a6f06..d4c852b1 100644 --- a/src/assets/css/view.css +++ b/src/assets/css/view.css @@ -665,6 +665,16 @@ html, body { margin: 60px 20px 0 20px; width: calc(100% - 80px); } +.pop-box-body.full iframe { + height: calc(100vh - 145px); +} +.pop-box-body.full.window { + height: calc(100vh - 80px); + margin: 20px 20px 0 20px; +} +.pop-box-body.full.window iframe { + height: calc(100vh - 105px); +} .pop-box-body > header { align-items: center; display: flex; diff --git a/src/assets/l10n/zh-CN.json b/src/assets/l10n/zh-CN.json index 97ca723b..391e8285 100644 --- a/src/assets/l10n/zh-CN.json +++ b/src/assets/l10n/zh-CN.json @@ -275,7 +275,7 @@ "option_dev_restart": "重启应用", "option_dev_restart_tip": "99% 的特性都能通过重启解决!", "btn_close": "关闭", - "btn_open": "打开", + "btn_open": "打开…", "pop_send_file_fail": "发送文件失败", "pop_send_file_err": "发送文件错误", "pop_send_file": "正在发送文件 {percent}%", @@ -299,5 +299,30 @@ "sw_updated_1": "应用已更新,刷新生效。", "chat_show_msg_error": "解析消息错误", "botinfo_protocol_version": "OneBot 版本", - "botinfo_nt_protocol": "NT 协议版本" + "botinfo_nt_protocol": "NT 协议版本", + "load_success": "应用显示完成,应用初始化完成!欢迎使用 {name}!", + "pop_reload_user_success": "刷新用户列表成功", + "pop_log_con_close": "正在断开链接……", + + "menu_about": "关于", + "menu_update": "检查更新…", + "menu_hide": "隐藏", + "menu_hide_others": "隐藏其他", + "menu_unhide": "全部显示", + "menu_quit": "退出", + "menu_account": "账户", + "menu_user_list": "用户列表({count})", + "menu_flush_user": "刷新列表…", + "menu_logout": "登出", + "menu_edit": "编辑", + "menu_undo": "撤销", + "menu_redo": "重做", + "menu_cut": "剪切", + "menu_copy": "复制", + "menu_paste": "粘贴", + "menu_select_all": "全选", + "menu_help": "帮助", + "menu_doc": "帮助文档", + "menu_feedback": "在 Github 上反馈问题", + "menu_license": "许可协议" } \ No newline at end of file diff --git a/src/background.ts b/src/background.ts index d7db1d29..3123361d 100644 --- a/src/background.ts +++ b/src/background.ts @@ -2,10 +2,11 @@ import Store from 'electron-store' import windowStateKeeper from 'electron-window-state' -import regIpcListener from './function/electron/ipc' +import { regIpcListener } from './function/electron/ipc' import path from 'path' +import { version as appVersion } from '../package.json' -import installExtension, { VUEJS3_DEVTOOLS } from 'electron-devtools-installer' +import installExtension from 'electron-devtools-installer' import { Menu, session } from 'electron' import { app, protocol, BrowserWindow } from 'electron' @@ -20,10 +21,20 @@ protocol.registerSchemesAsPrivileged([ export let win = undefined as BrowserWindow | undefined async function createWindow() { - console.log('开始创建窗口 ……') - // 窗口创建前事务 + console.log('') + console.log('███████╗████████╗ █████╗ ██████╗ ██╗ ██╗') + console.log('██╔════╝╚══██╔══╝██╔══██╗██╔══██╗╚██╗██╔╝') + console.log('███████╗ ██║ ███████║██████╔╝ ╚███╔╝ ') + console.log('╚════██║ ██║ ██╔══██║██╔═══╝ ██╔██╗ ') + console.log('███████║ ██║ ██║ ██║██║ ██╔╝ ██╗') + console.log('╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝') + console.log('===========================================') + console.log('Welcome to Stapxs QQ Lite, current version: ' + appVersion) + console.log('The background language component will be initialized after the frontend is loaded.') + + console.log('Platform:' + process.platform) + console.log('Start creating main window ……') Menu.setApplicationMenu(null) - regIpcListener() // 创建窗口 const mainWindowState = windowStateKeeper({ defaultWidth: 1200, @@ -31,7 +42,6 @@ async function createWindow() { }) const store = new Store() const noWindow = await store.get('opt_no_window') - console.log('窗口框架状态:' + noWindow) win = new BrowserWindow({ x: mainWindowState.x, y: mainWindowState.y, @@ -46,7 +56,9 @@ async function createWindow() { }) win.once('focus', () => {if(win)win.flashFrame(false)}) mainWindowState.manage(win) // 窗口状态管理器 - console.log('窗口创建完成') + console.log('Create main window to complete.') + // 注册 IPC 事务 + regIpcListener() // 加载应用 if (process.env.WEBPACK_DEV_SERVER_URL) { await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string) @@ -55,7 +67,6 @@ async function createWindow() { createProtocol('app') win.loadURL('app://./index.html') } - console.log('应用加载完成') session.defaultSession.webRequest.onHeadersReceived((details, callback) => { if(details.responseHeaders) { @@ -68,9 +79,9 @@ async function createWindow() { } app.on('window-all-closed', () => { - if (process.platform !== 'darwin') { + // if (process.platform !== 'darwin') { app.quit() - } + // } }) app.on('activate', () => { @@ -80,12 +91,7 @@ app.on('activate', () => { app.on('ready', async () => { if (isDevelopment && !process.env.IS_TEST) { try { - await installExtension(VUEJS3_DEVTOOLS) - // 这是个谷歌分析调试工具,好像用不了?? - // await installExtension({ - // id: 'ilnpmccnfdjdjjikgkefkcegefikecdc', - // electron: '>=1.2.1' - // }) + await installExtension('nhdogjmejiglipccpnnnanhbledajbpd') } catch (e: any) { console.error('Vue Devtools failed to install:', e.toString()) } diff --git a/src/components/WelPan.vue b/src/components/WelPan.vue index 6c81b7b8..84293d0f 100644 --- a/src/components/WelPan.vue +++ b/src/components/WelPan.vue @@ -64,7 +64,8 @@ export default defineComponent({ const sender = event.target as HTMLInputElement // UM:上传语言选择 Umami.trackEvent('use_language', { name: sender.value }) - + // 刷新菜单 + // TODO }, setPage(name: string) { this.show = name diff --git a/src/function/connect.ts b/src/function/connect.ts index af1868a8..5bf6ed0f 100644 --- a/src/function/connect.ts +++ b/src/function/connect.ts @@ -14,6 +14,7 @@ import { LogType, Logger, PopType, PopInfo } from './base' import { parse, runtimeData, resetRimtime } from './msg' import { BotActionElem, LoginCacheElem } from './elements/system' +import { updateMenu } from "@/utils/appUtil" const logger = new Logger() const popInfo = new PopInfo() @@ -82,6 +83,11 @@ export class Connector { } websocket.onclose = (e) => { websocket = undefined + updateMenu({ + id: 'account', + action: 'visible', + value: false + }) switch(e.code) { case 1000: break; // 正常关闭 @@ -122,6 +128,7 @@ export class Connector { * 正常断开 Websocket 连接 */ static close() { + popInfo.add(PopType.INFO, app.config.globalProperties.$t('pop_log_con_close')) if(websocket) websocket.close(1000) } diff --git a/src/function/electron/ipc.ts b/src/function/electron/ipc.ts index 832800cd..f7e034ce 100644 --- a/src/function/electron/ipc.ts +++ b/src/function/electron/ipc.ts @@ -2,12 +2,15 @@ import Store from 'electron-store' import path from 'path' import os from 'os' -import { ipcMain, shell, systemPreferences, app } from "electron" +import { ipcMain, shell, systemPreferences, app, Menu, MenuItemConstructorOptions } from "electron" import { GtkTheme, GtkData } from '@jakejarrett/gtk-theme' import { queryKeys } from './util' import { win } from '@/background' -export default function regIpcListener() { +const store = new Store() +let template = [] as any[] + +export function regIpcListener() { // 关闭窗口 ipcMain.on('win:close', () => { if(win) win.close() @@ -25,11 +28,22 @@ export default function regIpcListener() { app.relaunch() app.exit() }) - // 单独用于保存窗口框架是否显示的设置 - // PS:因为改变窗口框架需要在窗口创建前设置,所以单独保存设置便于获取 - ipcMain.on('opt:saveNoWindow', (event, arg) => { - const store = new Store() - store.set('opt_no_window', Boolean(arg)) + // 保存设置 + // PS:升级至 electron 27 后 cookie 已完全无法持久化,只能进行保存 + ipcMain.on('opt:saveAll', (event, arg) => { + store.store = arg + }) + // 获取设置 + ipcMain.on('opt:getAll', (event, arg) => { + event.returnValue = store.store + }) + ipcMain.on('opt:get', (event, arg) => { + event.returnValue = store.get(arg) + }) + // 重置设置 + ipcMain.on('opt:clearAll', (event, arg) => { + store.clear() + event.returnValue = true }) // 获取补充的调试信息 ipcMain.handle('opt:getSystemInfo', () => { @@ -38,21 +52,6 @@ export default function regIpcListener() { systemInfo.os = os.homedir() return systemInfo }) - // 获取 GTK 主题 CSS - ipcMain.handle('sys:getGTKTheme', () => { - const gtkTheme = new GtkTheme({events: { - themeChange: (data: GtkData) => { - console.log('GTK 主题修改:' + data.name) - const info = {} as {[key:string]:any} - info.name = data.name - info.css = data.gtk.css - if(win) { - win.webContents.send('sys:updateGTKTheme', info) - } - } - }}) - return gtkTheme.getTheme().gtk.css - }) // 打开开发者工具 ipcMain.on('win:openDevTools', () => { if(win) win.webContents.openDevTools() @@ -61,30 +60,6 @@ export default function regIpcListener() { ipcMain.on('win:fouesWindow', () => { if(win) win.focus() }) - // Windows 的特有功能,闪烁状态栏图标 - ipcMain.on('win:flashWindow', () => { - if(win) win.flashFrame(true) - }) - // Winodws 通过注册表获取系统主题色 - ipcMain.handle('sys:getWinColor', async () => { - // 订阅颜色修改事件 - systemPreferences.addListener('accent-color-changed', async () => { - if(win) { - win.webContents.send('sys:WinColorChanged', await getWinSysColor()) - } - }) - return getWinSysColor() - }) - async function getWinSysColor() { - const keyPath = 'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\DWM\\' - try { - const info = await queryKeys(keyPath, 'AccentColor') - const color = info.stdout.substring(info.stdout.lastIndexOf('0xff') + 4) - return { color: [parseInt('0x' + color.substring(4, 6)), parseInt('0x' + color.substring(2, 4)), parseInt('0x' + color.substring(0, 2))] } - } catch(ex) { - return { err: (ex as Error).message } - } - } // 下载文件 ipcMain.on('sys:download', (evt, args) => { const downloadPath = args.downloadPath @@ -120,4 +95,158 @@ export default function regIpcListener() { win.webContents.downloadURL(downloadPath) } }) + + // Windows:闪烁状态栏图标 + ipcMain.on('win:flashWindow', () => { + if(win) win.flashFrame(true) + }) + // Winodws:通过注册表获取系统主题色 + ipcMain.handle('sys:getWinColor', async () => { + // 订阅颜色修改事件 + systemPreferences.addListener('accent-color-changed', async () => { + if(win) { + win.webContents.send('sys:WinColorChanged', await getWinSysColor()) + } + }) + return getWinSysColor() + }) + async function getWinSysColor() { + const keyPath = 'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\DWM\\' + try { + const info = await queryKeys(keyPath, 'AccentColor') + const color = info.stdout.substring(info.stdout.lastIndexOf('0xff') + 4) + return { color: [parseInt('0x' + color.substring(4, 6)), parseInt('0x' + color.substring(2, 4)), parseInt('0x' + color.substring(0, 2))] } + } catch(ex) { + return { err: (ex as Error).message } + } + } + + // Linux:获取 GTK 主题 CSS + ipcMain.handle('sys:getGTKTheme', () => { + const gtkTheme = new GtkTheme({events: { + themeChange: (data: GtkData) => { + console.log('GTK 主题修改:' + data.name) + const info = {} as {[key:string]:any} + info.name = data.name + info.css = data.gtk.css + if(win) { + win.webContents.send('sys:updateGTKTheme', info) + } + } + }}) + return gtkTheme.getTheme().gtk.css + }) + + // MacOS:初始化菜单 + // PS:由于本地化的存在,需要让 vue 获取到本地化信息之后再由 electron 构建 + ipcMain.on('sys:createMenu', (event, args) => { + console.log(args.success) + if (process.platform === 'darwin') { + template = [ + { + label: args.title, + submenu: [ + { label: args.about, click: () => { sendMenuClick('app:about') } }, + { label: args.update }, + { type: 'separator' }, + { label: args.hide, role: 'hide' }, + { label: args.hideOthers, role: 'hideothers' }, + { label: args.unhide, role: 'unhide' }, + { type: 'separator' }, + { label: args.quit, role: 'quit' } + ], + }, { + label: args.edit, + role: 'editMenu', + submenu: [ + { label: args.undo, role: 'undo' }, + { label: args.redo, role: 'redo' }, + { type: 'separator' }, + { label: args.cut, role: 'cut' }, + { label: args.copy, role: 'copy' }, + { label: args.paste, role: 'paste' }, + { type: 'separator' }, + { label: args.selectAll, role: 'selectall' } + ], + }, { + id: 'account', + label: args.account, + visible: false, + submenu: [ + { id: 'userName', label: '' }, + { type: 'separator' }, + { id: 'userList', label: args.userList, click: () => { sendMenuClick('app:changeTab', 'Friends') } }, + { label: args.flushUser, click: () => { sendMenuClick('bot:flushUser') } }, + { type: 'separator' }, + { label: args.logout, click: () => { sendMenuClick('bot:logout') } } + ], + }, { + label: args.help, + role: 'help', + submenu: [ + { label: args.doc, click: () => { sendMenuClick('app:openLink', 'https://github.com/Stapxs/Stapxs-QQ-Lite-2.0/wiki') } }, + { label: args.feedback, click: () => { sendMenuClick('app:openLink', 'https://github.com/Stapxs/Stapxs-QQ-Lite-2.0/issues') } }, + { type: 'separator' }, + { label: args.license, click: () => { sendMenuClick('app:openLink', 'https://github.com/Stapxs/Stapxs-QQ-Lite-2.0/blob/master/LICENSE') } } + ], + } + ] as MenuItemConstructorOptions[] + Menu.setApplicationMenu(Menu.buildFromTemplate(template)) + } + }) + ipcMain.on('sys:updateMenu', (event, args) => { + if (process.platform === 'darwin') { + const id = args.id + const action = args.action + const value = args.value + // 在 template 里寻找这个 id 修改对应的值 + let menuIndex = -1; + let itemIndex = -1; + for (let i = 0; i < template.length; i++) { + const menuItem = template[i] + if(menuItem.id == id) { + menuIndex = i + break + } + } + if (menuIndex == -1) { + for (let i = 0; i < template.length; i++) { + const menuItem = template[i] + const submenu = menuItem.submenu as MenuItemConstructorOptions[]; + if (submenu && submenu.length > 0) { + for (let j = 0; j < submenu.length; j++) { + const subMenuItem = submenu[j] + if (subMenuItem.id == id) { + menuIndex = i + itemIndex = j + break + } + } + } + } + } + if (menuIndex > -1) { + switch (action) { + case 'label': itemIndex > -1 ? + template[menuIndex].submenu[itemIndex].label = value : + template[menuIndex].label = value + break + case 'visible': itemIndex > -1 ? + template[menuIndex].submenu[itemIndex].visible = value : + template[menuIndex].visible = value + break + } + } + Menu.setApplicationMenu(Menu.buildFromTemplate(template)) + } + }) + function sendMenuClick(name: string, value = undefined as any) { + if(win) { + if(value) { + win.webContents.send(name, value) + } else { + win.webContents.send(name) + } + } + } } diff --git a/src/function/msg.ts b/src/function/msg.ts index 010a4f7d..69e2a674 100644 --- a/src/function/msg.ts +++ b/src/function/msg.ts @@ -20,7 +20,7 @@ import Umami from '@bitprojects/umami-logger-typescript' import { buildMsgList, getMsgData, parseMsgList, getMsgRawTxt } from '@/utils/msgUtil' import { htmlDecodeByRegExp, randomNum } from '@/utils/systemUtil' -import { reloadUsers, downloadFile } from '@/utils/appUtil' +import { reloadUsers, downloadFile, updateMenu } from '@/utils/appUtil' import { reactive, nextTick, markRaw, defineAsyncComponent } from 'vue' import { PopInfo, PopType, Logger, LogType } from './base' import { Connector, login } from './connect' @@ -148,6 +148,9 @@ function saveLoginInfo(msg: { [key: string]: any }) { // 完成登陆初始化 runtimeData.loginInfo = data login.status = true + // 显示账户菜单 + updateMenu({ id: 'account', action: 'visible', value: true }) + updateMenu({ id: 'userName', action: 'label', value: data.nickname }) // 结束登录页面的水波动画 clearInterval(runtimeData.tags.loginWaveTimer) // 跳转标签卡 @@ -210,6 +213,12 @@ function saveUser(msg: { [key: string]: any }, type: string) { }) } } + // 更新菜单 + updateMenu({ + id: 'userList', + action: 'label', + value: app.config.globalProperties.$t('menu_user_list', { count: runtimeData.userList.length }) + }) } } diff --git a/src/function/option.ts b/src/function/option.ts index 92c84425..f9e67647 100644 --- a/src/function/option.ts +++ b/src/function/option.ts @@ -45,16 +45,7 @@ const configFunction: { [key: string]: (value: any) => void } = { initial_scale: changeInitialScale, msg_type: setMsgType, opt_auto_gtk: updateGTKColor, - opt_auto_win_color: updateWinColorOpt, - opt_no_window: changeNoWindow -} - -function changeNoWindow(value: boolean) { - const electron = (process.env.IS_ELECTRON as any) === true ? window.require('electron') : null - const reader = electron ? electron.ipcRenderer : null - if(reader) { - reader.send('opt:saveNoWindow', value) - } + opt_auto_win_color: updateWinColorOpt } function updateWinColorOpt(value: boolean) { @@ -278,34 +269,54 @@ function changeChatView(name: string | undefined) { * @returns 设置项集合 */ export function load(): { [key: string]: any } { - const options: { [key: string]: any } = {} - const str = localStorage.getItem('options') - if (str != null) { - const list = str.split('&') - for (let i = 0; i <= list.length; i++) { - if (list[i] !== undefined) { - const opt: string[] = list[i].split(':') - if (opt.length === 2) { - if (opt[1] === 'true' || opt[1] === 'false') { - // 特殊处理被字符串化的布尔值 - options[opt[0]] = (opt[1] === 'true') - } else if(opt[0] == 'top_info') { - // 特殊处理 top_info - try { - options[opt[0]] = JSON.parse(decodeURIComponent(opt[1])) - } catch (e) { - // 无法解析的数据,初始化为空对象 - options[opt[0]] = {} - } - } else { - options[opt[0]] = decodeURIComponent(opt[1]) + let data = {} as { [key: string]: any } + + const electron = (process.env.IS_ELECTRON as any) === true ? window.require('electron') : null + const reader = electron ? electron.ipcRenderer : null + if (reader) { + data = reader.sendSync('opt:getAll') + } else { + const str = localStorage.getItem('options') + if (str != null) { + const list = str.split('&') + for (let i = 0; i <= list.length; i++) { + if (list[i] !== undefined) { + const opt: string[] = list[i].split(':') + if (opt.length === 2) { + data[opt[0]] = opt[1] } - // 执行设置项操作 - run(opt[0], opt[1]) } } } } + return loadOptData(data) +} + +function loadOptData(data: { [key: string]: any }) { + console.log(data) + const options: { [key: string]: any } = {} + Object.keys(data).forEach(function (key) { + const value = data[key] + if (value === 'true' || value === 'false') { + options[key] = value === 'true' + } else if (value === 'null') { + options[key] = null + } else if (key == 'top_info') { + // 特殊处理 top_info + try { + options[key] = JSON.parse(decodeURIComponent(value)) + } catch (e) { + // 无法解析的数据,初始化为空对象 + options[key] = {} + } + } else if(typeof value == 'string') { + options[key] = decodeURIComponent(value) + } else { + options[key] = value + } + // 执行设置项操作 + run(key, options[key]) + }); // 初始化不存在的需要进行初始化的值 Object.keys(optDefault).forEach((key) => { if (options[key] === undefined) { @@ -351,16 +362,22 @@ export function get(name: string): any { * @returns 设置项值(如果没有则为 null) */ export function getRaw(name: string) { - // 解析拆分 cookie 并执行各个设置项的初始化方法 - const str = localStorage.getItem('options') - if (str != null) { - const list = str.split('&') - for (let i = 0; i <= list.length; i++) { - if (list[i] !== undefined) { - const opt: string[] = list[i].split(':') - if (opt.length === 2) { - if(name == opt[0]) { - return opt[1] + const electron = (process.env.IS_ELECTRON as any) === true ? window.require('electron') : null + const reader = electron ? electron.ipcRenderer : null + if (reader) { + return reader.sendSync('opt:get', name) + } else { + // 解析拆分 cookie 并执行各个设置项的初始化方法 + const str = localStorage.getItem('options') + if (str != null) { + const list = str.split('&') + for (let i = 0; i <= list.length; i++) { + if (list[i] !== undefined) { + const opt: string[] = list[i].split(':') + if (opt.length === 2) { + if (name == opt[0]) { + return opt[1] + } } } } @@ -379,7 +396,7 @@ export function save(name: string, value: any) { } export function saveAll(config = {} as {[key: string]: any}) { if(Object.keys(config).length == 0) { - config = cacheConfigs + Object.assign(config, cacheConfigs) } let str = '' Object.keys(config).forEach(key => { @@ -389,6 +406,18 @@ export function saveAll(config = {} as {[key: string]: any}) { }) str = str.substring(0, str.length - 1) localStorage.setItem('options', str) + + // electron:将配置保存 + const electron = (process.env.IS_ELECTRON as any) === true ? window.require('electron') : null + const reader = electron ? electron.ipcRenderer : null + if(reader) { + const saveConfig = config + Object.keys(config).forEach(key => { + const isObject = typeof config[key] == 'object' + saveConfig[key] = isObject ? JSON.stringify(config[key]) : config[key] + }) + reader.send('opt:saveAll', saveConfig) + } } /** @@ -458,6 +487,7 @@ export default { get, load, save, + run, runAS, runASWEvent, remove diff --git a/src/pages/Options.vue b/src/pages/Options.vue index 9c542d9c..281c28da 100644 --- a/src/pages/Options.vue +++ b/src/pages/Options.vue @@ -8,7 +8,7 @@