mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-12-20 19:20:37 +08:00
ConfigurationPage组件化
This commit is contained in:
35
src/components/config/BackgroundLayer.vue
Normal file
35
src/components/config/BackgroundLayer.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div
|
||||
class="background-layer"
|
||||
:style="{
|
||||
background: $q.dark.isActive
|
||||
? $root.profile.backgroundImgDark
|
||||
? `url('${$root.profile.backgroundImgDark}')`
|
||||
: 'none'
|
||||
: $root.profile.backgroundImgLight
|
||||
? `url('${$root.profile.backgroundImgLight}')`
|
||||
: 'none',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundAttachment: 'fixed',
|
||||
}"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BackgroundLayer'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.background-layer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
</style>
|
||||
130
src/components/config/CommandPanels.vue
Normal file
130
src/components/config/CommandPanels.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<q-tab-panels
|
||||
animated
|
||||
class="absolute-right"
|
||||
:style="{
|
||||
bottom: footerBarHeight,
|
||||
left: tabBarWidth,
|
||||
}"
|
||||
v-model="currentTag"
|
||||
transition-prev="jump-down"
|
||||
transition-next="jump-up"
|
||||
>
|
||||
<q-tab-panel
|
||||
style="padding: 0"
|
||||
v-for="tag in allQuickCommandTags"
|
||||
:key="tag"
|
||||
:name="tag"
|
||||
>
|
||||
<q-scroll-area
|
||||
style="height: 100%"
|
||||
:thumb-style="{
|
||||
background: 'grey',
|
||||
width: '6px',
|
||||
opacity: 0.5,
|
||||
}"
|
||||
>
|
||||
<div class="row center q-pa-xs">
|
||||
<CommandCard
|
||||
v-for="commandInfo in currentTagQuickCommands"
|
||||
:key="commandInfo.features.code"
|
||||
:commandInfo="commandInfo"
|
||||
:isCommandActivated="
|
||||
activatedQuickCommandFeatureCodes.includes(
|
||||
commandInfo.features.code
|
||||
)
|
||||
"
|
||||
:cardStyle="cardStyleSheet[$root.profile.commandCardStyle]"
|
||||
@commandChanged="$emit('command-changed', $event)"
|
||||
:style="{
|
||||
width: cardStyleSheet[$root.profile.commandCardStyle].width,
|
||||
}"
|
||||
class="relative-position q-pa-sm"
|
||||
></CommandCard>
|
||||
</div>
|
||||
</q-scroll-area>
|
||||
</q-tab-panel>
|
||||
</q-tab-panels>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CommandCard from "components/CommandCard.vue";
|
||||
|
||||
export default {
|
||||
name: "CommandPanels",
|
||||
components: {
|
||||
CommandCard,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cardStyleSheet: {
|
||||
mini: {
|
||||
width: "20%",
|
||||
code: 1,
|
||||
},
|
||||
dense: {
|
||||
width: "33%",
|
||||
code: 2,
|
||||
},
|
||||
normal: {
|
||||
width: "50%",
|
||||
code: 3,
|
||||
},
|
||||
large: {
|
||||
width: "100%",
|
||||
code: 4,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
props: {
|
||||
footerBarHeight: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
tabBarWidth: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
allQuickCommandTags: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
currentTagQuickCommands: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
activatedQuickCommandFeatureCodes: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
currentTag: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit("update:modelValue", value);
|
||||
},
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue", "command-changed"],
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.q-tab-panels {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* 面板过渡效果 */
|
||||
.q-tab-panel {
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
</style>
|
||||
229
src/components/config/FooterBar.vue
Normal file
229
src/components/config/FooterBar.vue
Normal file
@@ -0,0 +1,229 @@
|
||||
<template>
|
||||
<div
|
||||
class="absolute-bottom footer-bar"
|
||||
:style="{ height: height, left: left }"
|
||||
>
|
||||
<div class="row">
|
||||
<!-- 搜索栏 -->
|
||||
<div class="col">
|
||||
<q-input
|
||||
:model-value="searchKeyword"
|
||||
@update:model-value="updateSearch"
|
||||
debounce="200"
|
||||
filled
|
||||
dense
|
||||
:autofocus="$root.profile.autofocusSearch"
|
||||
placeholder="搜索,支持拼音首字母"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<!-- 按钮组 -->
|
||||
<div class="col-auto justify-end flex">
|
||||
<q-btn-group>
|
||||
<!-- 暗色模式切换按钮 -->
|
||||
<q-btn
|
||||
flat
|
||||
v-if="isDev"
|
||||
:color="isDarkMode ? 'amber' : 'blue-9'"
|
||||
:icon="isDarkMode ? 'dark_mode' : 'light_mode'"
|
||||
@click="toggleDarkMode"
|
||||
>
|
||||
<q-tooltip>
|
||||
{{ isDarkMode ? "切换到亮色模式" : "切换到暗色模式" }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
<!-- 切换视图 -->
|
||||
<q-btn-toggle
|
||||
v-model="$root.profile.commandCardStyle"
|
||||
toggle-color="primary"
|
||||
flat
|
||||
:options="[
|
||||
{ slot: 'normal', value: 'normal' },
|
||||
{ slot: 'dense', value: 'dense' },
|
||||
{ slot: 'mini', value: 'mini' },
|
||||
]"
|
||||
>
|
||||
<template v-slot:normal>
|
||||
<q-icon name="dashboard" />
|
||||
<q-tooltip>双列视图</q-tooltip>
|
||||
</template>
|
||||
<template v-slot:dense>
|
||||
<q-icon name="apps" />
|
||||
<q-tooltip>三列视图</q-tooltip>
|
||||
</template>
|
||||
<template v-slot:mini>
|
||||
<q-icon name="view_comfy" />
|
||||
<q-tooltip>
|
||||
五列面板视图<br />
|
||||
未启用、匹配类型为窗口的命令在此视图下不显示
|
||||
</q-tooltip>
|
||||
</template>
|
||||
</q-btn-toggle>
|
||||
<!-- 分享中心按钮 -->
|
||||
<q-btn
|
||||
split
|
||||
flat
|
||||
@click="goShareCenter"
|
||||
color="primary"
|
||||
label="分享中心"
|
||||
icon="groups"
|
||||
class="share-btn"
|
||||
/>
|
||||
<!-- 新建按钮 -->
|
||||
<q-btn
|
||||
split
|
||||
flat
|
||||
@click="$emit('add-new')"
|
||||
color="primary"
|
||||
label="新建"
|
||||
icon="add"
|
||||
class="new-btn"
|
||||
/>
|
||||
<!-- 菜单按钮 -->
|
||||
<q-btn
|
||||
stretch
|
||||
color="primary"
|
||||
flat
|
||||
size="xs"
|
||||
id="menuBtn"
|
||||
:style="{ height: height }"
|
||||
>
|
||||
<q-spinner-bars color="primary" size="1.5em" />
|
||||
<ConfigurationMenu
|
||||
:isTagStared="isTagStared"
|
||||
:currentTag="currentTag"
|
||||
/>
|
||||
</q-btn>
|
||||
</q-btn-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ConfigurationMenu from "components/menu";
|
||||
|
||||
export default {
|
||||
name: "FooterBar",
|
||||
components: {
|
||||
ConfigurationMenu,
|
||||
},
|
||||
props: {
|
||||
height: {
|
||||
type: String,
|
||||
default: "40px",
|
||||
},
|
||||
left: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isTagStared: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
currentTag: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
searchKeyword: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
emits: ["update:search", "add-new"],
|
||||
data() {
|
||||
return {
|
||||
isDarkMode: this.$q.dark.isActive,
|
||||
isDev: utools.isDev(),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
updateSearch(value) {
|
||||
this.$emit("update:search", value);
|
||||
},
|
||||
toggleDarkMode() {
|
||||
this.$q.dark.toggle();
|
||||
this.isDarkMode = this.$q.dark.isActive;
|
||||
},
|
||||
goShareCenter() {
|
||||
utools.shellOpenExternal("https://qc.qaz.ink/");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.footer-bar {
|
||||
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
will-change: left;
|
||||
}
|
||||
|
||||
/* 底栏输入框样式 */
|
||||
.q-field__control {
|
||||
background: rgba(255, 255, 255, 0.15) !important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
:deep(.body--dark) .q-field__control {
|
||||
background: rgba(0, 0, 0, 0.2) !important;
|
||||
}
|
||||
|
||||
/* 底栏毛玻璃效果 */
|
||||
:deep(body.glass-effect-menu) .footer-bar {
|
||||
background: rgba(
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
calc(0.15 + var(--glass-effect-strength) * 0.01)
|
||||
) !important;
|
||||
backdrop-filter: blur(calc(var(--glass-effect-strength) * 1px)) !important;
|
||||
-webkit-backdrop-filter: blur(
|
||||
calc(var(--glass-effect-strength) * 1px)
|
||||
) !important;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
:deep(body.body--dark.glass-effect-menu) .footer-bar {
|
||||
background: rgba(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
calc(0.2 + var(--glass-effect-strength) * 0.02)
|
||||
) !important;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
/* 按钮动画 */
|
||||
.share-btn,
|
||||
.new-btn {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.share-btn:hover,
|
||||
.new-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
background: rgba(var(--q-primary-rgb), 0.1) !important;
|
||||
}
|
||||
|
||||
/* 搜索框动画 */
|
||||
@keyframes searchIconShake {
|
||||
0%,
|
||||
100% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
25% {
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
75% {
|
||||
transform: rotate(10deg);
|
||||
}
|
||||
}
|
||||
|
||||
.q-field--focused .q-field__prepend .q-icon {
|
||||
animation: searchIconShake 0.8s ease;
|
||||
color: var(--q-primary);
|
||||
}
|
||||
</style>
|
||||
158
src/components/config/TagBar.vue
Normal file
158
src/components/config/TagBar.vue
Normal file
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<q-scroll-area
|
||||
v-show="$root.profile.commandCardStyle !== 'mini'"
|
||||
class="absolute-left tag-bar"
|
||||
:thumb-style="{
|
||||
width: '2px',
|
||||
}"
|
||||
:style="{
|
||||
width: tabBarWidth,
|
||||
zIndex: 1,
|
||||
}"
|
||||
>
|
||||
<q-tabs
|
||||
v-model="currentTag"
|
||||
vertical
|
||||
switch-indicator
|
||||
:key="denseTagBar"
|
||||
:dense="denseTagBar"
|
||||
>
|
||||
<!-- 所有标签 -->
|
||||
<q-tab
|
||||
v-for="tag in allQuickCommandTags"
|
||||
:key="tag"
|
||||
:name="tag"
|
||||
:data-search-result="tag === '搜索结果'"
|
||||
:data-active-panel="activatedQuickPanels.includes(tag)"
|
||||
>
|
||||
{{ tag }}
|
||||
<q-tooltip v-if="tag === '未分类'">
|
||||
所有没有添加标签的命令都会归在未分类 <br />
|
||||
可以在新建命令时在标签一栏选择或直接键入标签名来添加标签
|
||||
</q-tooltip>
|
||||
</q-tab>
|
||||
</q-tabs>
|
||||
</q-scroll-area>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TagBar",
|
||||
props: {
|
||||
tabBarWidth: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
allQuickCommandTags: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
activatedQuickPanels: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
denseTagBar: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
currentTag: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit("update:modelValue", value);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 标签栏容器样式 */
|
||||
.q-tabs {
|
||||
height: 100vh !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
/* 标签栏和底栏内的按钮悬浮效果 */
|
||||
.q-tabs .q-tab:hover {
|
||||
background: rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
.body--dark .q-tabs .q-tab:hover {
|
||||
background: rgba(255, 255, 255, 0.05) !important;
|
||||
}
|
||||
|
||||
.q-tab {
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
position: relative;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
color: var(--q-blue-grey) !important;
|
||||
opacity: 0.7;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 激活标签样式 */
|
||||
.q-tab--active {
|
||||
color: var(--q-primary) !important;
|
||||
font-weight: 600 !important;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.q-tab:hover::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--q-primary);
|
||||
opacity: 0.1;
|
||||
border-radius: 4px;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
/* 特殊标签样式(搜索结果和激活面板) */
|
||||
.q-tab[data-search-result="true"],
|
||||
.q-tab[data-active-panel="true"] {
|
||||
color: #4286de !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
/* 标签悬浮效果 */
|
||||
.q-tab:hover {
|
||||
opacity: 0.9;
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
/* 标签内容过渡效果 */
|
||||
.q-tab__content {
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
/* 标签栏过渡动画 */
|
||||
.tag-bar {
|
||||
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
will-change: width, opacity;
|
||||
opacity: 1;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* 标签栏隐藏时的样式 */
|
||||
.tag-bar[style*="display: none"] {
|
||||
opacity: 0;
|
||||
width: 0 !important;
|
||||
}
|
||||
|
||||
/* 暗色模式适配 */
|
||||
:deep(.body--dark) .q-tab:hover::after {
|
||||
opacity: 0.15;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user