35 changed files with 559 additions and 180 deletions
@ -0,0 +1,7 @@ |
|||||
|
import type { IOnFunc } from "setting/main" |
||||
|
|
||||
|
export type EventMaps = { |
||||
|
init: IOnFunc |
||||
|
update: IOnFunc |
||||
|
change: (key: string, value: any) => void |
||||
|
} |
@ -1,7 +1,4 @@ |
|||||
import { buildEmitter } from "base/event/main" |
import { buildEmitter } from "base/event/main" |
||||
import type { IOnFunc } from "setting/main" |
import { EventMaps } from "setting/common" |
||||
|
|
||||
export const emitter = buildEmitter<{ |
export const emitter = buildEmitter<EventMaps>() |
||||
init: IOnFunc |
|
||||
update: IOnFunc |
|
||||
}>() |
|
||||
|
@ -0,0 +1,8 @@ |
|||||
|
import type { EventMaps } from "setting/common" |
||||
|
|
||||
|
export interface SettingCommand { |
||||
|
save: () => void |
||||
|
reset: () => void |
||||
|
} |
||||
|
|
||||
|
export { EventMaps } |
@ -0,0 +1,13 @@ |
|||||
|
.slide-fade-enter-active { |
||||
|
transition: all 0.2s ease-out; |
||||
|
} |
||||
|
|
||||
|
.slide-fade-leave-active { |
||||
|
transition: all 0.2s cubic-bezier(1, 0.5, 0.8, 1); |
||||
|
} |
||||
|
|
||||
|
.slide-fade-enter-from, |
||||
|
.slide-fade-leave-to { |
||||
|
// transform: translateX(20px); |
||||
|
opacity: 0; |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
import { monaco } from "./monaco" |
||||
|
|
||||
|
const defaultOptions: monaco.editor.IStandaloneEditorConstructionOptions = { |
||||
|
fontSize: 14, |
||||
|
readOnly: false, |
||||
|
theme: "vs-light", |
||||
|
fontFamily: "Cascadia Mono, Consolas, 'Courier New', monospace", |
||||
|
lineHeight: 22, |
||||
|
scrollBeyondLastLine: false, |
||||
|
automaticLayout: true, |
||||
|
minimap: { |
||||
|
enabled: false, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
function getOptions(opt = {}) { |
||||
|
return { |
||||
|
...defaultOptions, |
||||
|
...opt, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function useMonacoEditor(editor: HTMLDivElement) { |
||||
|
let editor: monaco.editor.IStandaloneCodeEditor | null = null |
||||
|
let placeholderWidget: PlaceholderContentWidget | null = null |
||||
|
|
||||
|
|
||||
|
return { |
||||
|
scrollTop() { |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,136 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import Simplebar from "simplebar-vue" |
||||
|
|
||||
|
const settingStore = useApiSetting() |
||||
|
|
||||
|
const router = useRouter() |
||||
|
|
||||
|
const active = ref(0) |
||||
|
|
||||
|
const allApp = reactive([ |
||||
|
{ label: "基础设置", path: "/setting/" }, |
||||
|
{ label: "更新设置", path: "/setting/update" }, |
||||
|
{ label: "开发设置", path: "/setting/dev" }, |
||||
|
]) |
||||
|
|
||||
|
watchEffect(() => { |
||||
|
const currentPath = router.currentRoute.value.path |
||||
|
const index = allApp.findIndex(app => app.path === currentPath) |
||||
|
if (index !== -1) { |
||||
|
active.value = index |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
function onClick(app: any, index: number) { |
||||
|
active.value = index |
||||
|
router.replace(app.path) |
||||
|
} |
||||
|
|
||||
|
function onClickSave() { |
||||
|
settingStore |
||||
|
.save() |
||||
|
.then(() => { |
||||
|
toast("设置已保存", { type: "success" }) |
||||
|
}) |
||||
|
.catch(() => { |
||||
|
toast("保存失败,请重试", { type: "error" }) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
function onClickReset() { |
||||
|
settingStore.reset() |
||||
|
toast("设置已重置", { type: "info" }) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div h-full flex> |
||||
|
<div w="100px" h-full relative max-w="500px" min-w="100px"> |
||||
|
<Simplebar h-full> |
||||
|
<div |
||||
|
v-for="(app, index) in allApp" |
||||
|
:key="index" |
||||
|
p="8px 10px" |
||||
|
text="12px" |
||||
|
border |
||||
|
border-b |
||||
|
h="30px" |
||||
|
cursor="pointer" |
||||
|
hover:bg-gray-50 |
||||
|
class="item" |
||||
|
transition-all |
||||
|
:class="{ active: active === index }" |
||||
|
@click="onClick(app, index)" |
||||
|
> |
||||
|
<div class="text" transition-all position="absolute" left="10px">{{ app.label }}</div> |
||||
|
</div> |
||||
|
</Simplebar> |
||||
|
<AdjustLine></AdjustLine> |
||||
|
</div> |
||||
|
<div class="content" relative b-l="1px solid #E5E5E5" flex-1 w-0 overflow-auto flex flex-col> |
||||
|
<RouterView v-slot="{ Component, route }"> |
||||
|
<Transition name="slide-fade" mode="out-in"> |
||||
|
<component :is="Component" :key="route.fullPath" /> |
||||
|
</Transition> |
||||
|
</RouterView> |
||||
|
</div> |
||||
|
<div v-if="!settingStore.isSame" absolute bottom-20px right-20px flex flex-col gap-20px> |
||||
|
<div |
||||
|
:disabled="settingStore.isSaving" |
||||
|
shadow |
||||
|
flex-center |
||||
|
cursor-pointer |
||||
|
rounded="50%" |
||||
|
p="10px" |
||||
|
bg="blue-500" |
||||
|
color="white" |
||||
|
@click="onClickSave" |
||||
|
> |
||||
|
<icon-material-symbols:save></icon-material-symbols:save> |
||||
|
</div> |
||||
|
<div :disabled="settingStore.isSaving" shadow flex-center cursor-pointer rounded="50%" p="10px" bg="white" @click="onClickReset"> |
||||
|
<icon-ix:reset></icon-ix:reset> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.item { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
white-space: nowrap; |
||||
|
&::before { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
top: 0; |
||||
|
height: 100%; |
||||
|
width: 6px; |
||||
|
background-color: #f3f4f6; |
||||
|
transition: all linear 300ms; |
||||
|
} |
||||
|
&:hover { |
||||
|
&::before { |
||||
|
width: 30px; |
||||
|
} |
||||
|
.text { |
||||
|
left: 20px; |
||||
|
} |
||||
|
} |
||||
|
&.active { |
||||
|
@apply: text-black; |
||||
|
&::before { |
||||
|
width: 100%; |
||||
|
} |
||||
|
.text { |
||||
|
left: 50%; |
||||
|
transform: translateX(-50%); |
||||
|
} |
||||
|
} |
||||
|
.text { |
||||
|
transition-duration: 300ms; |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,42 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
const settingStore = useApiSetting() |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div h-full> |
||||
|
<div class="form"> |
||||
|
<div class="form-item" :class="{ ['not-save']: settingStore.diffKeys.includes('dev:debug') }"> |
||||
|
<div class="form-item__label">存储地址</div> |
||||
|
<div class="form-item__value"> |
||||
|
<div class="select"> |
||||
|
<select v-model="settingStore.config['dev:debug']"> |
||||
|
<option :value="0">开启调试模式</option> |
||||
|
<option :value="2">关闭调试模式</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.form { |
||||
|
padding: 20px; |
||||
|
.form-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
+ .form-item { |
||||
|
margin-top: 15px; |
||||
|
} |
||||
|
.form-item__label { |
||||
|
width: 100px; |
||||
|
font-weight: bold; |
||||
|
flex-basis: 100px; |
||||
|
} |
||||
|
.form-item__value { |
||||
|
width: 600px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,72 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div h-full flex>dasd</div> |
||||
|
</template> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.form { |
||||
|
padding: 20px; |
||||
|
.form-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
+ .form-item { |
||||
|
margin-top: 15px; |
||||
|
} |
||||
|
.form-item__label { |
||||
|
width: 100px; |
||||
|
font-weight: bold; |
||||
|
flex-basis: 100px; |
||||
|
} |
||||
|
.form-item__value { |
||||
|
width: 300px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.submit { |
||||
|
margin: 20px; |
||||
|
padding: 10px 20px; |
||||
|
background-color: #007bff; |
||||
|
color: white; |
||||
|
border: none; |
||||
|
border-radius: 5px; |
||||
|
cursor: pointer; |
||||
|
&:disabled { |
||||
|
background-color: #ccc; |
||||
|
cursor: not-allowed; |
||||
|
} |
||||
|
} |
||||
|
// .input-wrapper { |
||||
|
// .input { |
||||
|
// width: 100%; |
||||
|
// padding: 8px; |
||||
|
// border: 1px solid #ccc; |
||||
|
// border-radius: 4px; |
||||
|
// } |
||||
|
// } |
||||
|
.radio-group { |
||||
|
display: inline-flex; |
||||
|
border: 1px solid #ccc; |
||||
|
border-radius: 5px; |
||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
||||
|
.radio { |
||||
|
flex: 1; |
||||
|
cursor: pointer; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
padding: 10px; |
||||
|
&:hover { |
||||
|
background-color: #f0f0f0; |
||||
|
} |
||||
|
&.active { |
||||
|
background-color: #e0e0e0; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
+ .radio { |
||||
|
border-left: 1px solid #ccc; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
Loading…
Reference in new issue