完善卡片上标签显示逻辑的算法

This commit is contained in:
fofolee 2024-12-21 19:38:17 +08:00
parent b230088a3a
commit 5c02214063
7 changed files with 273 additions and 99 deletions

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

View File

@ -11,6 +11,7 @@
:isPlatformSupported="isPlatformSupported"
:isHovered="isHovered"
:style="iconHaloStyle"
:cardStyleCode="cardStyleCode"
/>
</q-card>
</template>

View File

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

View File

@ -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 {

View File

@ -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 {

View File

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

View 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,
};