mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-08 14:34:13 +08:00
命令卡片组件化
This commit is contained in:
parent
5e654a8964
commit
45a2170ff3
@ -1,301 +1,48 @@
|
||||
<template>
|
||||
<!-- mini 模式下如果命令未启用或者不可直接运行则隐藏卡片面板 -->
|
||||
<div
|
||||
:class="{ wrapper: 1, warpperHover: isWarpperHover }"
|
||||
:class="{
|
||||
'card-wrapper': 1,
|
||||
'card-wrapper-hover': isWarpperHover,
|
||||
}"
|
||||
v-show="!cardStyleVars.hideCard"
|
||||
:id="commandInfo.features.code"
|
||||
@mouseenter="isWarpperHover = true"
|
||||
@mouseleave="if (!isCtrlBtnMenuOpen) isWarpperHover = false;"
|
||||
@mouseleave="if (!$refs.controlButtons?.isMenuOpen) isWarpperHover = false;"
|
||||
>
|
||||
<div>
|
||||
<!-- mini 模式下不显示各类按钮 -->
|
||||
<div v-show="cardStyleVars.showButtons">
|
||||
<!-- 开关 -->
|
||||
<div class="absolute" style="z-index: 1; left: 20px; bottom: 16px">
|
||||
<q-toggle
|
||||
:model-value="isCommandActivated"
|
||||
checked-icon="flash_on"
|
||||
color="orange-6"
|
||||
@click="toggleCommandActivated"
|
||||
<ControlButtons
|
||||
ref="controlButtons"
|
||||
v-model:isVisible="isWarpperHover"
|
||||
v-show="cardStyleVars.showButtons"
|
||||
:isActivated="isCommandActivated"
|
||||
:commandInfo="commandInfo"
|
||||
:canRunAtCurrentOS="canRunAtCurrentOS"
|
||||
@commandChanged="$emit('commandChanged', $event)"
|
||||
/>
|
||||
</div>
|
||||
<!-- 选项按钮 -->
|
||||
<div
|
||||
class="absolute control-buttons"
|
||||
style="z-index: 1; right: 16px; top: 16px"
|
||||
:class="{ 'buttons-visible': isWarpperHover }"
|
||||
>
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
color="green"
|
||||
icon="play_arrow"
|
||||
v-show="canCommandRun"
|
||||
@click="runCommand"
|
||||
><q-tooltip anchor="top middle" self="center middle">
|
||||
运行
|
||||
</q-tooltip></q-btn
|
||||
>
|
||||
<q-btn
|
||||
v-if="canCommandEdit"
|
||||
dense
|
||||
flat
|
||||
round
|
||||
:color="!!cronExp ? 'amber' : 'blue-9'"
|
||||
:icon="!!cronExp ? 'timer' : 'insights'"
|
||||
@click="isCtrlBtnMenuOpen = true"
|
||||
>
|
||||
<q-tooltip anchor="top middle" self="center middle">
|
||||
设置
|
||||
</q-tooltip>
|
||||
<q-menu
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
@hide="
|
||||
isCtrlBtnMenuOpen = false;
|
||||
isWarpperHover = false;
|
||||
"
|
||||
>
|
||||
<q-list style="min-width: 100px">
|
||||
<q-item clickable v-close-popup @click="exportCommandFile">
|
||||
<q-item-section side>
|
||||
<q-icon name="save" />
|
||||
</q-item-section>
|
||||
<q-item-section>导出</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-close-popup @click="exportCommandRaw">
|
||||
<q-item-section side>
|
||||
<q-icon name="content_paste_go" />
|
||||
</q-item-section>
|
||||
<q-item-section>复制到剪贴板</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable @click="shareCommand" v-close-popup>
|
||||
<q-item-section side>
|
||||
<q-icon name="share" />
|
||||
</q-item-section>
|
||||
<q-item-section>分享</q-item-section>
|
||||
<q-tooltip>分享到分享中心</q-tooltip>
|
||||
</q-item>
|
||||
<q-item
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="showCrontab = true"
|
||||
:disable="!canCommandAddCron"
|
||||
>
|
||||
<q-item-section side>
|
||||
<q-icon name="timer" />
|
||||
</q-item-section>
|
||||
<q-item-section>定时任务</q-item-section>
|
||||
<q-tooltip
|
||||
>在后台定时执行当前命令,仅匹配类型为关键字时可用,且一律忽略输出<br />
|
||||
不同电脑间的定时任务不会同步</q-tooltip
|
||||
>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="canCommandEdit"
|
||||
flat
|
||||
round
|
||||
dense
|
||||
color="red"
|
||||
icon="close"
|
||||
@click="removeCommand"
|
||||
><q-tooltip anchor="top middle" self="center middle">
|
||||
删除
|
||||
</q-tooltip></q-btn
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 未启用的命令文字为灰色 -->
|
||||
<q-card
|
||||
<CommandCardContent
|
||||
:commandInfo="commandInfo"
|
||||
:isActivated="isCommandActivated"
|
||||
:cardStyleVars="cardStyleVars"
|
||||
:canRunAtCurrentOS="canRunAtCurrentOS"
|
||||
:isHovered="isWarpperHover"
|
||||
@click="handleCardClick"
|
||||
v-ripple
|
||||
:class="{ [`text-${disabledColor}`]: !isCommandActivated, command: 1 }"
|
||||
>
|
||||
<q-card-section>
|
||||
<!-- logo -->
|
||||
<div class="row" :class="cardStyleVars.logoPosition">
|
||||
<q-avatar
|
||||
square
|
||||
size="48px"
|
||||
:class="{
|
||||
featureIco: 1,
|
||||
featureIcoHover: isWarpperHover,
|
||||
'feature-disabled': !isCommandActivated,
|
||||
}"
|
||||
>
|
||||
<img :src="commandInfo.features.icon" />
|
||||
</q-avatar>
|
||||
</div>
|
||||
<!-- 名称 -->
|
||||
<!-- mini 和 small 模式下命令标题字体变小 -->
|
||||
<div :class="'row ' + cardStyleVars.fontPosition">
|
||||
<div
|
||||
class="ellipsis"
|
||||
:style="{
|
||||
fontSize: cardStyleVars.showBiggerTitle ? '1.25rem' : '1.1rem',
|
||||
}"
|
||||
v-html="commandInfo.features.explain.trim() || '<br/>'"
|
||||
/>
|
||||
</div>
|
||||
<!-- 匹配模式 -->
|
||||
<div class="row">
|
||||
<div
|
||||
:class="
|
||||
'matchTypesBox flex q-gutter-xs ' + cardStyleVars.fontPosition
|
||||
"
|
||||
>
|
||||
<span v-for="cmd in commandInfo.features.cmds" :key="cmd">
|
||||
<span v-if="typeof cmd === 'string'">
|
||||
<q-badge
|
||||
rounded
|
||||
:color="matchTypeColor()"
|
||||
:class="cardBadgeClass"
|
||||
>
|
||||
<q-icon class="q-mr-xs" :name="commandTypes.key.icon" />
|
||||
{{ getShortStrByByte(cmd) }}
|
||||
</q-badge>
|
||||
<q-tooltip>
|
||||
<div class="text-subtitle2">
|
||||
{{ cmd }}
|
||||
</div>
|
||||
</q-tooltip>
|
||||
</span>
|
||||
<span v-else-if="cmd.type === 'window' && cmd.match">
|
||||
<q-badge
|
||||
rounded
|
||||
:color="matchTypeColor(cmd.type)"
|
||||
:class="cardBadgeClass"
|
||||
>
|
||||
<q-icon class="q-mr-xs" :name="commandTypes.window.icon" />
|
||||
{{ getShortStrByByte(cmd.match.app[0]) }}
|
||||
</q-badge>
|
||||
<q-tooltip>
|
||||
<div
|
||||
class="text-subtitle2"
|
||||
v-for="app in cmd.match.app"
|
||||
:key="app"
|
||||
>
|
||||
{{ app }}
|
||||
</div>
|
||||
</q-tooltip>
|
||||
</span>
|
||||
<span v-else-if="cmd.type === 'files'">
|
||||
<q-badge
|
||||
rounded
|
||||
:color="matchTypeColor(cmd.type)"
|
||||
:class="cardBadgeClass"
|
||||
>
|
||||
<q-icon class="q-mr-xs" :name="commandTypes.files.icon" />
|
||||
{{
|
||||
(cmd.match && getShortStrByByte(cmd.match)) || "所有文件"
|
||||
}}
|
||||
</q-badge>
|
||||
<q-tooltip>
|
||||
<div class="text-subtitle2">
|
||||
{{ cmd.match || "所有文件" }}
|
||||
</div>
|
||||
</q-tooltip>
|
||||
</span>
|
||||
<span v-else-if="cmd.type === 'regex'">
|
||||
<q-badge
|
||||
rounded
|
||||
:color="matchTypeColor(cmd.type)"
|
||||
:class="cardBadgeClass"
|
||||
>
|
||||
<q-icon class="q-mr-xs" :name="commandTypes.regex.icon" />
|
||||
{{ getShortStrByByte(cmd.match) }}
|
||||
</q-badge>
|
||||
<q-tooltip>
|
||||
<div class="text-subtitle2">
|
||||
{{ cmd.match }}
|
||||
</div>
|
||||
</q-tooltip>
|
||||
</span>
|
||||
<span v-else-if="cmd.type === 'over'">
|
||||
<q-badge
|
||||
rounded
|
||||
:color="matchTypeColor(cmd.type)"
|
||||
:class="cardBadgeClass"
|
||||
>
|
||||
<q-icon
|
||||
class="q-mr-xs"
|
||||
:name="commandTypes.over.icon"
|
||||
/>所有文本
|
||||
</q-badge>
|
||||
</span>
|
||||
<span v-else-if="cmd.type === 'img'">
|
||||
<q-badge
|
||||
rounded
|
||||
:color="matchTypeColor(cmd.type)"
|
||||
:class="cardBadgeClass"
|
||||
>
|
||||
<q-icon class="q-mr-xs" :name="commandTypes.img.icon" />图片
|
||||
</q-badge>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- mini模式下不显示语言类型和适配系统 -->
|
||||
<!-- 语言类型 -->
|
||||
<div
|
||||
class="row justify-end items-center q-gutter-xs"
|
||||
v-show="cardStyleVars.showLanguages"
|
||||
>
|
||||
<span :class="`text-${programColor}`">●</span>
|
||||
<span class="text-subtitle2">{{ commandInfo.program }}</span>
|
||||
<!-- mini 和 small 模式下不显示适配系统 -->
|
||||
<!-- 适配系统 -->
|
||||
<div class="q-gutter-xs" v-show="cardStyleVars.showPlatforms">
|
||||
<span
|
||||
v-for="platform in commandInfo.features.platform"
|
||||
:key="platform"
|
||||
:class="`iconfont icon-${platformTypes[platform].icon} text-${programColor}`"
|
||||
style="font-size: 12px"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
<q-dialog v-model="showCrontab">
|
||||
<CrontabSetting
|
||||
:cronExp="cronExp"
|
||||
@addCrontab="addCrontab"
|
||||
@delCrontab="delCrontab"
|
||||
/>
|
||||
</q-dialog>
|
||||
<!-- <q-dialog v-model="showShare">
|
||||
<ShareDialog :command="getRawCommand(commandInfo)" />
|
||||
</q-dialog> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import commandTypes from "js/options/commandTypes.js";
|
||||
import platformTypes from "js/options/platformTypes.js";
|
||||
import CrontabSetting from "components/popup/CrontabSetting";
|
||||
// import ShareDialog from "components/popup/ShareDialog";
|
||||
import ControlButtons from "components/card/ControlButtons.vue";
|
||||
import CommandCardContent from "components/card/CommandCardContent.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CrontabSetting,
|
||||
// ShareDialog
|
||||
ControlButtons,
|
||||
CommandCardContent,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
allProgrammings: this.$root.programs,
|
||||
maxCmdStingLen: 8,
|
||||
commandTypes: commandTypes,
|
||||
platformTypes: platformTypes,
|
||||
showCrontab: false,
|
||||
// showShare: false,
|
||||
cronJob: null,
|
||||
isWarpperHover: false,
|
||||
isCtrlBtnMenuOpen: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -312,64 +59,15 @@ export default {
|
||||
this.cardStyle.code > 1 ? "justify-end" : "justify-center",
|
||||
hideCard:
|
||||
this.cardStyle.code === 1 &&
|
||||
(!this.isCommandActivated || !this.canCommandRun),
|
||||
(!this.isCommandActivated || !this.$refs.controlButtons?.canRun),
|
||||
};
|
||||
},
|
||||
// 命令是否适合当前平台
|
||||
canCommandRunAtCurrentOS() {
|
||||
canRunAtCurrentOS() {
|
||||
let { platform } = this.commandInfo.features;
|
||||
return !_.isEmpty(platform) && !platform.includes(window.processPlatform)
|
||||
? false
|
||||
: true;
|
||||
},
|
||||
// 命令是否可直接运行, 无论 cmds 长度为多少,只运行 cmds[0]
|
||||
canCommandRun() {
|
||||
// 未启用
|
||||
if (!this.isCommandActivated) return false;
|
||||
if (!this.canCommandRunAtCurrentOS) return false;
|
||||
let { cmds } = this.commandInfo.features;
|
||||
// 窗口模式
|
||||
if (cmds[0].type && cmds[0].type === "window") return false;
|
||||
return true;
|
||||
},
|
||||
// 命令未启用也可以添加计划任务
|
||||
canCommandAddCron() {
|
||||
return !!this.commandInfo.features.cmds[0].length;
|
||||
},
|
||||
// 默认命令不可删除
|
||||
canCommandEdit() {
|
||||
if (utools.isDev()) return true;
|
||||
return this.commandInfo.tags?.includes("默认") ? false : true;
|
||||
},
|
||||
// 匹配类型的颜色
|
||||
matchTypeColor() {
|
||||
return (cmdType = "key") => {
|
||||
if (!this.canCommandRunAtCurrentOS || !this.isCommandActivated) {
|
||||
return this.$q.dark.isActive ? "grey-9" : this.disabledColor;
|
||||
}
|
||||
return this.commandTypes[cmdType].color;
|
||||
};
|
||||
},
|
||||
programColor() {
|
||||
return this.isCommandActivated
|
||||
? this.allProgrammings[this.commandInfo.program].color
|
||||
: this.disabledColor;
|
||||
},
|
||||
disabledColor() {
|
||||
return this.$q.dark.isActive ? "grey-6" : "grey-5";
|
||||
},
|
||||
featureCode() {
|
||||
return this.commandInfo.features.code;
|
||||
},
|
||||
cronExp() {
|
||||
return this.$root.nativeProfile.crontabs[this.featureCode];
|
||||
},
|
||||
cardBadgeClass() {
|
||||
return (!this.canCommandRunAtCurrentOS || !this.isCommandActivated) &&
|
||||
this.$q.dark.isActive
|
||||
? "text-grey-6"
|
||||
: "";
|
||||
},
|
||||
},
|
||||
props: {
|
||||
commandInfo: Object,
|
||||
@ -377,96 +75,22 @@ export default {
|
||||
cardStyle: Object,
|
||||
},
|
||||
methods: {
|
||||
// 匹配类型太长的话截断
|
||||
getShortStrByByte(str) {
|
||||
let byteLen = 0;
|
||||
let shortStr = "";
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
byteLen += 129 - str[i].charCodeAt(0) > 0 ? 1 : 2;
|
||||
if (byteLen > this.maxCmdStingLen) break;
|
||||
shortStr += str[i];
|
||||
}
|
||||
return shortStr === str ? shortStr : shortStr + "...";
|
||||
},
|
||||
// 命令卡片点击事件
|
||||
handleCardClick() {
|
||||
// 视图模式直接运行命令
|
||||
if (this.cardStyle.code === 1) return this.runCommand();
|
||||
if (this.cardStyle.code === 1) {
|
||||
return this.$refs.controlButtons.runCommand();
|
||||
}
|
||||
this.editCommand();
|
||||
},
|
||||
// 编辑命令
|
||||
editCommand() {
|
||||
let event = {
|
||||
type: "edit",
|
||||
data: this.featureCode,
|
||||
data: this.commandInfo.features.code,
|
||||
};
|
||||
this.$emit("commandChanged", event);
|
||||
},
|
||||
// 运行命令
|
||||
runCommand() {
|
||||
let event = {
|
||||
type: "run",
|
||||
data: this.featureCode,
|
||||
};
|
||||
this.$emit("commandChanged", event);
|
||||
},
|
||||
addCrontab(cronExp) {
|
||||
this.$root.nativeProfile.crontabs[this.featureCode] = cronExp;
|
||||
this.$root.runCronTask(this.featureCode, cronExp);
|
||||
},
|
||||
delCrontab() {
|
||||
delete this.$root.nativeProfile.crontabs[this.featureCode];
|
||||
this.$root.cronJobs[this.featureCode].stop();
|
||||
},
|
||||
// 启用/禁用命令
|
||||
toggleCommandActivated() {
|
||||
let event = {
|
||||
data: this.featureCode,
|
||||
type: "toggle",
|
||||
};
|
||||
event.type = this.isCommandActivated ? "disable" : "enable";
|
||||
this.$emit("commandChanged", event);
|
||||
},
|
||||
// 移除命令
|
||||
removeCommand() {
|
||||
quickcommand.showConfirmBox("删除这个快捷命令").then((x) => {
|
||||
if (!x) return;
|
||||
this.isCommandAlive = false;
|
||||
let code = this.featureCode;
|
||||
this.$emit("commandChanged", {
|
||||
type: "remove",
|
||||
data: code,
|
||||
});
|
||||
});
|
||||
},
|
||||
getRawCommand() {
|
||||
let command = _.cloneDeep(this.commandInfo);
|
||||
command.features.explain = window.removeHtmlTags(
|
||||
command.features.explain
|
||||
);
|
||||
return command;
|
||||
},
|
||||
// 导出到剪贴板
|
||||
exportCommandRaw() {
|
||||
utools.copyText(JSON.stringify(this.getRawCommand(), null, 4)) &&
|
||||
utools.showNotification(
|
||||
`「${this.commandInfo.features.explain}」已复制到剪贴板`
|
||||
);
|
||||
},
|
||||
// 导出到文件
|
||||
exportCommandFile() {
|
||||
window.saveFile(JSON.stringify(this.getRawCommand()), {
|
||||
title: "选择保存位置",
|
||||
defaultPath: `${window.removeHtmlTags(
|
||||
this.commandInfo.features.explain
|
||||
)}.json`,
|
||||
filters: [{ name: "json", extensions: ["json"] }],
|
||||
});
|
||||
},
|
||||
shareCommand() {
|
||||
this.exportCommandRaw();
|
||||
utools.shellOpenExternal("https://qc.qaz.ink/submit");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@ -492,50 +116,12 @@ export default {
|
||||
0 3px 1px -2px rgb(69 67 67 / 12%);
|
||||
}
|
||||
|
||||
.q-badge {
|
||||
font-size: 15px;
|
||||
margin: 0 1px;
|
||||
}
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.matchTypesBox {
|
||||
height: 23px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
}
|
||||
.wrapper {
|
||||
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.warpperHover {
|
||||
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
.featureIco {
|
||||
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.featureIcoHover {
|
||||
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.control-buttons {
|
||||
opacity: 0;
|
||||
transform: translateY(5px);
|
||||
visibility: hidden;
|
||||
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
.card-wrapper {
|
||||
transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.control-buttons.buttons-visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.feature-disabled {
|
||||
opacity: 0.5;
|
||||
filter: grayscale(100%);
|
||||
.card-wrapper-hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
</style>
|
||||
|
190
src/components/card/CommandCardContent.vue
Normal file
190
src/components/card/CommandCardContent.vue
Normal file
@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<q-card
|
||||
@click="$emit('click')"
|
||||
v-ripple
|
||||
:class="{ [`text-${disabledColor}`]: !isActivated, command: 1 }"
|
||||
>
|
||||
<q-card-section>
|
||||
<!-- logo -->
|
||||
<div class="row" :class="cardStyleVars.logoPosition">
|
||||
<q-avatar
|
||||
square
|
||||
size="48px"
|
||||
:class="{
|
||||
featureIco: 1,
|
||||
featureIcoHover: isHovered,
|
||||
'feature-disabled': !isActivated,
|
||||
}"
|
||||
>
|
||||
<img :src="commandInfo.features.icon" />
|
||||
</q-avatar>
|
||||
</div>
|
||||
|
||||
<!-- 名称 -->
|
||||
<div :class="'row ' + cardStyleVars.fontPosition">
|
||||
<div
|
||||
class="ellipsis"
|
||||
:style="{
|
||||
fontSize: cardStyleVars.showBiggerTitle ? '16px' : '14px',
|
||||
}"
|
||||
v-html="commandInfo.features.explain.trim() || '<br/>'"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 匹配模式 -->
|
||||
<div class="row">
|
||||
<div
|
||||
:class="
|
||||
'matchTypesBox flex q-gutter-xs ' + cardStyleVars.fontPosition
|
||||
"
|
||||
>
|
||||
<span v-for="cmd in commandInfo.features.cmds" :key="cmd">
|
||||
<CommandTypeTag
|
||||
:cmd="cmd"
|
||||
:isGrayColor="!canRunAtCurrentOS || !isActivated"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 语言类型和适配系统 -->
|
||||
<div
|
||||
class="row justify-end items-center q-gutter-xs"
|
||||
v-show="cardStyleVars.showLanguages"
|
||||
>
|
||||
<span :class="`text-${programColor}`">●</span>
|
||||
<span class="text-subtitle2">{{ commandInfo.program }}</span>
|
||||
|
||||
<div class="q-gutter-xs" v-show="cardStyleVars.showPlatforms">
|
||||
<span
|
||||
v-for="platform in commandInfo.features.platform"
|
||||
:key="platform"
|
||||
:class="`iconfont icon-${platformTypes[platform].icon} text-${programColor}`"
|
||||
style="font-size: 12px"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import commandTypes from "js/options/commandTypes.js";
|
||||
import platformTypes from "js/options/platformTypes.js";
|
||||
import CommandTypeTag from "./CommandTypeTag.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CommandTypeTag,
|
||||
},
|
||||
props: {
|
||||
commandInfo: Object,
|
||||
isActivated: Boolean,
|
||||
cardStyleVars: Object,
|
||||
canRunAtCurrentOS: Boolean,
|
||||
isHovered: Boolean,
|
||||
},
|
||||
emits: ["click"],
|
||||
data() {
|
||||
return {
|
||||
commandTypes,
|
||||
platformTypes,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
programColor() {
|
||||
return this.isActivated
|
||||
? this.$root.programs[this.commandInfo.program].color
|
||||
: this.disabledColor;
|
||||
},
|
||||
disabledColor() {
|
||||
return this.$q.dark.isActive ? "grey-6" : "grey-5";
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// 设置图标URL作为光晕背景
|
||||
this.$el.style.setProperty(
|
||||
"--icon-url",
|
||||
`url(${this.commandInfo.features.icon})`
|
||||
);
|
||||
},
|
||||
watch: {
|
||||
"commandInfo.features.icon"(newVal) {
|
||||
this.$el.style.setProperty("--icon-url", `url(${newVal})`);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.q-card.command {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
background: rgba(255, 255, 255, 0.3) !important;
|
||||
backdrop-filter: blur(calc(var(--glass-effect-strength) * 1px)) !important;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 4px 16px 0 rgba(31, 38, 135, 0.07);
|
||||
will-change: transform, box-shadow;
|
||||
}
|
||||
|
||||
.body--dark .q-card.command {
|
||||
background: rgba(57, 57, 57, 0.09) !important;
|
||||
border: 1px solid rgb(59 58 58 / 5%);
|
||||
box-shadow: 0 1px 5px rgb(0 0 0 / 20%), 0 2px 2px rgb(0 0 0 / 14%),
|
||||
0 3px 1px -2px rgb(69 67 67 / 12%);
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.matchTypesBox {
|
||||
height: 23px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.featureIco {
|
||||
transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
will-change: transform;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.featureIcoHover {
|
||||
transform: scale(1.08) translateY(-2px);
|
||||
}
|
||||
|
||||
.featureIco::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
opacity: 0;
|
||||
background-image: var(--icon-url);
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
filter: blur(6px) brightness(1.2);
|
||||
transform: scale(1.1);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.featureIcoHover::after {
|
||||
opacity: 1;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.feature-disabled {
|
||||
opacity: 0.5;
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
</style>
|
96
src/components/card/CommandTypeTag.vue
Normal file
96
src/components/card/CommandTypeTag.vue
Normal file
@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<span v-if="typeof cmd === 'string'">
|
||||
<q-badge rounded :class="badgeClass">
|
||||
<q-icon class="q-mr-xs" :name="commandTypes.key.icon" />
|
||||
<span class="badge-text">{{ cmd }}</span>
|
||||
</q-badge>
|
||||
<q-tooltip>
|
||||
<div class="text-subtitle2">{{ cmd }}</div>
|
||||
</q-tooltip>
|
||||
</span>
|
||||
<span v-else-if="cmd.type === 'window' && cmd.match">
|
||||
<q-badge rounded :class="badgeClass">
|
||||
<q-icon class="q-mr-xs" :name="commandTypes.window.icon" />
|
||||
<span class="badge-text">{{ cmd.match.app[0] }}</span>
|
||||
</q-badge>
|
||||
<q-tooltip>
|
||||
<div class="text-subtitle2" v-for="app in cmd.match.app" :key="app">
|
||||
{{ app }}
|
||||
</div>
|
||||
</q-tooltip>
|
||||
</span>
|
||||
<span v-else-if="cmd.type === 'files'">
|
||||
<q-badge rounded :class="badgeClass">
|
||||
<q-icon class="q-mr-xs" :name="commandTypes.files.icon" />
|
||||
<span class="badge-text">{{ cmd.match || "所有文件" }}</span>
|
||||
</q-badge>
|
||||
<q-tooltip>
|
||||
<div class="text-subtitle2">
|
||||
{{ cmd.match || "所有文件" }}
|
||||
</div>
|
||||
</q-tooltip>
|
||||
</span>
|
||||
<span v-else-if="cmd.type === 'regex'">
|
||||
<q-badge rounded :class="badgeClass">
|
||||
<q-icon class="q-mr-xs" :name="commandTypes.regex.icon" />
|
||||
<span class="badge-text">{{ cmd.match }}</span>
|
||||
</q-badge>
|
||||
<q-tooltip>
|
||||
<div class="text-subtitle2">
|
||||
{{ cmd.match }}
|
||||
</div>
|
||||
</q-tooltip>
|
||||
</span>
|
||||
<span v-else-if="cmd.type === 'over'">
|
||||
<q-badge rounded :class="badgeClass">
|
||||
<q-icon class="q-mr-xs" :name="commandTypes.over.icon" />所有文本
|
||||
</q-badge>
|
||||
</span>
|
||||
<span v-else-if="cmd.type === 'img'">
|
||||
<q-badge rounded :class="badgeClass">
|
||||
<q-icon class="q-mr-xs" :name="commandTypes.img.icon" />图片
|
||||
</q-badge>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import commandTypes from "js/options/commandTypes.js";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
cmd: [String, Object],
|
||||
isGrayColor: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
commandTypes,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
badgeClass() {
|
||||
return this.isGrayColor
|
||||
? this.$q.dark.isActive
|
||||
? "text-grey-6 bg-grey-9"
|
||||
: "bg-grey-5"
|
||||
: "bg-" + this.commandTypes[this.cmd.type || "key"].color;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.q-badge {
|
||||
font-size: 13px;
|
||||
margin: 0 1px;
|
||||
max-width: 120px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.badge-text {
|
||||
display: inline-block;
|
||||
max-width: 80px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
</style>
|
250
src/components/card/ControlButtons.vue
Normal file
250
src/components/card/ControlButtons.vue
Normal file
@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 开关按钮 -->
|
||||
<div class="absolute" style="z-index: 1; left: 20px; bottom: 16px">
|
||||
<q-toggle
|
||||
:model-value="isActivated"
|
||||
checked-icon="flash_on"
|
||||
color="orange-6"
|
||||
@click="toggleCommandActivated"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 控制按钮组 -->
|
||||
<div
|
||||
class="absolute control-buttons"
|
||||
style="z-index: 1; right: 16px; top: 16px"
|
||||
:class="{ 'buttons-visible': isVisible }"
|
||||
>
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
color="green"
|
||||
icon="play_arrow"
|
||||
v-show="canRun"
|
||||
@click="runCommand"
|
||||
>
|
||||
<q-tooltip anchor="top middle" self="center middle">运行</q-tooltip>
|
||||
</q-btn>
|
||||
|
||||
<q-btn
|
||||
v-if="canEdit"
|
||||
dense
|
||||
flat
|
||||
round
|
||||
:color="!!cronExp ? 'amber' : 'blue-9'"
|
||||
:icon="!!cronExp ? 'timer' : 'insights'"
|
||||
@click="isMenuOpen = true"
|
||||
>
|
||||
<q-tooltip anchor="top middle" self="center middle">设置</q-tooltip>
|
||||
<q-menu
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
@hide="hideMenu"
|
||||
>
|
||||
<q-list style="min-width: 100px">
|
||||
<q-item clickable v-close-popup @click="exportCommandFile">
|
||||
<q-item-section side>
|
||||
<q-icon name="save" />
|
||||
</q-item-section>
|
||||
<q-item-section>导出</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item clickable v-close-popup @click="exportCommandRaw">
|
||||
<q-item-section side>
|
||||
<q-icon name="content_paste_go" />
|
||||
</q-item-section>
|
||||
<q-item-section>复制到剪贴板</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item clickable @click="shareCommand" v-close-popup>
|
||||
<q-item-section side>
|
||||
<q-icon name="share" />
|
||||
</q-item-section>
|
||||
<q-item-section>分享</q-item-section>
|
||||
<q-tooltip>分享到分享中心</q-tooltip>
|
||||
</q-item>
|
||||
|
||||
<q-item
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="showCrontab = true"
|
||||
:disable="!canAddCron"
|
||||
>
|
||||
<q-item-section side>
|
||||
<q-icon name="timer" />
|
||||
</q-item-section>
|
||||
<q-item-section>定时任务</q-item-section>
|
||||
<q-tooltip>
|
||||
在后台定时执行当前命令,仅匹配类型为关键字时可用,且一律忽略输出<br />
|
||||
不同电脑间的定时任务不会同步
|
||||
</q-tooltip>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
|
||||
<q-btn
|
||||
v-if="canEdit"
|
||||
flat
|
||||
round
|
||||
dense
|
||||
color="red"
|
||||
icon="close"
|
||||
@click="removeCommand"
|
||||
>
|
||||
<q-tooltip anchor="top middle" self="center middle">删除</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
|
||||
<!-- 定时任务设置对话框 -->
|
||||
<q-dialog v-model="showCrontab">
|
||||
<CrontabSetting
|
||||
:cronExp="cronExp"
|
||||
@addCrontab="addCrontab"
|
||||
@delCrontab="delCrontab"
|
||||
/>
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CrontabSetting from "components/popup/CrontabSetting";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CrontabSetting,
|
||||
},
|
||||
props: {
|
||||
isVisible: Boolean,
|
||||
isActivated: Boolean,
|
||||
commandInfo: Object,
|
||||
canRunAtCurrentOS: Boolean,
|
||||
},
|
||||
emits: ["update:isVisible", "commandChanged"],
|
||||
data() {
|
||||
return {
|
||||
isMenuOpen: false,
|
||||
showCrontab: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 获取定时任务表达式
|
||||
cronExp() {
|
||||
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() {
|
||||
return !!this.commandInfo.features.cmds[0].length;
|
||||
},
|
||||
// 默认命令不可删除
|
||||
canEdit() {
|
||||
if (window.utools.isDev()) return true;
|
||||
return this.commandInfo.tags?.includes("默认") ? false : true;
|
||||
},
|
||||
featureCode() {
|
||||
return this.commandInfo.features.code;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hideMenu() {
|
||||
this.isMenuOpen = false;
|
||||
this.$emit("update:isVisible", false);
|
||||
},
|
||||
runCommand() {
|
||||
this.$emit("commandChanged", {
|
||||
type: "run",
|
||||
data: this.featureCode,
|
||||
});
|
||||
},
|
||||
removeCommand() {
|
||||
quickcommand.showConfirmBox("删除这个快捷命令").then((ok) => {
|
||||
if (!ok) return;
|
||||
this.$emit("commandChanged", {
|
||||
type: "remove",
|
||||
data: this.featureCode,
|
||||
});
|
||||
});
|
||||
},
|
||||
toggleCommandActivated() {
|
||||
this.$emit("commandChanged", {
|
||||
type: this.isActivated ? "disable" : "enable",
|
||||
data: this.featureCode,
|
||||
});
|
||||
},
|
||||
getRawCommand() {
|
||||
let command = _.cloneDeep(this.commandInfo);
|
||||
command.features.explain = window.removeHtmlTags(
|
||||
command.features.explain
|
||||
);
|
||||
return command;
|
||||
},
|
||||
// 导出到剪贴板
|
||||
exportCommandRaw() {
|
||||
utools.copyText(JSON.stringify(this.getRawCommand(), null, 4)) &&
|
||||
utools.showNotification(
|
||||
`「${this.commandInfo.features.explain}」已复制到剪贴板`
|
||||
);
|
||||
},
|
||||
// 导出到文件
|
||||
exportCommandFile() {
|
||||
window.saveFile(JSON.stringify(this.getRawCommand()), {
|
||||
title: "选择保存位置",
|
||||
defaultPath: `${window.removeHtmlTags(
|
||||
this.commandInfo.features.explain
|
||||
)}.json`,
|
||||
filters: [{ name: "json", extensions: ["json"] }],
|
||||
});
|
||||
},
|
||||
shareCommand() {
|
||||
this.exportCommandRaw();
|
||||
utools.shellOpenExternal("https://qc.qaz.ink/submit");
|
||||
},
|
||||
addCrontab(cronExp) {
|
||||
this.$root.nativeProfile.crontabs[this.featureCode] = cronExp;
|
||||
this.$root.runCronTask(this.featureCode, cronExp);
|
||||
},
|
||||
delCrontab() {
|
||||
delete this.$root.nativeProfile.crontabs[this.featureCode];
|
||||
this.$root.cronJobs[this.featureCode].stop();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.control-buttons {
|
||||
opacity: 0;
|
||||
transform: translateY(5px);
|
||||
visibility: hidden;
|
||||
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
will-change: transform, opacity, visibility;
|
||||
}
|
||||
|
||||
.control-buttons.buttons-visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* 按钮悬浮效果 */
|
||||
.q-btn {
|
||||
transition: transform 0.35s cubic-bezier(0.68, -0.6, 0.32, 1.6);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.q-btn:hover {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user