mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-08 06:16:27 +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"
|
||||
:isHovered="isHovered"
|
||||
:style="iconHaloStyle"
|
||||
:cardStyleCode="cardStyleCode"
|
||||
/>
|
||||
</q-card>
|
||||
</template>
|
||||
|
@ -1,83 +1,192 @@
|
||||
<template>
|
||||
<div class="matchTypesBox">
|
||||
<div v-for="cmd in cmds" :key="cmd">
|
||||
<span v-if="typeof cmd === 'string'">
|
||||
<q-badge rounded :class="getBadgeClass(cmd)">
|
||||
<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="getBadgeClass(cmd)">
|
||||
<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 class="matchTypesBox" :style="{ '--max-tag-width': maxTagWidth + 'px' }">
|
||||
<!-- 遍历显示可见的标签 -->
|
||||
<template v-for="(cmd, index) in visibleCommands" :key="index">
|
||||
<CommandBadge
|
||||
:cmd="cmd"
|
||||
:isGrayColor="isGrayColor"
|
||||
:showTooltip="needTooltip(cmd)"
|
||||
>
|
||||
<!-- 窗口类型标签的额外应用列表提示 -->
|
||||
<template
|
||||
v-if="cmd.type === 'window' && cmd.match.app.length > 1"
|
||||
#tooltip
|
||||
>
|
||||
<div
|
||||
class="text-subtitle2 row items-center"
|
||||
v-for="app in cmd.match.app.slice(1)"
|
||||
:key="app"
|
||||
>
|
||||
<q-icon class="q-mr-xs" :name="commandTypes.window.icon" />
|
||||
<span>{{ app }}</span>
|
||||
</div>
|
||||
</q-tooltip>
|
||||
</span>
|
||||
<span v-else-if="cmd.type === 'files'">
|
||||
<q-badge rounded :class="getBadgeClass(cmd)">
|
||||
<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 || "所有文件" }}
|
||||
</template>
|
||||
</CommandBadge>
|
||||
|
||||
<!-- 窗口类型的额外应用数量标签 -->
|
||||
<CommandBadge
|
||||
v-if="cmd.type === 'window' && cmd.match.app.length > 1"
|
||||
:cmd="{ type: 'add', count: cmd.match.app.length - 1 }"
|
||||
:isGrayColor="isGrayColor"
|
||||
:iconStyle="{ width: '8px' }"
|
||||
: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>
|
||||
</q-tooltip>
|
||||
</span>
|
||||
<span v-else-if="cmd.type === 'regex'">
|
||||
<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>
|
||||
</template>
|
||||
</template>
|
||||
</CommandBadge>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CommandBadge from "./CommandBadge.vue";
|
||||
import commandTypes from "js/options/commandTypes.js";
|
||||
import { textDisplayRules } from "js/options/textDisplayRules.js";
|
||||
|
||||
export default {
|
||||
name: "CommandTypeTag",
|
||||
props: {
|
||||
cmds: Array,
|
||||
isGrayColor: Boolean,
|
||||
},
|
||||
components: { CommandBadge },
|
||||
data() {
|
||||
return {
|
||||
commandTypes,
|
||||
textDisplayRules,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getBadgeClass(cmd) {
|
||||
if (this.isGrayColor) {
|
||||
return this.$q.dark.isActive ? "text-grey-6 bg-grey-9" : "bg-grey-4";
|
||||
props: {
|
||||
cmds: Array,
|
||||
isGrayColor: Boolean,
|
||||
// 卡片样式代码: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;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.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;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
</style>
|
||||
|
@ -28,6 +28,7 @@
|
||||
<CommandTypeTag
|
||||
:cmds="commandInfo.features.cmds"
|
||||
:isGrayColor="!isPlatformSupported || !isActivated"
|
||||
:cardStyleCode="cardStyleCode"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -63,6 +64,7 @@ export default {
|
||||
isActivated: Boolean,
|
||||
isPlatformSupported: Boolean,
|
||||
isHovered: Boolean,
|
||||
cardStyleCode: Number,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -23,6 +23,7 @@
|
||||
<CommandTypeTag
|
||||
:cmds="commandInfo.features.cmds"
|
||||
:isGrayColor="!isPlatformSupported || !isActivated"
|
||||
:cardStyleCode="cardStyleCode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -58,6 +59,7 @@ export default {
|
||||
isActivated: Boolean,
|
||||
isPlatformSupported: Boolean,
|
||||
isHovered: Boolean,
|
||||
cardStyleCode: Number,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -24,7 +24,10 @@
|
||||
|
||||
<!-- 匹配模式 -->
|
||||
<div class="row justify-center w-100">
|
||||
<CommandTypeTag :cmds="commandInfo.features.cmds" />
|
||||
<CommandTypeTag
|
||||
:cmds="commandInfo.features.cmds"
|
||||
:cardStyleCode="cardStyleCode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -32,7 +35,6 @@
|
||||
<script>
|
||||
import CommandTypeTag from "../CommandTypeTag.vue";
|
||||
import platformTypes from "js/options/platformTypes.js";
|
||||
import { computed } from "vue";
|
||||
|
||||
export default {
|
||||
name: "MiniLayout",
|
||||
@ -40,6 +42,7 @@ export default {
|
||||
props: {
|
||||
commandInfo: Object,
|
||||
isHovered: Boolean,
|
||||
cardStyleCode: Number,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
@ -55,7 +58,7 @@ export default {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
padding: 16px 8px;
|
||||
}
|
||||
|
||||
.w-100 {
|
||||
@ -66,9 +69,9 @@ export default {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 90%;
|
||||
/* max-width: 90%; */
|
||||
margin: 0 auto;
|
||||
padding: 0 8px;
|
||||
/* padding: 0 8px; */
|
||||
font-size: 14px;
|
||||
}
|
||||
</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