配置菜单组件化

This commit is contained in:
fofolee 2024-12-20 23:01:26 +08:00
parent a65d9ccdff
commit ffdb737750
11 changed files with 755 additions and 805 deletions

View File

@ -16,15 +16,15 @@
v-model:isVisible="isWarpperHover" v-model:isVisible="isWarpperHover"
v-show="cardStyleVars.showButtons" v-show="cardStyleVars.showButtons"
:isActivated="isCommandActivated" :isActivated="isCommandActivated"
:isRunButtonVisible="canRunInConfigurationPage"
:commandInfo="commandInfo" :commandInfo="commandInfo"
:canRunAtCurrentOS="canRunAtCurrentOS"
@commandChanged="$emit('commandChanged', $event)" @commandChanged="$emit('commandChanged', $event)"
/> />
<CommandCardContent <CommandCardContent
:commandInfo="commandInfo" :commandInfo="commandInfo"
:isActivated="isCommandActivated" :isActivated="isCommandActivated"
:isPlatformSupported="isPlatformSupported"
:cardStyleVars="cardStyleVars" :cardStyleVars="cardStyleVars"
:canRunAtCurrentOS="canRunAtCurrentOS"
:isHovered="isWarpperHover" :isHovered="isWarpperHover"
@click="handleCardClick" @click="handleCardClick"
/> />
@ -57,17 +57,25 @@ export default {
this.cardStyle.code > 1 ? "justify-start" : "justify-center", this.cardStyle.code > 1 ? "justify-start" : "justify-center",
fontPosition: fontPosition:
this.cardStyle.code > 1 ? "justify-end" : "justify-center", this.cardStyle.code > 1 ? "justify-end" : "justify-center",
hideCard: hideCard: this.cardStyle.code === 1 && !this.canRunInConfigurationPage,
this.cardStyle.code === 1 &&
(!this.isCommandActivated || !this.$refs.controlButtons?.canRun),
}; };
}, },
canRunAtCurrentOS() { isPlatformSupported() {
let { platform } = this.commandInfo.features; let { platform } = this.commandInfo.features;
return !_.isEmpty(platform) && !platform.includes(window.processPlatform) return !_.isEmpty(platform) && !platform.includes(window.processPlatform)
? false ? false
: true; : true;
}, },
canRunInConfigurationPage() {
//
if (!this.isCommandActivated) return false;
//
if (!this.isPlatformSupported) return false;
let { cmds } = this.commandInfo.features;
//
if (cmds[0].type && cmds[0].type === "window") return false;
return true;
},
}, },
props: { props: {
commandInfo: Object, commandInfo: Object,

View File

@ -1,765 +0,0 @@
<template>
<div>
<q-menu
target="#menuBtn"
max-height="480px"
max-width="300px"
transition-show="jump-up"
transition-hide="jump-down"
>
<!-- 用户信息 -->
<UserInfo
ref="user"
:allQuickCommandsLength="allQuickCommandsLength"
:allFeaturesLength="allFeaturesLength"
/>
<!-- 菜单 -->
<q-list>
<!-- 命令管理 -->
<q-item clickable>
<q-item-section side>
<q-icon name="keyboard_arrow_left" />
</q-item-section>
<q-item-section>命令管理</q-item-section>
<q-menu anchor="top end" self="top start">
<q-list>
<!-- 导入 -->
<q-item
clickable
v-close-popup
@click="importCommand(importCommandFromFile())"
>
<q-item-section side>
<q-icon name="text_snippet" />
</q-item-section>
<q-item-section>从文件导入命令</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="importCommand(importCommandFromClipboard())"
>
<q-item-section side>
<q-icon name="content_paste" />
</q-item-section>
<q-item-section>从剪贴板导入命令</q-item-section>
</q-item>
<!-- 导出 -->
<q-item clickable v-close-popup @click="exportAllCommands">
<q-item-section side>
<q-icon name="file_upload" />
</q-item-section>
<q-item-section>导出所有命令</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="enableAllCommands">
<q-item-section side>
<q-icon name="checklist_rtl" />
</q-item-section>
<q-item-section>启用本页所有命令</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="disableAllCommands">
<q-item-section side>
<q-icon name="remove_done" />
</q-item-section>
<q-item-section>禁用本页所有命令</q-item-section>
</q-item>
<!-- 清空 -->
<q-item
style="color: red"
clickable
v-close-popup
@click="clearAllCommands"
>
<q-item-section side>
<q-icon name="delete" />
</q-item-section>
<q-item-section>清空数据</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-item>
<!-- 实用功能 -->
<q-item clickable>
<q-item-section side>
<q-icon name="keyboard_arrow_left" />
</q-item-section>
<q-item-section>实用功能</q-item-section>
<q-menu anchor="top end" self="top start">
<q-list>
<q-item>
<q-item-section side>
<q-icon name="folder_special" />
</q-item-section>
<q-input
dense
prefix="快速收藏文件至"
suffix="标签"
outlined
input-class="text-center"
style="width: 280px"
autofocus
v-model="$root.profile.quickFileTag"
@blur="
$root.profile.quickFileTag ||
($root.profile.quickFileTag = '文件')
"
type="text"
>
<template v-slot:append>
<q-toggle
@update:model-value="
(val, e) => toggleFeature('favFile', val)
"
v-model="$root.profile.quickFileEnable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip
>启用后选中文件可以通过超级面板快速将文件收藏到{{
$root.profile.quickFileTag
}}标签
</q-tooltip>
</q-input>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="bookmarks" />
</q-item-section>
<q-input
dense
prefix="快速收藏网址至"
suffix="标签"
outlined
input-class="text-center"
style="width: 280px"
v-model="$root.profile.quickUrlTag"
@blur="
$root.profile.quickUrlTag ||
($root.profile.quickUrlTag = '网址')
"
type="text"
>
<template v-slot:append>
<q-toggle
@update:model-value="
(val, e) => toggleFeature('favUrl', val)
"
v-model="$root.profile.quickUrlEnable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip
>启用后在浏览器界面可以通过超级面板快速将网址收藏到{{
$root.profile.quickUrlTag
}}标签
</q-tooltip>
</q-input>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="drive_file_rename_outline" />
</q-item-section>
<q-input
dense
prefix="新建插件别名至"
suffix="标签"
outlined
input-class="text-center"
style="width: 280px"
autofocus
v-model="$root.profile.pluNickNameTag"
type="text"
>
<template v-slot:append>
<q-toggle
@update:model-value="
(val, e) => toggleFeature('pluNickName', val)
"
v-model="$root.profile.pluNickNameEnable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip
>启用后在主输入框输入插件别名可以快速设置插件别名<br />
并将所有设置的别名保存至{{
$root.profile.pluNickNameTag
}}标签
</q-tooltip>
</q-input>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="api" />
</q-item-section>
<q-field dense outlined style="width: 280px">
<template v-slot:control>
<div class="self-center full-width no-outline" tabindex="0">
快捷命令服务
</div>
</template>
<template v-slot:append>
<q-btn
flat
@click="$router.push('server')"
icon="open_in_new"
/>
</template>
<q-tooltip
>通过本地监听
{{ $root.nativeProfile.serverPort }}
端口的形式接收用户传送过来的参数然后根据参数执行不同的操作
<br />
需要配置插件跟随 utools 启动和保留后台<br />
也可在主输入框通过关键字快捷命令服务配置进入
</q-tooltip>
</q-field>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="code" />
</q-item-section>
<q-field dense outlined style="width: 280px">
<template v-slot:control>
<div class="self-center full-width no-outline" tabindex="0">
运行代码
</div>
</template>
<template v-slot:append>
<q-btn
flat
@click="$router.push('code')"
icon="open_in_new"
/>
</template>
<q-tooltip
>一个可以直接运行代码的代码编辑器<br />
也可在主输入框输入关键字RunCode进入
</q-tooltip>
</q-field>
</q-item>
</q-list>
</q-menu>
</q-item>
<!-- 环境配置 -->
<q-item clickable>
<q-item-section side>
<q-icon name="keyboard_arrow_left" />
</q-item-section>
<q-item-section>环境配置</q-item-section>
<q-menu anchor="bottom end" self="bottom start">
<q-list>
<q-item clickable v-close-popup @click="showUserDara = true">
<q-item-section side>
<q-icon name="manage_accounts" />
</q-item-section>
<q-item-section>用户特殊变量</q-item-section>
<q-tooltip
>用户设置的变量类似一个全局配置项<br />
配置好后可选择特殊变量中的usr:插入<br />
也可直接在特殊变量中配置</q-tooltip
>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="dvr" />
</q-item-section>
<q-input
dense
outlined
autogrow
style="width: 280px"
autofocus
v-model="$root.nativeProfile.envPath"
type="text"
label="环境变量 PATH"
>
<q-tooltip
>修改本插件环境变量中的 PATH直接覆盖而非追加
<br />将会影响到除 quickcommandhtml
以外的所有环境</q-tooltip
>
</q-input>
</q-item>
<q-item v-if="showAlias">
<q-item-section side>
<q-icon name="code" />
</q-item-section>
<q-input
dense
outlined
autogrow
style="width: 280px"
v-model="$root.nativeProfile.alias"
type="text"
label="Alias"
>
<q-tooltip
>一行一条配置方法和 shell 的语法一样<br /> alias
python="/home/user/.bin/python"<br />将会影响到除
quickcommandhtml 以外的所有环境</q-tooltip
>
</q-input>
</q-item>
</q-list>
</q-menu>
</q-item>
<!-- 个性化设置 -->
<q-item clickable>
<q-item-section side>
<q-icon name="keyboard_arrow_left" />
</q-item-section>
<q-item-section>个性化设置</q-item-section>
<q-menu anchor="top end" self="top start">
<q-list>
<q-item clickable :disable="!$refs.user.isVIP">
<q-item-section side>
<q-icon name="color_lens" />
</q-item-section>
<q-item-section>主颜色</q-item-section>
<q-tooltip>你可以更改界面的主题色会员限定 😎</q-tooltip>
<q-menu
v-if="$refs.user.isVIP"
nchor="top left"
self="bottom end"
style="min-width: 200px; min-height: 200px"
>
<q-color
@change="setPrimaryColor"
v-model="$root.profile.primaryColor"
/>
<q-btn
color="primary"
label="重置为默认"
class="full-width"
@click="resetPrimary"
/>
</q-menu>
</q-item>
<q-item clickable :disable="!$refs.user.isVIP">
<q-item-section side>
<q-icon name="image" />
</q-item-section>
<q-item-section>背景图片设置</q-item-section>
<q-tooltip>设置背景图片会员限定 😎</q-tooltip>
<q-menu
v-if="$refs.user.isVIP"
anchor="top left"
self="bottom end"
>
<q-card>
<q-card-section>
<div class="text-subtitle2">亮色模式背景</div>
<q-file
dense
standout="bg-primary text-white"
v-model="selectFileLight"
autofocus
@update:model-value="setBackgroundImg('light')"
accept="image/*"
label="请选择一张图片"
>
<template v-slot:prepend>
<q-icon name="attach_file" />
</template>
</q-file>
</q-card-section>
<q-card-section>
<div class="text-subtitle2">暗色模式背景</div>
<q-file
dense
standout="bg-primary text-white"
v-model="selectFileDark"
@update:model-value="setBackgroundImg('dark')"
accept="image/*"
label="请选择一张图片"
>
<template v-slot:prepend>
<q-icon name="attach_file" />
</template>
</q-file>
</q-card-section>
<q-btn
color="negative"
label="取消背景"
class="full-width"
@click="removeBackgroundImg()"
/>
</q-card>
</q-menu>
</q-item>
<!-- 毛玻璃效果滑块 -->
<q-item>
<q-item-section side>
<q-icon name="blur_on" />
</q-item-section>
<q-item-section class="flex">毛玻璃效果</q-item-section>
<q-tooltip
>启用毛玻璃界面并调节效果强度会员限定 😎</q-tooltip
>
<q-item-section side>
<div
class="flex items-center justify-center"
style="width: 56px"
>
<q-knob
v-model="$root.profile.glassEffect"
:min="0"
:max="12"
color="primary"
:thickness="0.6"
size="24px"
track-color="grey-3"
@change="toggleGlassEffect"
class="q-mx-auto"
/>
</div>
</q-item-section>
</q-item>
<q-item clickable :disable="!$refs.user.isVIP">
<q-item-section side>
<q-icon name="label" />
</q-item-section>
<q-item-section class="flex">紧凑标签栏 </q-item-section>
<q-tooltip>更为紧凑的标签栏适用于标签非常多的情形</q-tooltip>
<q-item-section side
><q-toggle
v-model="$root.profile.denseTagBar"
:disable="!$refs.user.isVIP"
color="primary"
@update:model-value="$root.saveProfile"
/></q-item-section>
</q-item>
<q-item clickable>
<q-item-section side>
<q-icon name="search" />
</q-item-section>
<q-item-section class="flex">自动聚焦搜索 </q-item-section>
<q-tooltip>进入插件时自动聚焦搜索</q-tooltip>
<q-item-section side
><q-toggle
v-model="$root.profile.autofocusSearch"
color="primary"
@update:model-value="$root.saveProfile"
/></q-item-section>
</q-item>
<!-- 自动分离 -->
<!-- <q-item clickable v-close-popup @click="getActivatedFutures(); showAutoDetachFeatures = true">
<q-item-section side>
<q-icon name="web_stories" />
</q-item-section>
<q-item-section class="flex">自动分离 </q-item-section>
<q-tooltip>utools的自动分离对整个插件生效配置此选项可以实现只对某些特定的功能进行自动分离</q-tooltip>
</q-item> -->
</q-list>
</q-menu>
</q-item>
<!-- 收藏 -->
<q-item v-if="isTagStared" clickable v-close-popup @click="unMarkTag">
<q-item-section side>
<q-icon name="star_border" />
</q-item-section>
<q-item-section>取消收藏</q-item-section>
</q-item>
<q-item v-else clickable v-close-popup @click="showPanelConf = true">
<q-item-section side>
<q-icon name="star" />
</q-item-section>
<q-item-section>收藏标签</q-item-section>
<q-tooltip
>收藏后会将当前标签名作为全局关键字可在 uTools
的主输入框进行搜索
<br />
搜索进入后默认进入当前标签的面板视图 <br />
类似于旧版本的快捷面板</q-tooltip
>
</q-item>
<!-- 关于 -->
<q-item clickable v-close-popup @click="showAbout = true">
<q-item-section side>
<q-icon name="info" />
</q-item-section>
<q-item-section>关于和帮助</q-item-section></q-item
>
</q-list>
</q-menu>
<!-- 关于弹窗 -->
<q-dialog v-model="showAbout">
<AboutThis />
</q-dialog>
<!-- 面板视图弹窗 -->
<q-dialog v-model="showPanelConf">
<PanelSetting :isTagStared="isTagStared" :currentTag="currentTag" />
</q-dialog>
<q-dialog v-model="showUserDara">
<UserData :showInsertBtn="false" />
</q-dialog>
<!-- <q-dialog v-model="showAutoDetachFeatures">
<q-card>
<q-card-section style="height: 400px; overflow: auto;">
<q-option-group v-model="$root.profile.autoDetachFeatures" type="checkbox" :options="activateFeatures">
<template v-slot:label="opt">
<q-item clickable v-ripple style="width: 300px;">
<q-item-section avatar>
<q-img :src="opt.icon" width="30px" />
</q-item-section>
<q-item-section>
{{ opt.label }}
</q-item-section>
</q-item>
</template>
</q-option-group>
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="取消" @click="showAutoDetachFeatures = false" />
<q-btn color="primary" label="确定" @click="changeAutoDetachFeatures" />
</q-card-actions>
</q-card>
</q-dialog> -->
</div>
</template>
<script>
import { setCssVar } from "quasar";
import AboutThis from "components/popup/AboutThis";
import PanelSetting from "components/popup/PanelSetting";
import UserInfo from "components/popup/UserInfo";
import features from "js/options/quickFeatures.js";
import UserData from "components/popup/UserData";
export default {
components: {
AboutThis,
PanelSetting,
UserInfo,
UserData,
},
data() {
return {
setCssVar: setCssVar,
selectFileLight: null,
selectFileDark: null,
showAbout: false,
showPanelConf: false,
showUserDara: false,
showAutoDetachFeatures: false,
features: features,
redirect: utools.redirect,
showAlias: this.$q.platform.is.mac || this.$q.platform.is.linux,
activateFeatures: [],
};
},
computed: {
configurationPage() {
return this.$root.$refs.view;
},
allQuickCommandsLength() {
return Object.keys(this.configurationPage.allQuickCommands).length;
},
allFeaturesLength() {
return this.configurationPage.activatedQuickCommandFeatureCodes.length;
},
},
props: {
isTagStared: Boolean,
currentTag: String,
},
methods: {
//
importCommand(command) {
this.configurationPage.importCommand(command);
},
//
importCommandFromFile() {
let options = {
type: "dialog",
argvs: { filters: [{ name: "json", extensions: ["json"] }] },
readfile: true,
};
let fileContent = window.getFileInfo(options);
return fileContent ? fileContent.data : false;
},
//
importCommandFromClipboard() {
return window.clipboardReadText();
},
//
exportAllCommands() {
this.configurationPage.exportAllCommands();
},
//
clearAllCommands() {
this.configurationPage.clearAllCommands();
},
//
enableAllCommands() {
// dom
document
.querySelectorAll(".q-toggle[aria-checked='false']")
.forEach((x) => x.click());
},
//
disableAllCommands() {
// dom
document
.querySelectorAll(".q-toggle[aria-checked='true']")
.forEach((x) => x.click());
},
//
setPrimaryColor() {
this.setCssVar("primary", this.$root.profile.primaryColor);
this.$root.saveProfile();
},
//
resetPrimary() {
this.$root.profile.primaryColor = this.$root.profile.defaultPrimaryColor;
this.setPrimaryColor();
},
//
async setBackgroundImg(mode) {
const file =
mode === "light" ? this.selectFileLight : this.selectFileDark;
if (!file) return;
// 使 Node.js
const processedImage = await window.imageProcessor(file.path);
//
if (mode === "light") {
this.$root.profile.backgroundImgLight = processedImage;
} else {
this.$root.profile.backgroundImgDark = processedImage;
}
this.$root.saveProfile();
},
removeBackgroundImg() {
this.$root.profile.backgroundImgLight = "";
this.$root.profile.backgroundImgDark = "";
this.$root.saveProfile();
},
//
unMarkTag() {
this.$root.utools.whole.removeFeature(
`panel_${window.hexEncode(this.currentTag)}`
);
_.pull(this.$root.$refs.view.activatedQuickPanels, this.currentTag);
quickcommand.showMessageBox("取消收藏成功");
},
//
toggleFeature(type, enable) {
enable
? this.$root.utools.whole.setFeature(_.cloneDeep(this.features[type]))
: this.$root.utools.whole.removeFeature(this.features[type].code);
},
//
getActivatedFutures() {
let activateFeatures = this.$root.utools.whole
.getFeatures()
.map((fts) => {
return {
value: fts.code,
icon: fts.icon,
label: fts.explain,
};
});
let defaultFeatures = [
{
value: "configuration",
label: "快捷命令配置",
icon: "logo.png",
},
{
value: "code",
label: "运行代码",
icon: "features/code.png",
},
{
value: "server",
label: "快捷命令服务",
icon: "features/server.png",
},
];
this.activateFeatures = _.concat(
defaultFeatures,
_.cloneDeep(activateFeatures)
);
},
changeAutoDetachFeatures() {
this.showAutoDetachFeatures = false;
quickcommand.showMessageBox("设置成功");
},
toggleGlassEffect(val) {
this.$root.profile.glassEffect = val;
this.$root.saveProfile();
},
},
};
</script>
<style>
/* 基础菜单样式 - 始终保持最小毛玻璃效果 */
.q-menu {
background: rgba(255, 255, 255, 0.15) !important;
backdrop-filter: blur(5px) !important;
-webkit-backdrop-filter: blur(5px) !important;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 16px 0 rgba(31, 38, 135, 0.07);
}
.body--dark .q-menu {
background: rgba(0, 0, 0, 0.2) !important;
border: 1px solid rgba(255, 255, 255, 0.05);
}
/* 毛玻璃菜单效果 - 叠加用户设置的效果 */
body.glass-effect-menu .q-menu {
background: rgba(
255,
255,
255,
calc(0.15 + var(--glass-effect-strength) * 0.01)
) !important;
backdrop-filter: blur(
calc(5px + var(--glass-effect-strength) * 1px)
) !important;
-webkit-backdrop-filter: blur(
calc(5px + var(--glass-effect-strength) * 1px)
) !important;
}
/* 暗色模式菜单 */
body.body--dark.glass-effect-menu .q-menu {
background: rgba(
0,
0,
0,
calc(0.2 + var(--glass-effect-strength) * 0.02)
) !important;
}
/* 菜单列表透明背景 */
.q-menu .q-list {
background: transparent !important;
}
/* 菜单项浮效果 */
.q-menu .q-item:hover {
background: rgba(255, 255, 255, 0.1) !important;
}
.body--dark .q-menu .q-item:hover {
background: rgba(255, 255, 255, 0.05) !important;
}
/* 输入框样式 */
.q-menu .q-field__control {
background: rgba(255, 255, 255, 0.15) !important;
border-radius: 4px;
}
.body--dark .q-menu .q-field__control {
background: rgba(0, 0, 0, 0.3) !important;
}
</style>

View File

@ -41,7 +41,7 @@
<span v-for="cmd in commandInfo.features.cmds" :key="cmd"> <span v-for="cmd in commandInfo.features.cmds" :key="cmd">
<CommandTypeTag <CommandTypeTag
:cmd="cmd" :cmd="cmd"
:isGrayColor="!canRunAtCurrentOS || !isActivated" :isGrayColor="!isPlatformSupported || !isActivated"
/> />
</span> </span>
</div> </div>
@ -81,7 +81,7 @@ export default {
commandInfo: Object, commandInfo: Object,
isActivated: Boolean, isActivated: Boolean,
cardStyleVars: Object, cardStyleVars: Object,
canRunAtCurrentOS: Boolean, isPlatformSupported: Boolean,
isHovered: Boolean, isHovered: Boolean,
}, },
emits: ["click"], emits: ["click"],

View File

@ -22,7 +22,7 @@
dense dense
color="green" color="green"
icon="play_arrow" icon="play_arrow"
v-show="canRun" v-show="isRunButtonVisible"
@click="runCommand" @click="runCommand"
> >
<q-tooltip anchor="top middle" self="center middle">运行</q-tooltip> <q-tooltip anchor="top middle" self="center middle">运行</q-tooltip>
@ -120,7 +120,7 @@ export default {
isVisible: Boolean, isVisible: Boolean,
isActivated: Boolean, isActivated: Boolean,
commandInfo: Object, commandInfo: Object,
canRunAtCurrentOS: Boolean, isRunButtonVisible: Boolean,
}, },
emits: ["update:isVisible", "commandChanged"], emits: ["update:isVisible", "commandChanged"],
data() { data() {
@ -134,16 +134,6 @@ export default {
cronExp() { cronExp() {
return this.$root.nativeProfile.crontabs[this.featureCode]; return this.$root.nativeProfile.crontabs[this.featureCode];
}, },
//
canRun() {
//
if (!this.isActivated) return false;
if (!this.canRunAtCurrentOS) return false;
let { cmds } = this.commandInfo.features;
//
if (cmds[0].type && cmds[0].type === "window") return false;
return true;
},
// //
canAddCron() { canAddCron() {
return !!this.commandInfo.features.cmds[0].length; return !!this.commandInfo.features.cmds[0].length;

View File

@ -0,0 +1,87 @@
<template>
<q-menu anchor="top end" self="top start">
<q-list>
<!-- 导入 -->
<q-item clickable v-close-popup @click="importCommand(importCommandFromFile())">
<q-item-section side>
<q-icon name="text_snippet" />
</q-item-section>
<q-item-section>从文件导入命令</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="importCommand(importCommandFromClipboard())">
<q-item-section side>
<q-icon name="content_paste" />
</q-item-section>
<q-item-section>从剪贴板导入命令</q-item-section>
</q-item>
<!-- 导出 -->
<q-item clickable v-close-popup @click="exportAllCommands">
<q-item-section side>
<q-icon name="file_upload" />
</q-item-section>
<q-item-section>导出所有命令</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="enableAllCommands">
<q-item-section side>
<q-icon name="checklist_rtl" />
</q-item-section>
<q-item-section>启用本页所有命令</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="disableAllCommands">
<q-item-section side>
<q-icon name="remove_done" />
</q-item-section>
<q-item-section>禁用本页所有命令</q-item-section>
</q-item>
<!-- 清空 -->
<q-item style="color: red" clickable v-close-popup @click="clearAllCommands">
<q-item-section side>
<q-icon name="delete" />
</q-item-section>
<q-item-section>清空数据</q-item-section>
</q-item>
</q-list>
</q-menu>
</template>
<script>
export default {
name: 'CommandManageMenu',
computed: {
configurationPage() {
return this.$root.$refs.view;
}
},
methods: {
importCommand(command) {
this.configurationPage.importCommand(command);
},
importCommandFromFile() {
let options = {
type: "dialog",
argvs: { filters: [{ name: "json", extensions: ["json"] }] },
readfile: true,
};
let fileContent = window.getFileInfo(options);
return fileContent ? fileContent.data : false;
},
importCommandFromClipboard() {
return window.clipboardReadText();
},
exportAllCommands() {
this.configurationPage.exportAllCommands();
},
clearAllCommands() {
this.configurationPage.clearAllCommands();
},
enableAllCommands() {
document.querySelectorAll('.q-toggle[aria-checked="false"]')
.forEach(x => x.click());
},
disableAllCommands() {
document.querySelectorAll('.q-toggle[aria-checked="true"]')
.forEach(x => x.click());
}
}
};
</script>

View File

@ -0,0 +1,77 @@
<template>
<q-menu anchor="bottom end" self="bottom start">
<q-list>
<q-item clickable v-close-popup @click="$emit('open-user-data')">
<q-item-section side>
<q-icon name="manage_accounts" />
</q-item-section>
<q-item-section>用户特殊变量</q-item-section>
<q-tooltip>
用户设置的变量类似一个全局配置项<br />
配置好后可选择特殊变量中的usr:插入<br />
也可直接在特殊变量中配置
</q-tooltip>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="dvr" />
</q-item-section>
<q-input
dense
outlined
autogrow
style="width: 280px"
autofocus
v-model="$root.nativeProfile.envPath"
@blur="saveProfile"
type="text"
label="环境变量 PATH"
>
<q-tooltip>
修改本插件环境变量中的 PATH直接覆盖而非追加
<br />将会影响到除 quickcommandhtml 以外的所有环境
</q-tooltip>
</q-input>
</q-item>
<q-item v-if="showAlias">
<q-item-section side>
<q-icon name="code" />
</q-item-section>
<q-input
dense
outlined
autogrow
style="width: 280px"
v-model="$root.nativeProfile.alias"
@blur="saveProfile"
type="text"
label="Alias"
>
<q-tooltip>
一行一条配置方法和 shell 的语法一样<br /> alias
python="/home/user/.bin/python"<br />
将会影响到除 quickcommandhtml 以外的所有环境
</q-tooltip>
</q-input>
</q-item>
</q-list>
</q-menu>
</template>
<script>
export default {
name: "EnvConfigMenu",
data() {
return {
showAlias: this.$q.platform.is.mac || this.$q.platform.is.linux,
};
},
methods: {
saveProfile() {
this.$root.saveProfile();
},
},
};
</script>

View File

@ -0,0 +1,208 @@
<template>
<q-menu anchor="top end" self="top start">
<q-list>
<q-item clickable :disable="!user.isVIP">
<q-item-section side>
<q-icon name="color_lens" />
</q-item-section>
<q-item-section>主颜色</q-item-section>
<q-tooltip>你可以更改界面的主题色会员限定 😎</q-tooltip>
<q-menu
v-if="user.isVIP"
anchor="center left"
self="center right"
style="min-width: 250px"
>
<div class="column">
<q-color
@change="setPrimaryColor"
v-model="$root.profile.primaryColor"
default-view="palette"
class="full-width"
/>
<q-btn
color="primary"
label="重置为默认"
class="q-mt-sm"
@click="resetPrimary"
/>
</div>
</q-menu>
</q-item>
<q-item clickable :disable="!user.isVIP" class="bg-img-menu">
<q-item-section side>
<q-icon name="image" />
</q-item-section>
<q-item-section>背景图片设置</q-item-section>
<q-tooltip>设置背景图片会员限定 😎</q-tooltip>
<q-menu v-if="user.isVIP" anchor="center left" self="center right">
<q-list style="min-width: 280px">
<q-item>
<q-item-section>
<q-file
v-model="selectFileLight"
label="亮色模式背景"
dense
outlined
class="full-width"
accept=".jpg,.png,jpeg"
@update:model-value="() => setBackgroundImg('light')"
>
<template v-slot:prepend>
<q-icon name="light_mode" />
</template>
</q-file>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-file
v-model="selectFileDark"
label="暗色模式背景"
dense
outlined
class="full-width"
accept=".jpg,.png,.webp"
@update:model-value="() => setBackgroundImg('dark')"
>
<template v-slot:prepend>
<q-icon name="dark_mode" />
</template>
</q-file>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-btn
color="negative"
label="删除背景图片"
class="full-width"
@click="removeBackgroundImg"
/>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-item>
<q-item clickable :disable="!user.isVIP">
<q-item-section side>
<q-icon name="blur_on" />
</q-item-section>
<q-item-section>毛玻璃效果</q-item-section>
<q-tooltip>启用毛玻璃界面并调节效果强度会员限定 😎</q-tooltip>
<q-item-section side>
<div class="flex items-center justify-center" style="width: 56px">
<q-knob
v-model="$root.profile.glassEffect"
:min="0"
:max="12"
:step="1"
color="primary"
@update:model-value="toggleGlassEffect"
size="34px"
:thickness="0.22"
class="glass-effect-knob"
show-value
font-size="13px"
>
</q-knob>
</div>
</q-item-section>
</q-item>
<q-item clickable :disable="!user.isVIP">
<q-item-section side>
<q-icon name="label" />
</q-item-section>
<q-item-section>紧凑标签栏</q-item-section>
<q-tooltip
>更为紧凑的标签栏适用于标签非常多的情形会员限定 😎</q-tooltip
>
<q-item-section side>
<q-toggle
v-model="$root.profile.denseTagBar"
:disable="!user.isVIP"
color="primary"
@update:model-value="$root.saveProfile"
/>
</q-item-section>
</q-item>
<q-item clickable>
<q-item-section side>
<q-icon name="search" />
</q-item-section>
<q-item-section class="flex">自动聚焦搜索</q-item-section>
<q-tooltip>进入插件时自动聚焦搜索</q-tooltip>
<q-item-section side>
<q-toggle
v-model="$root.profile.autofocusSearch"
color="primary"
@update:model-value="$root.saveProfile"
/>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</template>
<script>
import { setCssVar } from "quasar";
export default {
name: "PersonalizeMenu",
props: {
user: Object,
},
data() {
return {
selectFileLight: null,
selectFileDark: null,
};
},
methods: {
setPrimaryColor() {
setCssVar("primary", this.$root.profile.primaryColor);
this.$root.saveProfile();
},
resetPrimary() {
this.$root.profile.primaryColor = this.$root.profile.defaultPrimaryColor;
this.setPrimaryColor();
},
async setBackgroundImg(mode) {
const file =
mode === "light" ? this.selectFileLight : this.selectFileDark;
if (!file) return;
const processedImage = await window.imageProcessor(file.path);
if (mode === "light") {
this.$root.profile.backgroundImgLight = processedImage;
} else {
this.$root.profile.backgroundImgDark = processedImage;
}
this.$root.saveProfile();
},
removeBackgroundImg() {
this.$root.profile.backgroundImgLight = "";
this.$root.profile.backgroundImgDark = "";
this.$root.saveProfile();
},
toggleGlassEffect(val) {
this.$root.profile.glassEffect = val;
this.$root.saveProfile();
},
},
};
</script>
<style scoped>
.glass-effect-knob {
cursor: pointer;
transition: transform 0.3s;
border-radius: 50%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
.glass-effect-knob:hover {
transform: scale(1.1);
}
</style>

View File

@ -0,0 +1,136 @@
<template>
<q-menu anchor="top end" self="top start">
<q-list>
<q-item>
<q-item-section side>
<q-icon name="folder_special" />
</q-item-section>
<q-input
dense
prefix="快速收藏文件至"
suffix="标签"
outlined
input-class="text-center"
style="width: 280px"
autofocus
v-model="$root.profile.quickFileTag"
@blur="$root.profile.quickFileTag || ($root.profile.quickFileTag = '文件')"
type="text"
>
<template v-slot:append>
<q-toggle
@update:model-value="(val) => toggleFeature('favFile', val)"
v-model="$root.profile.quickFileEnable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip>启用后选中文件可以通过超级面板快速将文件收藏到{{ $root.profile.quickFileTag }}标签</q-tooltip>
</q-input>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="bookmarks" />
</q-item-section>
<q-input
dense
prefix="快速收藏网址至"
suffix="标签"
outlined
input-class="text-center"
style="width: 280px"
v-model="$root.profile.quickUrlTag"
@blur="$root.profile.quickUrlTag || ($root.profile.quickUrlTag = '网址')"
type="text"
>
<template v-slot:append>
<q-toggle
@update:model-value="(val) => toggleFeature('favUrl', val)"
v-model="$root.profile.quickUrlEnable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip>启用后在浏览器界面可以通过超级面板快速将网址收藏到{{ $root.profile.quickUrlTag }}标签</q-tooltip>
</q-input>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="drive_file_rename_outline" />
</q-item-section>
<q-input
dense
prefix="新建插件别名至"
suffix="标签"
outlined
input-class="text-center"
style="width: 280px"
autofocus
v-model="$root.profile.pluNickNameTag"
type="text"
>
<template v-slot:append>
<q-toggle
@update:model-value="(val) => toggleFeature('pluNickName', val)"
v-model="$root.profile.pluNickNameEnable"
checked-icon="check"
color="primary"
/>
</template>
<q-tooltip>
启用后在主输入框输入插件别名可以快速设置插件别名<br />
并将所有设置的别名保存至{{ $root.profile.pluNickNameTag }}标签
</q-tooltip>
</q-input>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="api" />
</q-item-section>
<q-field dense outlined style="width: 280px">
<template v-slot:control>
<div class="self-center full-width no-outline" tabindex="0">快捷命令服务</div>
</template>
<template v-slot:append>
<q-btn flat @click="$router.push('server')" icon="open_in_new" />
</template>
<q-tooltip>
通过本地监听 {{ $root.nativeProfile.serverPort }} 端口的形式接收用户传送过来的参数然后根据参数执行不同的操作
<br />需要配置插件跟随 utools 启动和保留后台<br />也可在主输入框通过关键字快捷命令服务配置进入
</q-tooltip>
</q-field>
</q-item>
<q-item>
<q-item-section side>
<q-icon name="code" />
</q-item-section>
<q-field dense outlined style="width: 280px">
<template v-slot:control>
<div class="self-center full-width no-outline" tabindex="0">运行代码</div>
</template>
<template v-slot:append>
<q-btn flat @click="$router.push('code')" icon="open_in_new" />
</template>
<q-tooltip>
一个可以直接运行代码的代码编辑器<br />也可在主输入框输入关键字RunCode进入
</q-tooltip>
</q-field>
</q-item>
</q-list>
</q-menu>
</template>
<script>
import features from "js/options/quickFeatures.js";
export default {
name: 'UtilityFeaturesMenu',
methods: {
toggleFeature(type, enable) {
enable
? this.$root.utools.whole.setFeature(_.cloneDeep(features[type]))
: this.$root.utools.whole.removeFeature(features[type].code);
}
}
};
</script>

View File

@ -0,0 +1,152 @@
<template>
<div>
<q-menu
target="#menuBtn"
max-height="480px"
max-width="300px"
transition-show="jump-up"
transition-hide="jump-down"
>
<!-- 用户信息 -->
<UserInfo
ref="user"
:allQuickCommandsLength="allQuickCommandsLength"
:allFeaturesLength="allFeaturesLength"
/>
<!-- 菜单列表 -->
<q-list>
<!-- 命令管理 -->
<q-item clickable>
<q-item-section side>
<q-icon name="keyboard_arrow_left" />
</q-item-section>
<q-item-section>命令管理</q-item-section>
<CommandManageMenu />
</q-item>
<!-- 实用功能 -->
<q-item clickable>
<q-item-section side>
<q-icon name="keyboard_arrow_left" />
</q-item-section>
<q-item-section>实用功能</q-item-section>
<UtilityFeaturesMenu />
</q-item>
<!-- 环境配置 -->
<q-item clickable>
<q-item-section side>
<q-icon name="keyboard_arrow_left" />
</q-item-section>
<q-item-section>环境配置</q-item-section>
<EnvConfigMenu @open-user-data="showUserData = true" />
</q-item>
<!-- 个性化设置 -->
<q-item clickable>
<q-item-section side>
<q-icon name="keyboard_arrow_left" />
</q-item-section>
<q-item-section>个性化设置</q-item-section>
<PersonalizeMenu :user="$refs.user" />
</q-item>
<!-- 收藏 -->
<q-item v-if="isTagStared" clickable v-close-popup @click="unMarkTag">
<q-item-section side>
<q-icon name="star_border" />
</q-item-section>
<q-item-section>取消收藏</q-item-section>
</q-item>
<q-item v-else clickable v-close-popup @click="showPanelConf = true">
<q-item-section side>
<q-icon name="star" />
</q-item-section>
<q-item-section>收藏标签</q-item-section>
<q-tooltip>
收藏后会将当前标签名作为全局关键字可在 uTools 的主输入框进行搜索
<br />
搜索进入后默认进入当前标签的面板视图 <br />
类似于旧版本的快捷面板
</q-tooltip>
</q-item>
<!-- 关于 -->
<q-item clickable v-close-popup @click="showAbout = true">
<q-item-section side>
<q-icon name="info" />
</q-item-section>
<q-item-section>关于和帮助</q-item-section>
</q-item>
</q-list>
</q-menu>
<!-- 弹窗组件 -->
<q-dialog v-model="showAbout">
<AboutThis />
</q-dialog>
<q-dialog v-model="showPanelConf">
<PanelSetting :isTagStared="isTagStared" :currentTag="currentTag" />
</q-dialog>
<q-dialog v-model="showUserData">
<UserData :showInsertBtn="false" />
</q-dialog>
</div>
</template>
<script>
import AboutThis from "../popup/AboutThis";
import PanelSetting from "../popup/PanelSetting";
import UserInfo from "../popup/UserInfo";
import CommandManageMenu from "./CommandManageMenu.vue";
import UtilityFeaturesMenu from "./UtilityFeaturesMenu.vue";
import EnvConfigMenu from "./EnvConfigMenu.vue";
import PersonalizeMenu from "./PersonalizeMenu.vue";
import UserData from "../popup/UserData.vue";
export default {
name: "ConfigurationMenu",
components: {
AboutThis,
PanelSetting,
UserInfo,
CommandManageMenu,
UtilityFeaturesMenu,
EnvConfigMenu,
PersonalizeMenu,
UserData,
},
data() {
return {
showAbout: false,
showPanelConf: false,
showUserData: false,
};
},
props: {
isTagStared: Boolean,
currentTag: String,
},
computed: {
configurationPage() {
return this.$root.$refs.view;
},
allQuickCommandsLength() {
return Object.keys(this.configurationPage.allQuickCommands).length;
},
allFeaturesLength() {
return this.configurationPage.activatedQuickCommandFeatureCodes.length;
},
},
methods: {
unMarkTag() {
this.$root.utools.whole.removeFeature(
`panel_${window.hexEncode(this.currentTag)}`
);
_.pull(this.$root.$refs.view.activatedQuickPanels, this.currentTag);
quickcommand.showMessageBox("取消收藏成功");
},
},
};
</script>

View File

@ -144,22 +144,6 @@ body {
height: 100vh; height: 100vh;
} }
.config-page-container {
position: relative;
height: 100vh;
width: 100%;
overflow: hidden;
}
.background-layer {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 0;
}
/* 毛玻璃效果叠加 */ /* 毛玻璃效果叠加 */
body.glass-effect-menu .q-card.command { body.glass-effect-menu .q-card.command {
background: rgba(255, 255, 255, calc(0.3 + var(--glass-effect-strength) * 0.01)) !important; background: rgba(255, 255, 255, calc(0.3 + var(--glass-effect-strength) * 0.01)) !important;
@ -181,3 +165,59 @@ body.body--dark.glass-effect-menu .q-tabs {
background: rgba(0, 0, 0, calc(0.2 + var(--glass-effect-strength) * 0.02)) !important; background: rgba(0, 0, 0, calc(0.2 + var(--glass-effect-strength) * 0.02)) !important;
border-right: 1px solid rgba(255, 255, 255, 0.05); border-right: 1px solid rgba(255, 255, 255, 0.05);
} }
/* 基础菜单样式 - 始终保持最小毛玻璃效果 */
.q-menu {
background: rgba(255, 255, 255, 0.15) !important;
backdrop-filter: blur(5px) !important;
-webkit-backdrop-filter: blur(5px) !important;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 16px 0 rgba(31, 38, 135, 0.07);
}
.body--dark .q-menu {
background: rgba(0, 0, 0, 0.2) !important;
border: 1px solid rgba(255, 255, 255, 0.05);
}
/* 毛玻璃菜单效果 - 叠加用户设置的效果 */
body.glass-effect-menu .q-menu {
background: rgba(255,
255,
255,
calc(0.15 + var(--glass-effect-strength) * 0.01)) !important;
backdrop-filter: blur(calc(5px + var(--glass-effect-strength) * 1px)) !important;
-webkit-backdrop-filter: blur(calc(5px + var(--glass-effect-strength) * 1px)) !important;
}
/* 暗色模式菜单 */
body.body--dark.glass-effect-menu .q-menu {
background: rgba(0,
0,
0,
calc(0.2 + var(--glass-effect-strength) * 0.02)) !important;
}
/* 菜单列表透明背景 */
.q-menu .q-list {
background: transparent !important;
}
/* 菜单项浮效果 */
.q-menu .q-item:hover {
background: rgba(255, 255, 255, 0.1) !important;
}
.body--dark .q-menu .q-item:hover {
background: rgba(255, 255, 255, 0.05) !important;
}
/* 输入框样式 */
.q-menu .q-field__control {
background: rgba(255, 255, 255, 0.15) !important;
border-radius: 4px;
}
.body--dark .q-menu .q-field__control {
background: rgba(0, 0, 0, 0.3) !important;
}

View File

@ -244,13 +244,11 @@ import changeLog from "js/options/changeLog.js";
import pinyinMatch from "pinyin-match"; import pinyinMatch from "pinyin-match";
import CommandCard from "components/CommandCard"; import CommandCard from "components/CommandCard";
import CommandEditor from "components/CommandEditor"; import CommandEditor from "components/CommandEditor";
import ConfigurationMenu from "components/menu";
const CommandRunResult = defineAsyncComponent(() => const CommandRunResult = defineAsyncComponent(() =>
import("components/CommandRunResult.vue") import("components/CommandRunResult.vue")
); );
// Performance Rendering > 300ms // Performance Rendering > 300ms
const ConfigurationMenu = defineAsyncComponent(() =>
import("components/ConfigurationMenu.vue")
);
// //
let defaultCommands = importAll(require.context("../json/", false, /\.json$/)); let defaultCommands = importAll(require.context("../json/", false, /\.json$/));
@ -568,7 +566,10 @@ export default {
this.getAllQuickCommandTags(); this.getAllQuickCommandTags();
this.changeCurrentTag("默认"); this.changeCurrentTag("默认");
quickcommand.showMessageBox( quickcommand.showMessageBox(
"清空完毕,为防止误操作,已将所有命令复制到剪贴板,可通过导入命令恢复" "清空完毕,为防止误操作,已将所有命令复制到剪贴板,可通过导入命令恢复",
"success",
2000,
"bottom-right"
); );
}); });
}, },
@ -655,6 +656,22 @@ export default {
</script> </script>
<style scoped> <style scoped>
.config-page-container {
position: relative;
height: 100vh;
width: 100%;
overflow: hidden;
}
.background-layer {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 0;
}
/* 标签栏容器样式 */ /* 标签栏容器样式 */
.q-tabs { .q-tabs {
height: 100vh !important; height: 100vh !important;