mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-09 06:54:11 +08:00
完善卡片上标签显示逻辑的算法
This commit is contained in:
parent
b230088a3a
commit
5c02214063
77
src/components/card/CommandBadge.vue
Normal file
77
src/components/card/CommandBadge.vue
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<template>
|
||||||
|
<q-badge rounded :class="badgeClass">
|
||||||
|
<q-icon class="q-mr-xs" :name="iconName" :style="iconStyle" />
|
||||||
|
<span class="badge-text">{{ text }}</span>
|
||||||
|
<q-tooltip v-if="showTooltip">
|
||||||
|
<slot name="tooltip">
|
||||||
|
<template v-if="cmd.type === 'add'">
|
||||||
|
<slot />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="text-subtitle2">{{ text }}</div>
|
||||||
|
</template>
|
||||||
|
</slot>
|
||||||
|
</q-tooltip>
|
||||||
|
</q-badge>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import commandTypes from "js/options/commandTypes.js";
|
||||||
|
import { textDisplayRules } from "js/options/textDisplayRules.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "CommandBadge",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
commandTypes,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
cmd: {
|
||||||
|
type: [String, Object],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isGrayColor: Boolean,
|
||||||
|
showTooltip: Boolean,
|
||||||
|
iconStyle: Object,
|
||||||
|
inheritColor: String,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
badgeClass() {
|
||||||
|
if (this.isGrayColor) {
|
||||||
|
return this.$q.dark.isActive ? "text-grey-6 bg-grey-9" : "bg-grey-4";
|
||||||
|
}
|
||||||
|
const color =
|
||||||
|
this.cmd.type === "add" && this.inheritColor
|
||||||
|
? this.inheritColor
|
||||||
|
: this.commandTypes[this.cmd.type || "key"].color;
|
||||||
|
return "bg-" + color + (this.$q.dark.isActive ? "-10" : "-4");
|
||||||
|
},
|
||||||
|
iconName() {
|
||||||
|
if (typeof this.cmd === "string") {
|
||||||
|
return this.commandTypes.key.icon;
|
||||||
|
}
|
||||||
|
return this.cmd.type === "add"
|
||||||
|
? "add"
|
||||||
|
: this.commandTypes[this.cmd.type].icon;
|
||||||
|
},
|
||||||
|
text() {
|
||||||
|
const type = typeof this.cmd === "string" ? "string" : this.cmd.type;
|
||||||
|
return textDisplayRules[type]?.(this.cmd, false) ?? "";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.q-badge {
|
||||||
|
font-size: 12px;
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: var(--max-tag-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-text {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
</style>
|
@ -11,6 +11,7 @@
|
|||||||
:isPlatformSupported="isPlatformSupported"
|
:isPlatformSupported="isPlatformSupported"
|
||||||
:isHovered="isHovered"
|
:isHovered="isHovered"
|
||||||
:style="iconHaloStyle"
|
:style="iconHaloStyle"
|
||||||
|
:cardStyleCode="cardStyleCode"
|
||||||
/>
|
/>
|
||||||
</q-card>
|
</q-card>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,83 +1,192 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="matchTypesBox">
|
<div class="matchTypesBox" :style="{ '--max-tag-width': maxTagWidth + 'px' }">
|
||||||
<div v-for="cmd in cmds" :key="cmd">
|
<!-- 遍历显示可见的标签 -->
|
||||||
<span v-if="typeof cmd === 'string'">
|
<template v-for="(cmd, index) in visibleCommands" :key="index">
|
||||||
<q-badge rounded :class="getBadgeClass(cmd)">
|
<CommandBadge
|
||||||
<q-icon class="q-mr-xs" :name="commandTypes.key.icon" />
|
:cmd="cmd"
|
||||||
<span class="badge-text">{{ cmd }}</span>
|
:isGrayColor="isGrayColor"
|
||||||
</q-badge>
|
:showTooltip="needTooltip(cmd)"
|
||||||
<q-tooltip>
|
>
|
||||||
<div class="text-subtitle2">{{ cmd }}</div>
|
<!-- 窗口类型标签的额外应用列表提示 -->
|
||||||
</q-tooltip>
|
<template
|
||||||
</span>
|
v-if="cmd.type === 'window' && cmd.match.app.length > 1"
|
||||||
<span v-else-if="cmd.type === 'window' && cmd.match">
|
#tooltip
|
||||||
<q-badge rounded :class="getBadgeClass(cmd)">
|
>
|
||||||
<q-icon class="q-mr-xs" :name="commandTypes.window.icon" />
|
<div
|
||||||
<span class="badge-text">{{ cmd.match.app[0] }}</span>
|
class="text-subtitle2 row items-center"
|
||||||
</q-badge>
|
v-for="app in cmd.match.app.slice(1)"
|
||||||
<q-tooltip>
|
:key="app"
|
||||||
<div class="text-subtitle2" v-for="app in cmd.match.app" :key="app">
|
>
|
||||||
{{ app }}
|
<q-icon class="q-mr-xs" :name="commandTypes.window.icon" />
|
||||||
|
<span>{{ app }}</span>
|
||||||
</div>
|
</div>
|
||||||
</q-tooltip>
|
</template>
|
||||||
</span>
|
</CommandBadge>
|
||||||
<span v-else-if="cmd.type === 'files'">
|
|
||||||
<q-badge rounded :class="getBadgeClass(cmd)">
|
<!-- 窗口类型的额外应用数量标签 -->
|
||||||
<q-icon class="q-mr-xs" :name="commandTypes.files.icon" />
|
<CommandBadge
|
||||||
<span class="badge-text">{{ cmd.match || "所有文件" }}</span>
|
v-if="cmd.type === 'window' && cmd.match.app.length > 1"
|
||||||
</q-badge>
|
:cmd="{ type: 'add', count: cmd.match.app.length - 1 }"
|
||||||
<q-tooltip>
|
:isGrayColor="isGrayColor"
|
||||||
<div class="text-subtitle2">
|
:iconStyle="{ width: '8px' }"
|
||||||
{{ cmd.match || "所有文件" }}
|
:showTooltip="true"
|
||||||
|
:inheritColor="commandTypes[cmd.type].color"
|
||||||
|
>
|
||||||
|
<!-- 显示额外的应用列表 -->
|
||||||
|
<template #tooltip>
|
||||||
|
<template v-for="(app, i) in cmd.match.app.slice(1)" :key="i">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-icon class="q-mr-xs" :name="commandTypes.window.icon" />
|
||||||
|
<div>{{ app }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</CommandBadge>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 溢出标签(当标签数量超过显示限制时显示) -->
|
||||||
|
<CommandBadge
|
||||||
|
v-if="hasOverflow"
|
||||||
|
:cmd="{ type: 'add', count: cmds.length - maxTagNums }"
|
||||||
|
:isGrayColor="isGrayColor"
|
||||||
|
:iconStyle="{ width: '7px' }"
|
||||||
|
:showTooltip="true"
|
||||||
|
:inheritColor="
|
||||||
|
commandTypes[visibleCommands[visibleCommands.length - 1].type || 'key']
|
||||||
|
.color
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<!-- tooltip 显示所有溢出的标签 -->
|
||||||
|
<template #tooltip>
|
||||||
|
<template v-for="(cmd, i) in overflowCommands" :key="i">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-icon class="q-mr-xs" :name="getIconName(cmd)" />
|
||||||
|
<div>{{ getDisplayText(cmd) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</q-tooltip>
|
</template>
|
||||||
</span>
|
</template>
|
||||||
<span v-else-if="cmd.type === 'regex'">
|
</CommandBadge>
|
||||||
<q-badge rounded :class="getBadgeClass(cmd)">
|
|
||||||
<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="getBadgeClass(cmd)">
|
|
||||||
<q-icon class="q-mr-xs" :name="commandTypes.over.icon" />所有文本
|
|
||||||
</q-badge>
|
|
||||||
</span>
|
|
||||||
<span v-else-if="cmd.type === 'img'">
|
|
||||||
<q-badge rounded :class="getBadgeClass(cmd)">
|
|
||||||
<q-icon class="q-mr-xs" :name="commandTypes.img.icon" />图片
|
|
||||||
</q-badge>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import CommandBadge from "./CommandBadge.vue";
|
||||||
import commandTypes from "js/options/commandTypes.js";
|
import commandTypes from "js/options/commandTypes.js";
|
||||||
|
import { textDisplayRules } from "js/options/textDisplayRules.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "CommandTypeTag",
|
name: "CommandTypeTag",
|
||||||
props: {
|
components: { CommandBadge },
|
||||||
cmds: Array,
|
|
||||||
isGrayColor: Boolean,
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
commandTypes,
|
commandTypes,
|
||||||
|
textDisplayRules,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
props: {
|
||||||
getBadgeClass(cmd) {
|
cmds: Array,
|
||||||
if (this.isGrayColor) {
|
isGrayColor: Boolean,
|
||||||
return this.$q.dark.isActive ? "text-grey-6 bg-grey-9" : "bg-grey-4";
|
// 卡片样式代码:1-mini, 2-dense, 3-normal
|
||||||
|
cardStyleCode: Number,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 根据卡片样式确定最大显示标签数
|
||||||
|
maxTagNums() {
|
||||||
|
return { 1: 2, 2: 3, 3: 3 }[this.cardStyleCode];
|
||||||
|
},
|
||||||
|
// 根据卡片样式确定容器最大宽度
|
||||||
|
maxContainerWidth() {
|
||||||
|
return { 1: 120, 2: 180, 3: 260 }[this.cardStyleCode];
|
||||||
|
},
|
||||||
|
// 计算单个标签的最大宽度
|
||||||
|
maxTagWidth() {
|
||||||
|
// 计算所有额外标签(溢出标记和window类型的额外应用数量标签)的总宽度
|
||||||
|
let extraBadgesWidth = 0;
|
||||||
|
|
||||||
|
// 先确定实际显示的标签数量
|
||||||
|
const visibleTagCount = Math.min(this.cmds.length, this.maxTagNums);
|
||||||
|
|
||||||
|
// 溢出标记的宽度
|
||||||
|
if (this.hasOverflow) {
|
||||||
|
extraBadgesWidth += 31;
|
||||||
}
|
}
|
||||||
const color = this.commandTypes[cmd.type || "key"].color;
|
|
||||||
return "bg-" + color + (this.$q.dark.isActive ? "-10" : "-4");
|
// window类型的额外应用数量标签的宽度
|
||||||
|
for (let i = 0; i < visibleTagCount; i++) {
|
||||||
|
const cmd = this.cmds[i];
|
||||||
|
if (cmd.type === 'window' && cmd.match.app.length > 1) {
|
||||||
|
extraBadgesWidth += 31; // 额外应用数量标签的宽度
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算每个标签的可用宽度:(总宽度 - 额外标签总宽度 - 间距) / 标签数量
|
||||||
|
return (
|
||||||
|
(this.maxContainerWidth -
|
||||||
|
extraBadgesWidth -
|
||||||
|
(visibleTagCount - 1) * 3) /
|
||||||
|
visibleTagCount
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// 是否有标签溢出(标签总数超过最大显示数量)
|
||||||
|
hasOverflow() {
|
||||||
|
return this.cmds.length > this.maxTagNums;
|
||||||
|
},
|
||||||
|
// 获取可见的标签列表
|
||||||
|
visibleCommands() {
|
||||||
|
return this.cmds.slice(0, this.maxTagNums);
|
||||||
|
},
|
||||||
|
// 获取溢出的标签列表
|
||||||
|
overflowCommands() {
|
||||||
|
return this.cmds.slice(this.maxTagNums);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 获取标签的图标名称
|
||||||
|
getIconName(cmd) {
|
||||||
|
return typeof cmd === "string"
|
||||||
|
? this.commandTypes.key.icon
|
||||||
|
: this.commandTypes[cmd.type].icon;
|
||||||
|
},
|
||||||
|
// 获取标签的显示文本
|
||||||
|
getDisplayText(cmd, isTooltip = true) {
|
||||||
|
const type = typeof cmd === "string" ? "string" : cmd.type;
|
||||||
|
return this.textDisplayRules[type]?.(cmd, isTooltip) ?? "";
|
||||||
|
},
|
||||||
|
// 计算文本宽度(考虑中英文字符宽度差异)
|
||||||
|
getTextWidth(text) {
|
||||||
|
let width = 0;
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
// 中文字符和点占两个字符宽度
|
||||||
|
if (/[\u4e00-\u9fa5]|[\u3000-\u303f\uff00-\uff60]/.test(text[i])) {
|
||||||
|
width += 2;
|
||||||
|
} else {
|
||||||
|
width += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return width * 6; // 假设每个英文字符宽度为6px
|
||||||
|
},
|
||||||
|
// 判断是否需要显示tooltip(文本是否溢出)
|
||||||
|
needTooltip(cmd) {
|
||||||
|
let text;
|
||||||
|
// 获取需要显示的文本
|
||||||
|
if (typeof cmd === "string") {
|
||||||
|
text = cmd;
|
||||||
|
} else if (cmd.type === "window") {
|
||||||
|
text = cmd.match.app[0];
|
||||||
|
} else if (cmd.type === "regex") {
|
||||||
|
text = cmd.match;
|
||||||
|
} else if (cmd.type === "files") {
|
||||||
|
text = this.getDisplayText(cmd, false);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算文本是否超出可用宽度
|
||||||
|
const iconWidth = 20; // 图标宽度
|
||||||
|
const paddingWidth = 16; // 内边距宽度
|
||||||
|
const availableWidth = this.maxTagWidth - iconWidth - paddingWidth;
|
||||||
|
|
||||||
|
return this.getTextWidth(text) > availableWidth;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -88,36 +197,7 @@ export default {
|
|||||||
height: 23px;
|
height: 23px;
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
gap: 2px;
|
align-items: center;
|
||||||
}
|
gap: 3px;
|
||||||
|
|
||||||
.q-badge {
|
|
||||||
font-size: 12px;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tags-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 4px;
|
|
||||||
width: fit-content;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-wrapper {
|
|
||||||
display: inline-flex;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
<CommandTypeTag
|
<CommandTypeTag
|
||||||
:cmds="commandInfo.features.cmds"
|
:cmds="commandInfo.features.cmds"
|
||||||
:isGrayColor="!isPlatformSupported || !isActivated"
|
:isGrayColor="!isPlatformSupported || !isActivated"
|
||||||
|
:cardStyleCode="cardStyleCode"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -63,6 +64,7 @@ export default {
|
|||||||
isActivated: Boolean,
|
isActivated: Boolean,
|
||||||
isPlatformSupported: Boolean,
|
isPlatformSupported: Boolean,
|
||||||
isHovered: Boolean,
|
isHovered: Boolean,
|
||||||
|
cardStyleCode: Number,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
<CommandTypeTag
|
<CommandTypeTag
|
||||||
:cmds="commandInfo.features.cmds"
|
:cmds="commandInfo.features.cmds"
|
||||||
:isGrayColor="!isPlatformSupported || !isActivated"
|
:isGrayColor="!isPlatformSupported || !isActivated"
|
||||||
|
:cardStyleCode="cardStyleCode"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -58,6 +59,7 @@ export default {
|
|||||||
isActivated: Boolean,
|
isActivated: Boolean,
|
||||||
isPlatformSupported: Boolean,
|
isPlatformSupported: Boolean,
|
||||||
isHovered: Boolean,
|
isHovered: Boolean,
|
||||||
|
cardStyleCode: Number,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -24,7 +24,10 @@
|
|||||||
|
|
||||||
<!-- 匹配模式 -->
|
<!-- 匹配模式 -->
|
||||||
<div class="row justify-center w-100">
|
<div class="row justify-center w-100">
|
||||||
<CommandTypeTag :cmds="commandInfo.features.cmds" />
|
<CommandTypeTag
|
||||||
|
:cmds="commandInfo.features.cmds"
|
||||||
|
:cardStyleCode="cardStyleCode"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -32,7 +35,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import CommandTypeTag from "../CommandTypeTag.vue";
|
import CommandTypeTag from "../CommandTypeTag.vue";
|
||||||
import platformTypes from "js/options/platformTypes.js";
|
import platformTypes from "js/options/platformTypes.js";
|
||||||
import { computed } from "vue";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MiniLayout",
|
name: "MiniLayout",
|
||||||
@ -40,6 +42,7 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
commandInfo: Object,
|
commandInfo: Object,
|
||||||
isHovered: Boolean,
|
isHovered: Boolean,
|
||||||
|
cardStyleCode: Number,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
return {
|
return {
|
||||||
@ -55,7 +58,7 @@ export default {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 16px;
|
padding: 16px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.w-100 {
|
.w-100 {
|
||||||
@ -66,9 +69,9 @@ export default {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
max-width: 90%;
|
/* max-width: 90%; */
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0 8px;
|
/* padding: 0 8px; */
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
9
src/js/options/textDisplayRules.js
Normal file
9
src/js/options/textDisplayRules.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export const textDisplayRules = {
|
||||||
|
string: (cmd) => cmd,
|
||||||
|
window: (cmd, isTooltip) => isTooltip ? cmd.match.app.join(", ") : cmd.match.app[0],
|
||||||
|
files: (cmd) => cmd.match || (cmd.fileType === "directory" ? "所有文件夹" : "所有文件"),
|
||||||
|
regex: (cmd) => cmd.match,
|
||||||
|
over: () => "所有文本",
|
||||||
|
img: () => "图片",
|
||||||
|
add: (cmd) => cmd.count,
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user