Browse Source

一些功能

develop
谢亚昕 4 months ago
parent
commit
af91b0052a
  1. 6
      src/main/command/PlatForm/command.ts
  2. 66
      src/main/command/Tabs/command.ts
  3. 1
      src/main/controller/BasicService.ts
  4. 1
      src/main/controller/TabsService.ts
  5. 12
      src/main/controller/_ioc.ts
  6. 6
      src/main/modules/_ioc.ts
  7. 2
      src/renderer/components.d.ts
  8. 3
      src/renderer/src/commands/PlatForm.ts
  9. 0
      src/renderer/src/commands/Setting.ts
  10. 0
      src/renderer/src/commands/Updater.ts
  11. 10
      src/renderer/src/components/Card/Card.vue
  12. 4
      src/renderer/src/components/CodeEditor/PlaceholderContentWidget.ts
  13. 105
      src/renderer/src/components/CodeEditor/code-editor-plus.vue
  14. 210
      src/renderer/src/components/CodeEditor/hook.ts
  15. 5
      src/renderer/src/components/CodeEditor/monaco.ts
  16. 2
      src/renderer/src/composables/Api/Platform/useApiPlatForm.ts
  17. 2
      src/renderer/src/composables/Api/Setting/useApiSetting.ts
  18. 4
      src/renderer/src/pages/_ui/Browser.vue
  19. 14
      src/renderer/src/pages/index/index.vue
  20. 37
      src/renderer/src/pages/setting/index.vue

6
src/main/command/PlatForm/command.ts

@ -1,4 +1,4 @@
import { app, dialog, nativeTheme, TitleBarOverlayOptions } from "electron" import { app, dialog, nativeTheme, shell, TitleBarOverlayOptions } from "electron"
import { inject } from "inversify" import { inject } from "inversify"
import errorHandler from "logger/main-error" import errorHandler from "logger/main-error"
import WindowManager from "main/modules/window-manager" import WindowManager from "main/modules/window-manager"
@ -88,6 +88,10 @@ export default class PlatFormCommand {
app.exit() app.exit()
} }
openDir(url: string, opts: Parameters<typeof shell.openExternal>[1]) {
return shell.openExternal(url, opts)
}
reload() { reload() {
const focusedWindow = this._WindowManager.getFocusWindow() const focusedWindow = this._WindowManager.getFocusWindow()
// 重载之后, 刷新并关闭所有的次要窗体 // 重载之后, 刷新并关闭所有的次要窗体

66
src/main/command/Tabs/command.ts

@ -0,0 +1,66 @@
import { inject } from "inversify"
import Tabs from "main/modules/tabs"
import WindowManager from "main/modules/window-manager"
import { broadcast } from "utils/main"
class TabsCommand {
constructor(
@inject(Tabs) private _Tabs: Tabs,
@inject(WindowManager) private _WindowManager: WindowManager,
) {
this.listenerTabActive = this.listenerTabActive.bind(this)
this._Tabs.events.on("update", this.listenerTabActive)
}
bindElement(rect) {
this._Tabs.updateRect(rect)
}
reload() {
this._WindowManager.getMainWindow()?.reload()
}
sync() {
this.listenerTabActive()
if (!this.getAllTabs().length) {
this.add("about:blank")
}
}
listenerTabActive() {
broadcast("TabsCommand.update", this.getAllTabs())
}
add(url) {
this._Tabs.add(url, true, this._WindowManager.getMainWindow()!)
}
nagivate(index: number, url: string) {
this._Tabs.navigate(+index, url)
}
setActive(index) {
this._Tabs.changeActive(index)
}
closeTab(e) {
this._Tabs.remove(e.body.active)
}
closeAll() {
this._Tabs.closeAll()
}
getAllTabs() {
return this._Tabs._tabs.map(v => ({
url: v.url,
showUrl: v.showUrl,
title: v.title,
favicons: v.favicons,
isActive: v.isActive,
}))
}
}
export { TabsCommand }
export default TabsCommand

1
src/main/controller/BasicService.ts

@ -5,6 +5,7 @@ import WindowManager from "main/modules/window-manager"
@injectable() @injectable()
class BasicService extends BaseContainer { class BasicService extends BaseContainer {
static name = "Basic"
constructor( constructor(
@inject(WindowManager) private _WindowManager: WindowManager, @inject(WindowManager) private _WindowManager: WindowManager,
// @inject(Tabs) private _Tabs: Tabs, // @inject(Tabs) private _Tabs: Tabs,

1
src/main/controller/TabsService.ts

@ -5,6 +5,7 @@ import WindowManager from "main/modules/window-manager"
@injectable() @injectable()
class TabsService extends BaseContainer { class TabsService extends BaseContainer {
static name = "Tabs"
constructor( constructor(
@inject(Tabs) private _Tabs: Tabs, @inject(Tabs) private _Tabs: Tabs,
@inject(WindowManager) private _WindowManager: WindowManager, @inject(WindowManager) private _WindowManager: WindowManager,

12
src/main/controller/_ioc.ts

@ -1,4 +1,7 @@
import { Container, ContainerModule } from "inversify" import { Container, ContainerModule } from "inversify"
import _logger from "logger/main"
const logger = _logger.createNamespace("service")
/** /**
* *
@ -10,9 +13,12 @@ const modules = new ContainerModule(bind => {
Object.values(serviceModules).forEach(module => { Object.values(serviceModules).forEach(module => {
// 由于 module 类型为 unknown,需要进行类型断言 // 由于 module 类型为 unknown,需要进行类型断言
const ServiceClass = (module as { default: any }).default const ServiceClass = (module as { default: any }).default
if (ServiceClass && ServiceClass.name.endsWith("Service")) { if (ServiceClass) {
const serviceName = ServiceClass.name const className = ServiceClass.name.replace("Service", "")
bind(serviceName).to(ServiceClass).inSingletonScope() logger.debug(`绑定服务类: ${className}Service`)
bind(className + "Service")
.to(ServiceClass)
.inSingletonScope()
} }
}) })
}) })

6
src/main/modules/_ioc.ts

@ -1,7 +1,7 @@
import { Container, ContainerModule } from "inversify" import { Container, ContainerModule } from "inversify"
import { Api } from "./api" import { Api } from "./api"
import { WindowManager } from "./window-manager" import { WindowManager } from "./window-manager"
// import { Tabs } from "./tabs" import { Tabs } from "./tabs"
import Commands from "./commands" import Commands from "./commands"
import Zephyr from "./zephyr" import Zephyr from "./zephyr"
@ -10,7 +10,7 @@ const modules = new ContainerModule(bind => {
bind(Api).toSelf().inSingletonScope() bind(Api).toSelf().inSingletonScope()
bind(WindowManager).toSelf().inSingletonScope() bind(WindowManager).toSelf().inSingletonScope()
bind(Commands).toSelf().inSingletonScope() bind(Commands).toSelf().inSingletonScope()
// bind(Tabs).toSelf().inSingletonScope() bind(Tabs).toSelf().inSingletonScope()
}) })
async function destroyAllModules(ioc: Container) { async function destroyAllModules(ioc: Container) {
@ -18,7 +18,7 @@ async function destroyAllModules(ioc: Container) {
ioc.get(WindowManager).destroy(), ioc.get(WindowManager).destroy(),
ioc.get(Commands).destroy(), ioc.get(Commands).destroy(),
ioc.get(Zephyr).destroy(), ioc.get(Zephyr).destroy(),
// ioc.get(Tabs).destroy(), ioc.get(Tabs).destroy(),
ioc.get(Api).destroy(), ioc.get(Api).destroy(),
]) ])
ioc.unloadAsync(modules) ioc.unloadAsync(modules)

2
src/renderer/components.d.ts

@ -9,7 +9,9 @@ export {}
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
AdjustLine: typeof import('./src/components/AdjustLine.vue')['default'] AdjustLine: typeof import('./src/components/AdjustLine.vue')['default']
Card: typeof import('./src/components/Card/Card.vue')['default']
CodeEditor: typeof import('./src/components/CodeEditor/code-editor.vue')['default'] CodeEditor: typeof import('./src/components/CodeEditor/code-editor.vue')['default']
CodeEditorPlus: typeof import('./src/components/CodeEditor/code-editor-plus.vue')['default']
'IconBxs:error': typeof import('~icons/bxs/error')['default'] 'IconBxs:error': typeof import('~icons/bxs/error')['default']
'IconGrommetIcons:update': typeof import('~icons/grommet-icons/update')['default'] 'IconGrommetIcons:update': typeof import('~icons/grommet-icons/update')['default']
'IconIx:reset': typeof import('~icons/ix/reset')['default'] 'IconIx:reset': typeof import('~icons/ix/reset')['default']

3
src/renderer/src/composables/Api/Platform/_/index.ts → src/renderer/src/commands/PlatForm.ts

@ -53,6 +53,9 @@ class PlatForm extends BaseSingleton {
async toggleDevTools() { async toggleDevTools() {
return this.api.call("PlatFormCommand.toggleDevTools") return this.api.call("PlatFormCommand.toggleDevTools")
} }
async openDir(dir: string) {
return this.api.call("PlatFormCommand.openDir", dir)
}
} }
export { PlatForm } export { PlatForm }

0
src/renderer/src/composables/Api/Setting/_/index.ts → src/renderer/src/commands/Setting.ts

0
src/renderer/src/composables/Api/Updater/_/index.ts → src/renderer/src/commands/Updater.ts

10
src/renderer/src/components/Card/Card.vue

@ -0,0 +1,10 @@
<template>
<div class="w-[calc((100%-4*20px)/5)] <lg:w-[calc((100%-2*20px)/3)]" shadow>
<div p-2 text-lg font-bold>MarkdownUtils</div>
<div p-2 pt-0 text-sm>这是一个导航站</div>
<div flex gap="10px" px-4 py-2 tex border-t="1px solid #E5E5E5">
<div cursor="pointer" text-sm leading-1 py-2 px-3 border="1px solid #E5E5E5" rounded @click="$router.push('browser')">查看</div>
<div cursor="pointer" text-sm leading-1 py-2 px-3 border="1px solid #E5E5E5" rounded>访问</div>
</div>
</div>
</template>

4
src/renderer/src/components/CodeEditor/PlaceholderContentWidget.ts

@ -38,6 +38,10 @@ export class PlaceholderContentWidget implements monaco.editor.IContentWidget {
this.domNode.style.pointerEvents = "none" this.domNode.style.pointerEvents = "none"
this.domNode.textContent = this.placeholder this.domNode.textContent = this.placeholder
this.domNode.style.fontStyle = "italic" this.domNode.style.fontStyle = "italic"
const a = document.createElement("a")
a.href = "https://baiud.com"
a.textContent = "百度"
this.domNode.appendChild(a)
this.editor.applyFontInfo(this.domNode) this.editor.applyFontInfo(this.domNode)
} }

105
src/renderer/src/components/CodeEditor/code-editor-plus.vue

@ -0,0 +1,105 @@
<script lang="ts" setup>
import { useMonacoEditor, IOptions } from "./hook"
const props = withDefaults(
defineProps<{
readonly?: boolean
modelValue?: string
filename?: string
placeholder?: string
modelOptions?: IOptions["modelOptions"]
editorOptions?: IOptions["editorOptions"]
}>(),
{
readonly: false,
modelValue: "",
filename: "",
},
)
const emit = defineEmits<{
(e: "update:modelValue", code: string): void
(e: "change", code: string): void
(e: "cursor:position", position: [number, number]): void
}>()
const editorRef = ref<HTMLDivElement>()
const { updateOption, setValue } = useMonacoEditor(editorRef, {
placeholder: "请输入一些文本测试",
content: props.modelValue,
filename: props.filename,
modelOptions: props.modelOptions,
editorOptions: props.editorOptions,
onCursorChange(e) {
emit("cursor:position", [e.position.lineNumber, e.position.column])
},
onDidChangeContent(code) {
emit("update:modelValue", code)
emit("change", code)
},
})
watch(
() => props.modelValue,
() => {
setValue(props.modelValue)
},
)
watch(
() => props.filename,
() => {
updateOption({
filename: props.filename,
})
},
)
watch(
() => props.editorOptions,
() => {
updateOption({
editorOptions: props.editorOptions,
})
},
{ deep: true },
)
watch(
() => props.modelOptions,
() => {
updateOption({
modelOptions: props.modelOptions,
})
},
{ deep: true },
)
</script>
<template>
<div class="monaco-wrapper">
<div ref="editorRef" class="monaco-editor"></div>
</div>
</template>
<style lang="scss" scoped>
.monaco-wrapper {
height: 100%;
position: relative;
.monaco-editor {
height: 100%;
}
.monaco-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
opacity: 0.1;
overflow: hidden;
.monaco-logo {
@apply absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2;
}
}
}
</style>

210
src/renderer/src/components/CodeEditor/hook.ts

@ -1,33 +1,209 @@
import { monaco } from "./monaco" import { monaco } from "./monaco"
import { PlaceholderContentWidget } from "./PlaceholderContentWidget"
import { judgeFile } from "./utils"
import type { Ref } from "vue"
const defaultOptions: monaco.editor.IStandaloneEditorConstructionOptions = { function useResizeObserver(el: HTMLDivElement, callback: ResizeObserverCallback) {
fontSize: 14, const isSupported = window && "ResizeObserver" in window
readOnly: false, let observer: ResizeObserver | undefined
theme: "vs-light", const cleanup = () => {
fontFamily: "Cascadia Mono, Consolas, 'Courier New', monospace", if (observer) {
lineHeight: 22, observer.disconnect()
scrollBeyondLastLine: false, observer = undefined
automaticLayout: true, }
minimap: { }
enabled: false, const stopWatch = watch(
() => el,
el => {
cleanup()
if (isSupported && window && el) {
observer = new ResizeObserver(callback)
observer!.observe(el, {})
}
},
{ immediate: true },
)
const stop = () => {
cleanup()
stopWatch()
}
function tryOnScopeDispose(fn: () => void) {
if (getCurrentScope()) {
onScopeDispose(fn)
return true
}
return false
}
tryOnScopeDispose(() => {
stop()
})
}
export interface IOptions {
placeholder: string
filename: string
content: string
editorOptions: monaco.editor.IEditorOptions & monaco.editor.IGlobalEditorOptions
modelOptions: monaco.editor.ITextModelUpdateOptions
onCursorChange?: (e: monaco.editor.ICursorPositionChangedEvent) => void
onDidChangeContent?: (str: string) => void
}
const defaultOptions: IOptions = {
placeholder: "请输入文本",
filename: "temp",
content: "",
editorOptions: {
fontSize: 14,
readOnly: false,
theme: "vs-light",
fontFamily: "Cascadia Mono, Consolas, 'Courier New', monospace",
scrollBeyondLastLine: false,
lineHeight: 22,
automaticLayout: true,
minimap: {
enabled: false,
},
}, },
modelOptions: {},
} }
function getOptions(opt = {}) { const assign = (curOpt, opt, parenyKey: string[] = [], config = { arrayExtend: "concat" }) => {
return { for (const key in opt) {
...defaultOptions, if (opt[key] !== undefined) {
...opt, if (typeof opt[key] === "function" && curOpt[key] !== undefined && typeof curOpt[key] !== "function") {
opt[key] = opt[key](curOpt[key])
}
if (typeof opt[key] === "object" && !Array.isArray(opt[key]) && !Array.isArray(curOpt[key])) {
parenyKey.push(key)
assign(curOpt[key], opt[key], parenyKey, config)
} else if (typeof opt[key] === "object" && Array.isArray(opt[key]) && Array.isArray(curOpt[key])) {
if (config.arrayExtend === "concat") {
curOpt[key] = curOpt[key].concat(opt[key])
} else {
curOpt[key] = opt[key]
}
} else if (curOpt[key] !== undefined && typeof curOpt[key] !== typeof opt[key]) {
throw new Error(`Type of ${parenyKey.join(",") + "." + key} is not match`)
} else {
curOpt[key] = opt[key]
}
}
} }
return curOpt
} }
export function useMonacoEditor(editor: HTMLDivElement) { function getOptions(opt = {}, config = { arrayExtend: "concat" }): IOptions {
const curOptions = structuredClone(defaultOptions)
assign(curOptions, opt, [], config)
return curOptions
}
export function useMonacoEditor(editorElement: Ref<HTMLDivElement | undefined>, opts: Partial<IOptions>) {
let curOption = getOptions(opts) as IOptions
let editor: monaco.editor.IStandaloneCodeEditor | null = null let editor: monaco.editor.IStandaloneCodeEditor | null = null
let placeholderWidget: PlaceholderContentWidget | null = null let placeholderWidget: PlaceholderContentWidget | null = null
const updateOption = (opts: Partial<IOptions>) => {
if (!editor) return
curOption = assign(curOption, opts)
if (Object.hasOwn(opts, "placeholder")) {
if (placeholderWidget) {
placeholderWidget.dispose()
placeholderWidget = null
}
if (opts.placeholder) {
placeholderWidget = new PlaceholderContentWidget(opts.placeholder, editor)
}
}
if (Object.hasOwn(opts, "modelOptions") && opts.modelOptions) {
const model = editor.getModel()
model?.updateOptions(opts.modelOptions)
}
if (Object.hasOwn(opts, "editorOptions") && opts.editorOptions) {
editor.updateOptions(opts.editorOptions)
}
if (Object.hasOwn(opts, "filename")) {
updateModel(curOption.filename, curOption.content)
}
if (Object.hasOwn(opts, "content")) {
console.log("无法通过updateOption修改content")
}
}
let isInnerChange = "waitting" // waitting, out, in
const setValue = (content: string) => {
if (isInnerChange === "waitting") {
isInnerChange = "out"
}
if (editor && isInnerChange === "out") {
editor.setValue(content)
} else {
isInnerChange = "waitting"
}
}
function updateModel(name: string, content: string) {
if (editor) {
const oldModel = editor.getModel() //获取旧模型
const file = judgeFile(name)
// 这样定义的话model无法清除
// monaco.editor.createModel("const a = 111","typescript", monaco.Uri.parse('file://root/file3.ts'))
const model: monaco.editor.ITextModel = monaco.editor.createModel(content ?? "", file?.language ?? "txt")
model.updateOptions(curOption.modelOptions)
model.onDidChangeContent(() => {
if (model) {
if (isInnerChange === "out") {
isInnerChange = "waitting"
return
}
isInnerChange = "in"
const code = model.getValue()
curOption.onDidChangeContent?.(code)
}
})
if (oldModel) {
oldModel.dispose()
}
editor.setModel(model)
}
}
tryOnMounted(() => {
if (editorElement.value && !editor) {
editor = monaco.editor.create(editorElement.value, curOption.editorOptions) as monaco.editor.IStandaloneCodeEditor
editor.onDidChangeCursorPosition(e => {
curOption.onCursorChange?.(e)
})
if (!curOption.content) {
placeholderWidget = new PlaceholderContentWidget(curOption.placeholder, editor)
} else {
if (isInnerChange === "waitting") {
isInnerChange = "out"
}
}
updateModel(curOption.filename, curOption.content)
useResizeObserver(editorElement.value, () => {
editor!.layout()
})
}
})
tryOnUnmounted(() => {
if (editor) {
const oldModel = editor.getModel()
if (oldModel) {
oldModel.dispose()
}
editor.dispose()
editor = null
}
})
return { return {
setValue,
scrollTop() { scrollTop() {
editor?.setScrollTop(0)
} },
updateOption,
} }
} }

5
src/renderer/src/components/CodeEditor/monaco.ts

@ -1,7 +1,7 @@
// import 'monaco-editor/esm/vs/editor/editor.all.js'; // import 'monaco-editor/esm/vs/editor/editor.all.js';
// import 'monaco-editor/esm/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.js'; // import 'monaco-editor/esm/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.js';
import * as monaco from "monaco-editor/esm/vs/editor/editor.api"
// import 'monaco-editor/esm/vs/basic-languages/monaco.contribution.js'; // import 'monaco-editor/esm/vs/basic-languages/monaco.contribution.js';
// import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution.js'; // import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution.js';
@ -13,4 +13,7 @@ import * as monaco from "monaco-editor/esm/vs/editor/editor.api"
// 导入全部特性 // 导入全部特性
// import * as monaco from "monaco-editor" // import * as monaco from "monaco-editor"
import * as monaco from "monaco-editor/esm/vs/editor/editor.api"
import "monaco-editor/esm/vs/basic-languages/monaco.contribution.js"
export { monaco } export { monaco }

2
src/renderer/src/composables/Api/Platform/useApiPlatForm.ts

@ -1,5 +1,5 @@
import { LogLevel } from "logger/common" import { LogLevel } from "logger/common"
import { PlatForm } from "./_" import { PlatForm } from "@/commands/PlatForm"
export function useApiPlatForm() { export function useApiPlatForm() {
const plat = PlatForm.getInstance<PlatForm>() const plat = PlatForm.getInstance<PlatForm>()

2
src/renderer/src/composables/Api/Setting/useApiSetting.ts

@ -1,5 +1,5 @@
import { defineStore } from "pinia" import { defineStore } from "pinia"
import { Setting } from "./_" import { Setting } from "@/commands/Setting"
import type { IConfig } from "config" import type { IConfig } from "config"
import type { EventMaps, SettingCommand } from "command/Setting/type" import type { EventMaps, SettingCommand } from "command/Setting/type"

4
src/renderer/src/pages/_ui/Browser.vue

@ -51,9 +51,9 @@
} }
} }
if (import.meta.hot) { if (import.meta.hot) {
api.off("main:TabsCommand.update", listener) api.off("TabsCommand.update", listener)
} }
api.on("main:TabsCommand.update", listener) api.on("TabsCommand.update", listener)
onMounted(() => { onMounted(() => {
api.call("TabsCommand.sync") api.call("TabsCommand.sync")
}) })

14
src/renderer/src/pages/index/index.vue

@ -10,19 +10,13 @@
<template> <template>
<div gap="20px" h-full> <div gap="20px" h-full>
<input type="text" v-model="code" placeholder="请输入文本" class="input" /> <input type="text" v-model="code" placeholder="请输入文本" class="input" />
<input type="checkbox" v-model="aa"> {{ aa }} <input type="checkbox" v-model="aa" />
{{ aa }}
<div h-300px> <div h-300px>
<code-editor :options="{ readOnly: aa }" v-model="code" placeholder="请输入文本"></code-editor> <code-editor-plus :editorOptions="{ readOnly: aa }" filename="test.js" v-model="code" placeholder="请输入文本"></code-editor-plus>
</div> </div>
<div p="20px" flex flex-wrap items-start gap="20px" justify-start rounded> <div p="20px" flex flex-wrap items-start gap="20px" justify-start rounded>
<div v-for="i in 20" :key="i" class="w-[calc((100%-4*20px)/5)] <lg:w-[calc((100%-2*20px)/3)]" shadow> <Card v-for="i in 20" :key="i"></Card>
<div p-2 text-lg font-bold>MarkdownUtils</div>
<div p-2 pt-0 text-sm>这是一个导航站</div>
<div flex gap="10px" px-4 py-2 tex border-t="1px solid #E5E5E5">
<div cursor="pointer" text-sm leading-1 py-2 px-3 border="1px solid #E5E5E5" rounded>查看</div>
<div cursor="pointer" text-sm leading-1 py-2 px-3 border="1px solid #E5E5E5" rounded>访问</div>
</div>
</div>
<button class="button">Button</button> <button class="button">Button</button>
<!-- <button @click="UpdaterStore.checkForUpdates()">更新</button> --> <!-- <button @click="UpdaterStore.checkForUpdates()">更新</button> -->
<button @click="$router.push('/browser')">浏览器</button> <button @click="$router.push('/browser')">浏览器</button>

37
src/renderer/src/pages/setting/index.vue

@ -1,64 +1,65 @@
<script setup lang="ts"> <script setup lang="ts">
const settingStore = useApiSetting() const SettingStore = useApiSetting()
const ApiPlatForm = useApiPlatForm()
</script> </script>
<template> <template>
<div h-full> <div h-full>
<div class="form"> <div class="form">
<div class="form-item" :class="{ ['not-save']: settingStore.diffKeys.includes('storagePath') }"> <div class="form-item" :class="{ ['not-save']: SettingStore.diffKeys.includes('storagePath') }">
<div class="form-item__label">存储地址</div> <div class="form-item__label">存储地址</div>
<div class="form-item__value" flex gap="10px" items-center> <div class="form-item__value" flex gap="10px" items-center>
<div class="input-wrapper"> <div class="input-wrapper">
<input v-model="settingStore.config['storagePath']" class="input" readonly type="text" placeholder="请输入存储地址" /> <input v-model="SettingStore.config['storagePath']" class="input" readonly type="text" placeholder="请输入存储地址" />
</div> </div>
<button class="button">打开</button> <button class="button" @click="ApiPlatForm.power.openDir(SettingStore.config['storagePath'])">打开</button>
</div> </div>
</div> </div>
<div class="form-item" :class="{ ['not-save']: settingStore.diffKeys.includes('common.theme') }"> <div class="form-item" :class="{ ['not-save']: SettingStore.diffKeys.includes('common.theme') }">
<div class="form-item__label">主题</div> <div class="form-item__label">主题</div>
<div class="form-item__value"> <div class="form-item__value">
<div class="radio-group"> <div class="radio-group">
<div <div
class="radio" class="radio"
:class="{ active: settingStore.config['common.theme'] === 'auto' }" :class="{ active: SettingStore.config['common.theme'] === 'auto' }"
@click="settingStore.config['common.theme'] = 'auto'" @click="SettingStore.config['common.theme'] = 'auto'"
> >
Auto Auto
</div> </div>
<div <div
class="radio" class="radio"
:class="{ active: settingStore.config['common.theme'] === 'light' }" :class="{ active: SettingStore.config['common.theme'] === 'light' }"
@click="settingStore.config['common.theme'] = 'light'" @click="SettingStore.config['common.theme'] = 'light'"
> >
亮色 亮色
</div> </div>
<div <div
class="radio" class="radio"
:class="{ active: settingStore.config['common.theme'] === 'dark' }" :class="{ active: SettingStore.config['common.theme'] === 'dark' }"
@click="settingStore.config['common.theme'] = 'dark'" @click="SettingStore.config['common.theme'] = 'dark'"
> >
暗色 暗色
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="form-item" :class="{ ['not-save']: settingStore.diffKeys.includes('language') }"> <div class="form-item" :class="{ ['not-save']: SettingStore.diffKeys.includes('language') }">
<div class="form-item__label">语言</div> <div class="form-item__label">语言</div>
<div class="form-item__value"> <div class="form-item__value">
<div class="radio-group"> <div class="radio-group">
<div <div
class="radio" class="radio"
:class="{ active: settingStore.config['language'] === 'zh' }" :class="{ active: SettingStore.config['language'] === 'zh' }"
@click="settingStore.config['language'] = 'zh'" @click="SettingStore.config['language'] = 'zh'"
> >
汉语 中文
</div> </div>
<div <div
class="radio" class="radio"
:class="{ active: settingStore.config['language'] === 'en' }" :class="{ active: SettingStore.config['language'] === 'en' }"
@click="settingStore.config['language'] = 'en'" @click="SettingStore.config['language'] = 'en'"
> >
中文 English
</div> </div>
</div> </div>
</div> </div>

Loading…
Cancel
Save