命令卡片组件化

This commit is contained in:
fofolee 2024-12-20 17:32:27 +08:00
parent 5e654a8964
commit 45a2170ff3
4 changed files with 575 additions and 453 deletions

View File

@ -1,305 +1,52 @@
<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"
/>
</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
@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> -->
<!-- mini 模式下不显示各类按钮 -->
<ControlButtons
ref="controlButtons"
v-model:isVisible="isWarpperHover"
v-show="cardStyleVars.showButtons"
:isActivated="isCommandActivated"
:commandInfo="commandInfo"
:canRunAtCurrentOS="canRunAtCurrentOS"
@commandChanged="$emit('commandChanged', $event)"
/>
<CommandCardContent
:commandInfo="commandInfo"
:isActivated="isCommandActivated"
:cardStyleVars="cardStyleVars"
:canRunAtCurrentOS="canRunAtCurrentOS"
:isHovered="isWarpperHover"
@click="handleCardClick"
/>
</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: {
//
//
cardStyleVars() {
return {
showButtons: this.cardStyle.code > 1,
@ -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>

View 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>

View 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>

View 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>