mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-08 06:16:27 +08:00
869 lines
24 KiB
Vue
869 lines
24 KiB
Vue
<template>
|
||
<div class="config-page-container">
|
||
<div
|
||
class="background-layer"
|
||
:style="{
|
||
background: $q.dark.isActive
|
||
? $root.profile.backgroundImgDark
|
||
? `url('${$root.profile.backgroundImgDark}')`
|
||
: 'none'
|
||
: $root.profile.backgroundImgLight
|
||
? `url('${$root.profile.backgroundImgLight}')`
|
||
: 'none',
|
||
backgroundSize: 'cover',
|
||
backgroundPosition: 'center',
|
||
backgroundRepeat: 'no-repeat',
|
||
backgroundAttachment: 'fixed',
|
||
}"
|
||
></div>
|
||
<!-- 标签栏 -->
|
||
<!-- 面板视图不显示标签栏 -->
|
||
<q-scroll-area
|
||
v-show="commandCardStyle !== 'mini'"
|
||
class="absolute-left tag-bar"
|
||
:thumb-style="{
|
||
width: '2px',
|
||
}"
|
||
:style="{
|
||
width: tabBarWidth,
|
||
zIndex: 1,
|
||
}"
|
||
>
|
||
<q-tabs
|
||
v-model="currentTag"
|
||
vertical
|
||
switch-indicator
|
||
:key="$root.profile.denseTagBar"
|
||
:dense="$root.profile.denseTagBar"
|
||
>
|
||
<!-- 所有标签 -->
|
||
<q-tab
|
||
v-for="tag in allQuickCommandTags"
|
||
:key="tag"
|
||
:name="tag"
|
||
:data-search-result="tag === '搜索结果'"
|
||
:data-active-panel="activatedQuickPanels.includes(tag)"
|
||
>
|
||
{{ tag }}
|
||
<q-tooltip v-if="tag === '未分类'">
|
||
所有没有添加标签的命令都会归在未分类 <br />
|
||
可以在新建命令时在标签一栏选择或直接键入标签名来添加标签
|
||
</q-tooltip>
|
||
</q-tab>
|
||
</q-tabs>
|
||
</q-scroll-area>
|
||
<!-- 面板栏 -->
|
||
<q-tab-panels
|
||
animated
|
||
class="absolute-right"
|
||
:style="{
|
||
bottom: footerBarHeight,
|
||
left: tabBarWidth,
|
||
}"
|
||
v-model="currentTag"
|
||
transition-prev="jump-down"
|
||
transition-next="jump-up"
|
||
>
|
||
<q-tab-panel
|
||
style="padding: 0"
|
||
v-for="tag in allQuickCommandTags"
|
||
:key="tag"
|
||
:name="tag"
|
||
>
|
||
<q-scroll-area
|
||
style="height: 100%"
|
||
:thumb-style="{
|
||
background: 'grey',
|
||
width: '6px',
|
||
opacity: 0.5,
|
||
}"
|
||
>
|
||
<div class="row center q-pa-xs">
|
||
<CommandCard
|
||
v-for="commandInfo in currentTagQuickCommands"
|
||
:key="commandInfo.features.code"
|
||
:commandInfo="commandInfo"
|
||
:isCommandActivated="
|
||
activatedQuickCommandFeatureCodes.includes(
|
||
commandInfo.features.code
|
||
)
|
||
"
|
||
:cardStyle="commandCardStyleSheet[commandCardStyle]"
|
||
@commandChanged="commandChanged"
|
||
:style="{
|
||
width: commandCardStyleSheet[commandCardStyle].width,
|
||
}"
|
||
class="relative-position q-pa-sm"
|
||
></CommandCard>
|
||
</div>
|
||
</q-scroll-area>
|
||
</q-tab-panel>
|
||
</q-tab-panels>
|
||
<!-- 底栏 -->
|
||
<div
|
||
class="absolute-bottom footer-bar"
|
||
:style="{
|
||
height: footerBarHeight,
|
||
left: tabBarWidth,
|
||
}"
|
||
>
|
||
<div class="row">
|
||
<!-- 搜索栏 -->
|
||
<div class="col">
|
||
<q-input
|
||
v-model="commandSearchKeyword"
|
||
debounce="200"
|
||
filled
|
||
dense
|
||
:autofocus="$root.profile.autofocusSearch"
|
||
@update:model-value="updateSearch"
|
||
placeholder="搜索,支持拼音首字母"
|
||
>
|
||
<template v-slot:prepend>
|
||
<q-icon name="search" />
|
||
</template>
|
||
</q-input>
|
||
</div>
|
||
<!-- 按钮组 -->
|
||
<div class="col-auto justify-end flex">
|
||
<q-btn-group>
|
||
<!-- 添加暗色模式切换按钮 -->
|
||
<q-btn
|
||
flat
|
||
v-if="isDev"
|
||
:color="$q.dark.isActive ? 'amber' : 'blue-9'"
|
||
:icon="$q.dark.isActive ? 'dark_mode' : 'light_mode'"
|
||
@click="$q.dark.toggle()"
|
||
>
|
||
<q-tooltip>
|
||
{{ $q.dark.isActive ? "切换到亮色模式" : "切换到暗色模式" }}
|
||
</q-tooltip>
|
||
</q-btn>
|
||
<!-- 切换视图 -->
|
||
<q-btn-toggle
|
||
v-model="commandCardStyle"
|
||
@click="$root.profile.commandCardStyle = commandCardStyle"
|
||
toggle-color="primary"
|
||
flat
|
||
:options="[
|
||
{ slot: 'normal', value: 'normal' },
|
||
{ slot: 'dense', value: 'dense' },
|
||
{ slot: 'mini', value: 'mini' },
|
||
]"
|
||
>
|
||
<template v-slot:normal>
|
||
<q-icon name="dashboard" />
|
||
<q-tooltip>双列视图</q-tooltip>
|
||
</template>
|
||
<template v-slot:dense>
|
||
<q-icon name="apps" />
|
||
<q-tooltip>三列视图</q-tooltip>
|
||
</template>
|
||
<template v-slot:mini>
|
||
<q-icon name="view_comfy" />
|
||
<q-tooltip
|
||
>五列面板视图<br />
|
||
未启用、匹配类型为窗口的命令在此视图下不显示</q-tooltip
|
||
>
|
||
</template>
|
||
</q-btn-toggle>
|
||
<!-- <q-separator vertical /> -->
|
||
<q-btn
|
||
split
|
||
flat
|
||
@click="goShareCenter"
|
||
color="primary"
|
||
label="分享中心"
|
||
icon="groups"
|
||
class="share-btn"
|
||
/>
|
||
<!-- <q-separator vertical /> -->
|
||
<!-- 新建按钮 -->
|
||
<q-btn
|
||
split
|
||
flat
|
||
@click="addNewCommand"
|
||
color="primary"
|
||
label="新建"
|
||
icon="add"
|
||
class="new-btn"
|
||
/>
|
||
<!-- <q-separator vertical /> -->
|
||
<q-btn
|
||
stretch
|
||
color="primary"
|
||
flat
|
||
size="xs"
|
||
id="menuBtn"
|
||
:style="{
|
||
height: footerBarHeight,
|
||
}"
|
||
><q-spinner-bars color="primary" size="1.5em" />
|
||
<!-- 菜单 -->
|
||
<ConfigurationMenu
|
||
:isTagStared="activatedQuickPanels.includes(currentTag)"
|
||
:currentTag="currentTag"
|
||
>
|
||
</ConfigurationMenu>
|
||
</q-btn>
|
||
</q-btn-group>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 命令编辑界面 -->
|
||
<q-dialog
|
||
v-model="isCommandEditorShow"
|
||
persistent
|
||
maximized
|
||
:transition-show="fromNewCommand ? '' : 'slide-up'"
|
||
transition-hide="slide-down"
|
||
style="overflow: hidden"
|
||
>
|
||
<CommandEditor
|
||
ref="commandEditor"
|
||
:action="commandEditorAction"
|
||
@editorEvent="editorEvent"
|
||
></CommandEditor>
|
||
</q-dialog>
|
||
<CommandRunResult
|
||
:action="{ type: 'config' }"
|
||
ref="result"
|
||
></CommandRunResult>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { defineAsyncComponent } from "vue";
|
||
import quickcommandParser from "js/common/quickcommandParser.js";
|
||
import importAll from "js/common/importAll.js";
|
||
import changeLog from "js/options/changeLog.js";
|
||
import pinyinMatch from "pinyin-match";
|
||
import CommandCard from "components/CommandCard";
|
||
import CommandEditor from "components/CommandEditor";
|
||
import ConfigurationMenu from "components/menu";
|
||
const CommandRunResult = defineAsyncComponent(() =>
|
||
import("components/CommandRunResult.vue")
|
||
);
|
||
// Performance Rendering > 300ms
|
||
|
||
// 默认命令
|
||
let defaultCommands = importAll(require.context("../json/", false, /\.json$/));
|
||
|
||
export default {
|
||
components: {
|
||
CommandCard,
|
||
CommandEditor,
|
||
ConfigurationMenu,
|
||
CommandRunResult,
|
||
},
|
||
data() {
|
||
return {
|
||
currentTag: "默认",
|
||
lastTag: "",
|
||
activatedQuickCommandFeatureCodes: [],
|
||
activatedQuickPanels: [],
|
||
allQuickCommands: {},
|
||
allQuickCommandTags: [],
|
||
commandSearchKeyword: "",
|
||
isCommandEditorShow: false,
|
||
commandEditorAction: {},
|
||
footerBarHeight: "40px",
|
||
commandCardStyle: this.$root.profile.commandCardStyle,
|
||
isDev: window.utools.isDev(),
|
||
commandCardStyleSheet: {
|
||
mini: {
|
||
width: "20%",
|
||
code: 1,
|
||
},
|
||
dense: {
|
||
width: "33%",
|
||
code: 2,
|
||
},
|
||
normal: {
|
||
width: "50%",
|
||
code: 3,
|
||
},
|
||
large: {
|
||
width: "100%",
|
||
code: 4,
|
||
},
|
||
},
|
||
};
|
||
},
|
||
computed: {
|
||
// 当前标签下的所有快捷命令
|
||
currentTagQuickCommands() {
|
||
let commands = Object.values(_.cloneDeep(this.allQuickCommands));
|
||
switch (this.currentTag) {
|
||
case "未分类":
|
||
return commands.filter((cmd) => !cmd.tags || cmd.tags.length === 0);
|
||
// case "来自分享":
|
||
// return commands.filter((cmd) => cmd.fromShare);
|
||
case "搜索结果":
|
||
if (this.commandSearchKeyword?.length < 2) return;
|
||
let searchResult = [];
|
||
commands.forEach((cmd) => {
|
||
// 拼音搜索
|
||
let explain = cmd.features.explain;
|
||
let matchedWordPositions = pinyinMatch.match(
|
||
explain,
|
||
this.commandSearchKeyword
|
||
);
|
||
if (!matchedWordPositions) return;
|
||
let matchedWords = explain.slice(
|
||
matchedWordPositions[0],
|
||
matchedWordPositions[1] + 1
|
||
);
|
||
// 高亮
|
||
cmd.features.explain = explain.replace(
|
||
matchedWords,
|
||
`<strong style="color:#ed6237">${matchedWords}</strong>`
|
||
);
|
||
searchResult.push(cmd);
|
||
});
|
||
return searchResult;
|
||
default:
|
||
return commands.filter((cmd) => cmd.tags?.includes(this.currentTag));
|
||
}
|
||
},
|
||
// 标签栏宽度
|
||
tabBarWidth() {
|
||
return this.commandCardStyle === "mini" ? "0" : "80px";
|
||
},
|
||
fromNewCommand() {
|
||
return this.$route.name === "newcommand";
|
||
},
|
||
fromImportCommand() {
|
||
return this.$route.name === "importcommand";
|
||
},
|
||
},
|
||
mounted() {
|
||
this.initPage();
|
||
},
|
||
methods: {
|
||
// 初始化
|
||
initPage() {
|
||
// newcommand 直接新建命令
|
||
if (this.fromNewCommand) this.addNewCommand();
|
||
// importcommand 导入命令
|
||
else if (this.fromImportCommand)
|
||
this.importCommand(this.$root.enterData.payload);
|
||
this.$router.push("/configuration");
|
||
if (this.$route.params.tags) {
|
||
this.changeCurrentTag(window.hexDecode(this.$route.params.tags));
|
||
this.commandCardStyle = "mini";
|
||
}
|
||
this.showChangeLog();
|
||
// 异步读取
|
||
setTimeout(this.getActivatedFeatures, 0);
|
||
setTimeout(this.getAllQuickCommands, 0);
|
||
},
|
||
// 获取所有已启用的命令的 features 以及面板名称
|
||
getActivatedFeatures() {
|
||
let features = utools.getFeatures();
|
||
let currentFts = [];
|
||
let quickpanels = [];
|
||
features.forEach((x) =>
|
||
x.code.slice(0, 6) == "panel_"
|
||
? quickpanels.push(window.hexDecode(x.code.slice(6)))
|
||
: currentFts.push(x)
|
||
);
|
||
this.activatedQuickCommandFeatureCodes = currentFts.map((f) => f.code);
|
||
// 已启用的面板
|
||
this.activatedQuickPanels = quickpanels;
|
||
},
|
||
// 获取所有的命令(导出的格式)
|
||
getAllQuickCommands() {
|
||
this.allQuickCommands = _.cloneDeep(defaultCommands);
|
||
this.$root.utools.getAll("qc_").forEach((x) => {
|
||
if (x.data.features.code.includes("default_")) return;
|
||
this.allQuickCommands[x.data.features.code] = x.data;
|
||
});
|
||
this.getAllQuickCommandTags();
|
||
},
|
||
getAllQuickCommandTags() {
|
||
this.allQuickCommandTags = _.union(
|
||
...Object.values(this.allQuickCommands).map((x) => x.tags)
|
||
)
|
||
.concat([
|
||
"未分类",
|
||
// "来自分享"
|
||
])
|
||
.filter((x) => x);
|
||
},
|
||
// 监听命令变更事件
|
||
commandChanged(event) {
|
||
switch (event.type) {
|
||
case "remove":
|
||
this.removeCommand(event.data);
|
||
return;
|
||
case "enable":
|
||
this.enableCommand(event.data);
|
||
return;
|
||
case "disable":
|
||
this.disableCommand(event.data);
|
||
return;
|
||
case "edit":
|
||
this.editCommand(event.data);
|
||
return;
|
||
case "run":
|
||
this.runCommand(event.data);
|
||
return;
|
||
default:
|
||
return;
|
||
}
|
||
},
|
||
runCommand(code) {
|
||
this.$refs.result.runCurrentCommand(
|
||
_.cloneDeep(this.allQuickCommands[code])
|
||
);
|
||
},
|
||
// 启用命令
|
||
enableCommand(code) {
|
||
this.$root.utools.whole.setFeature(
|
||
_.cloneDeep(this.allQuickCommands[code].features)
|
||
);
|
||
this.activatedQuickCommandFeatureCodes.push(code);
|
||
},
|
||
// 禁用命令
|
||
disableCommand(code) {
|
||
this.$root.utools.whole.removeFeature(code);
|
||
this.activatedQuickCommandFeatureCodes =
|
||
this.activatedQuickCommandFeatureCodes.filter((x) => x !== code);
|
||
},
|
||
// 删除命令
|
||
removeCommand(code) {
|
||
utools.copyText(JSON.stringify(this.allQuickCommands[code], null, 4));
|
||
delete this.allQuickCommands[code];
|
||
this.$root.utools.delDB("qc_" + code);
|
||
this.removeCommandFromHistory(code);
|
||
this.disableCommand(code);
|
||
this.getAllQuickCommandTags();
|
||
if (!this.allQuickCommandTags.includes(this.currentTag))
|
||
this.changeCurrentTag("默认");
|
||
quickcommand.showMessageBox(
|
||
"删除成功,为防止误操作,已将删除的命令复制到剪贴板",
|
||
"success",
|
||
1000,
|
||
"bottom-right"
|
||
);
|
||
},
|
||
removeCommandFromHistory(code) {
|
||
for (let i = 0; i < localStorage.length; i++) {
|
||
const key = localStorage.key(i);
|
||
if (key.startsWith("editor_history_" + code)) {
|
||
localStorage.removeItem(key);
|
||
}
|
||
}
|
||
},
|
||
// 编辑命令
|
||
editCommand(command) {
|
||
// 即可传入 code,也可直接传入 command
|
||
if (typeof command === "string") command = this.allQuickCommands[command];
|
||
this.commandEditorAction = {
|
||
type: "edit",
|
||
data: _.cloneDeep(command),
|
||
};
|
||
this.isCommandEditorShow = true;
|
||
},
|
||
// 是否为默认命令
|
||
isDefaultCommand(code) {
|
||
return code.slice(0, 8) === "default_";
|
||
},
|
||
// 导入命令
|
||
async importCommand(quickCommandInfo) {
|
||
if (!quickCommandInfo)
|
||
return quickcommand.showMessageBox("导入未完成!", "warning");
|
||
let parsedData = await quickcommandParser(quickCommandInfo);
|
||
if (!parsedData) return quickcommand.showMessageBox("格式错误", "error");
|
||
// 单个命令导入
|
||
let dataToPushed = {};
|
||
if (parsedData.single) {
|
||
if (this.isDefaultCommand(parsedData.qc.features.code))
|
||
return quickcommand.showMessageBox("默认命令不能导入!", "error");
|
||
dataToPushed[parsedData.qc.features.code] = parsedData.qc;
|
||
// 多个
|
||
} else {
|
||
dataToPushed = parsedData.qc;
|
||
}
|
||
for (var code of Object.keys(dataToPushed)) {
|
||
if (this.isDefaultCommand(code)) continue;
|
||
this.$root.utools.putDB(dataToPushed[code], "qc_" + code);
|
||
}
|
||
Object.assign(this.allQuickCommands, dataToPushed);
|
||
this.getAllQuickCommandTags();
|
||
quickcommand.showMessageBox("导入成功!");
|
||
this.locateToCommand(parsedData.qc.tags, parsedData.qc.features?.code);
|
||
},
|
||
// 定位命令, 包含changeCurrentTag
|
||
locateToCommand(tags = ["默认"], code) {
|
||
this.currentTag = !tags || !tags.length ? "未分类" : tags[0];
|
||
if (!code) return;
|
||
// 等待 dom 渲染
|
||
this.$nextTick(() => {
|
||
let el = document.getElementById(code);
|
||
el.scrollIntoViewIfNeeded();
|
||
el.querySelector(".q-card").style.boxShadow =
|
||
"0px 1px var(--q-primary)";
|
||
setTimeout(() => {
|
||
el.querySelector(".q-card").style.boxShadow = "";
|
||
}, 5000);
|
||
// 跳转标签
|
||
document
|
||
.querySelector(".q-tab--active")
|
||
.scrollIntoView({ behavior: "smooth" });
|
||
});
|
||
},
|
||
// 修改并定位当前标签事件
|
||
changeCurrentTag(tagName) {
|
||
this.currentTag = tagName;
|
||
this.$nextTick(() => {
|
||
document
|
||
.querySelector(".q-tab--active")
|
||
.scrollIntoView({ behavior: "smooth" });
|
||
});
|
||
},
|
||
// 全部导出
|
||
exportAllCommands(saveAsFile = true) {
|
||
let options = {
|
||
title: "选择保存位置",
|
||
defaultPath: "quickCommand",
|
||
filters: [
|
||
{
|
||
name: "json",
|
||
extensions: ["json"],
|
||
},
|
||
],
|
||
};
|
||
let commandsToExport = _.cloneDeep(this.allQuickCommands);
|
||
// 不导出默认命令
|
||
Object.keys(commandsToExport).forEach((code) => {
|
||
if (this.isDefaultCommand(code)) delete commandsToExport[code];
|
||
});
|
||
let stringifyCommands = JSON.stringify(commandsToExport);
|
||
if (saveAsFile) {
|
||
window.saveFile(stringifyCommands, options) &&
|
||
quickcommand.showMessageBox("导出成功!");
|
||
} else {
|
||
utools.copyText(stringifyCommands);
|
||
}
|
||
},
|
||
// 清空
|
||
clearAllCommands() {
|
||
quickcommand
|
||
.showConfirmBox("将会清空所有自定义命令,停用所有实用功能,请确认!")
|
||
.then((isConfirmed) => {
|
||
if (!isConfirmed)
|
||
return quickcommand.showMessageBox("取消操作", "info");
|
||
this.exportAllCommands(false);
|
||
this.$root.utools.delAll("qc_");
|
||
this.clearAllFeatures();
|
||
this.allQuickCommands = _.cloneDeep(defaultCommands);
|
||
this.getAllQuickCommandTags();
|
||
this.changeCurrentTag("默认");
|
||
quickcommand.showMessageBox(
|
||
"清空完毕,为防止误操作,已将所有命令复制到剪贴板,可通过导入命令恢复",
|
||
"success",
|
||
2000,
|
||
"bottom-right"
|
||
);
|
||
});
|
||
},
|
||
// 删除所有 features
|
||
clearAllFeatures() {
|
||
for (var feature of utools.getFeatures()) {
|
||
if (feature.code.slice(0, 8) === "feature_") continue;
|
||
this.$root.utools.whole.removeFeature(feature.code);
|
||
}
|
||
this.activatedQuickCommandFeatureCodes = [];
|
||
},
|
||
// 搜索
|
||
updateSearch() {
|
||
// 记录当前标签页
|
||
let searchTagName = "搜索结果";
|
||
if (this.currentTag !== searchTagName) this.lastTag = this.currentTag;
|
||
if (this.commandSearchKeyword?.length > 1) {
|
||
if (!this.allQuickCommandTags.includes(searchTagName))
|
||
this.allQuickCommandTags.push(searchTagName);
|
||
// 搜索时跳转到搜索结果标签
|
||
this.changeCurrentTag(searchTagName);
|
||
} else {
|
||
// 清空搜索回跳到之前标签
|
||
if (this.allQuickCommandTags.slice(-1)[0] === searchTagName)
|
||
this.allQuickCommandTags.pop();
|
||
if (this.currentTag !== this.lastTag)
|
||
this.changeCurrentTag(this.lastTag);
|
||
}
|
||
},
|
||
// 新建命令
|
||
addNewCommand() {
|
||
this.commandEditorAction = {
|
||
type: "new",
|
||
data: {},
|
||
};
|
||
this.isCommandEditorShow = true;
|
||
},
|
||
saveCommand(command) {
|
||
let code = command.features.code;
|
||
this.allQuickCommands[code] = command;
|
||
//无论禁用还是启用都启用
|
||
if (!this.activatedQuickCommandFeatureCodes.includes(code))
|
||
this.activatedQuickCommandFeatureCodes.push(code);
|
||
// 先删除再添加,强制刷新
|
||
this.$root.utools.whole.removeFeature(code);
|
||
this.$root.utools.whole.setFeature(command.features);
|
||
this.getAllQuickCommandTags();
|
||
this.locateToCommand(command.tags, code);
|
||
},
|
||
editorEvent(event) {
|
||
switch (event.type) {
|
||
case "save":
|
||
this.saveCommand(event.data);
|
||
// this.isCommandEditorShow = false;
|
||
default:
|
||
return;
|
||
}
|
||
},
|
||
showChangeLog() {
|
||
let lastNeedLogEvent = changeLog[changeLog.length - 1];
|
||
let loggedVersion =
|
||
this.$root.utools.whole.dbStorage.getItem("cfg_loggedVersion") ||
|
||
"0.0.0";
|
||
if (loggedVersion < lastNeedLogEvent.version) {
|
||
quickcommand.showConfirmBox(
|
||
'<pre style="white-space: pre-wrap;word-wrap: break-word;">' +
|
||
lastNeedLogEvent.log +
|
||
"</pre>",
|
||
"更新日志",
|
||
true,
|
||
700
|
||
);
|
||
this.$root.utools.whole.dbStorage.setItem(
|
||
"cfg_loggedVersion",
|
||
lastNeedLogEvent.version
|
||
);
|
||
}
|
||
},
|
||
goShareCenter() {
|
||
utools.shellOpenExternal("https://qc.qaz.ink/");
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.config-page-container {
|
||
position: relative;
|
||
height: 100vh;
|
||
width: 100%;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.background-layer {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
z-index: 0;
|
||
}
|
||
|
||
/* 标签栏容器样式 */
|
||
.q-tabs {
|
||
height: 100vh !important;
|
||
background: transparent !important;
|
||
}
|
||
|
||
/* 标签栏和底栏内的按钮悬浮效果 */
|
||
.q-tabs .q-tab:hover,
|
||
.absolute-bottom .q-btn:hover {
|
||
background: rgba(255, 255, 255, 0.1) !important;
|
||
}
|
||
|
||
.body--dark .q-tabs .q-tab:hover,
|
||
.body--dark .absolute-bottom .q-btn:hover {
|
||
background: rgba(255, 255, 255, 0.05) !important;
|
||
}
|
||
|
||
.q-tab {
|
||
word-break: break-all;
|
||
white-space: normal;
|
||
position: relative;
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
color: var(--q-blue-grey) !important;
|
||
opacity: 0.7;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
/* 激活标签样式 */
|
||
.q-tab--active {
|
||
color: var(--q-primary) !important;
|
||
font-weight: 600 !important;
|
||
opacity: 1;
|
||
}
|
||
|
||
.q-tab:hover::after {
|
||
content: "";
|
||
position: absolute;
|
||
left: 0;
|
||
bottom: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: var(--q-primary);
|
||
opacity: 0.1;
|
||
border-radius: 4px;
|
||
transition: opacity 0.3s;
|
||
}
|
||
|
||
/* 特殊标签样式(搜索结果和激活面板) */
|
||
.q-tab[data-search-result="true"],
|
||
.q-tab[data-active-panel="true"] {
|
||
color: #4286de !important;
|
||
font-weight: 600 !important;
|
||
}
|
||
|
||
/* 标签悬浮效果 */
|
||
.q-tab:hover {
|
||
opacity: 0.9;
|
||
transform: translateX(4px);
|
||
}
|
||
|
||
/* 标签内容过渡效果 */
|
||
.q-tab__content {
|
||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
/* 面板过渡效果 */
|
||
.q-tab-panels {
|
||
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||
background: none !important;
|
||
}
|
||
|
||
.q-tab-panel {
|
||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||
opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
/* 暗色模式适配 */
|
||
:deep(.body--dark) .q-tab:hover::after {
|
||
opacity: 0.15;
|
||
}
|
||
|
||
/* 标签滚动区域样式 */
|
||
.q-scroll-area {
|
||
background: transparent;
|
||
}
|
||
|
||
:deep(.body--dark) .q-scroll-area {
|
||
background: transparent;
|
||
}
|
||
|
||
/* 底栏输入框样式 */
|
||
.absolute-bottom .q-field__control {
|
||
background: rgba(255, 255, 255, 0.15) !important;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.body--dark .absolute-bottom .q-field__control {
|
||
background: rgba(0, 0, 0, 0.2) !important;
|
||
}
|
||
|
||
/* 底栏毛玻璃效果 */
|
||
body.glass-effect-menu .absolute-bottom {
|
||
background: rgba(
|
||
255,
|
||
255,
|
||
255,
|
||
calc(0.15 + var(--glass-effect-strength) * 0.01)
|
||
) !important;
|
||
backdrop-filter: blur(calc(var(--glass-effect-strength) * 1px)) !important;
|
||
-webkit-backdrop-filter: blur(
|
||
calc(var(--glass-effect-strength) * 1px)
|
||
) !important;
|
||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
body.body--dark.glass-effect-menu .absolute-bottom {
|
||
background: rgba(
|
||
0,
|
||
0,
|
||
0,
|
||
calc(0.2 + var(--glass-effect-strength) * 0.02)
|
||
) !important;
|
||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||
}
|
||
|
||
/* 调整内容层级 */
|
||
.q-scroll-area,
|
||
.q-tab-panels,
|
||
.absolute-bottom {
|
||
position: absolute;
|
||
z-index: 1;
|
||
}
|
||
|
||
/* 底栏过渡动画 */
|
||
.footer-bar {
|
||
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||
will-change: left;
|
||
}
|
||
|
||
/* 底栏输入框样式 */
|
||
.absolute-bottom .q-field__control {
|
||
background: rgba(255, 255, 255, 0.15) !important;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.body--dark .absolute-bottom .q-field__control {
|
||
background: rgba(0, 0, 0, 0.2) !important;
|
||
}
|
||
|
||
/* 标签栏过渡动画 */
|
||
.tag-bar {
|
||
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||
will-change: width, opacity;
|
||
opacity: 1;
|
||
}
|
||
|
||
/* 标签栏隐藏时的样式 */
|
||
.tag-bar[style*="display: none"] {
|
||
opacity: 0;
|
||
width: 0 !important;
|
||
}
|
||
|
||
/* 面板过渡效果 */
|
||
.q-tab-panels {
|
||
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||
background: none !important;
|
||
}
|
||
|
||
/* 基础按钮动画 */
|
||
.share-btn,
|
||
.new-btn {
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
.share-btn:hover,
|
||
.new-btn:hover {
|
||
transform: translateY(-2px);
|
||
background: rgba(var(--q-primary-rgb), 0.1) !important;
|
||
}
|
||
|
||
/* 搜索框动画 */
|
||
@keyframes searchIconShake {
|
||
0%,
|
||
100% {
|
||
transform: rotate(0deg);
|
||
}
|
||
25% {
|
||
transform: rotate(-10deg);
|
||
}
|
||
75% {
|
||
transform: rotate(10deg);
|
||
}
|
||
}
|
||
|
||
.q-field--focused .q-field__prepend .q-icon {
|
||
animation: searchIconShake 0.8s ease;
|
||
color: var(--q-primary);
|
||
}
|
||
</style>
|