mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-08 22:51:25 +08:00
调整子流程标签栏样式,支持重命名
This commit is contained in:
parent
e0208eb119
commit
db0ce31c7d
@ -3,7 +3,7 @@
|
|||||||
<!-- 主体内容 -->
|
<!-- 主体内容 -->
|
||||||
<div class="composer-body row no-wrap">
|
<div class="composer-body row no-wrap">
|
||||||
<!-- 左侧命令列表 -->
|
<!-- 左侧命令列表 -->
|
||||||
<div class="col-3 command-section">
|
<div class="command-section" style="width: 180px">
|
||||||
<ComposerList :commands="availableCommands" @add-command="addCommand" />
|
<ComposerList :commands="availableCommands" @add-command="addCommand" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.body--dark .command-section {
|
.body--dark .command-section {
|
||||||
background: #1d1d1d;
|
background: #232323;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +230,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.body--dark .composer-list {
|
.body--dark .composer-list {
|
||||||
background-color: rgba(32, 32, 32, 0.8);
|
background-color: #232323;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-item {
|
.category-item {
|
||||||
|
@ -1,70 +1,71 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flow-tabs">
|
<div class="flow-tabs">
|
||||||
<div class="tabs-header">
|
<div class="tabs-header">
|
||||||
<div class="header-content">
|
<div class="tabs-container">
|
||||||
<div class="tabs-container">
|
<!-- main 作为固定按钮 -->
|
||||||
<!-- main 作为固定按钮 -->
|
<q-btn
|
||||||
<q-btn
|
flat
|
||||||
flat
|
dense
|
||||||
dense
|
label="主流程"
|
||||||
:color="activeTab === 'main' ? 'primary' : 'grey'"
|
class="main-btn"
|
||||||
label="main"
|
:class="{ 'main-btn-active': activeTab === 'main' }"
|
||||||
class="main-btn"
|
@click="activeTab = 'main'"
|
||||||
@click="activeTab = 'main'"
|
/>
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 其他流程标签可滚动 -->
|
<!-- 其他流程标签可滚动 -->
|
||||||
<q-tabs
|
<q-tabs
|
||||||
v-model="activeTab"
|
v-model="activeTab"
|
||||||
dense
|
dense
|
||||||
class="text-grey"
|
class="text-grey"
|
||||||
active-color="primary"
|
active-color="primary"
|
||||||
indicator-color="primary"
|
indicator-color="primary"
|
||||||
align="left"
|
align="left"
|
||||||
narrow-indicator
|
narrow-indicator
|
||||||
>
|
outside-arrows
|
||||||
<template v-for="flow in nonMainFlows" :key="flow.id">
|
>
|
||||||
<q-tab :name="flow.id" class="flow-tab">
|
<template v-for="flow in nonMainFlows" :key="flow.id">
|
||||||
<div class="flow-tab-content">
|
<q-tab :name="flow.id" class="flow-tab">
|
||||||
|
<div class="flow-tab-content">
|
||||||
|
<template v-if="flow.isEditing">
|
||||||
<q-input
|
<q-input
|
||||||
v-model="flow.name"
|
v-model="flow.label"
|
||||||
dense
|
dense
|
||||||
borderless
|
borderless
|
||||||
class="flow-name-input"
|
class="flow-name-input"
|
||||||
@keydown.space.prevent
|
@keydown.space.prevent
|
||||||
@blur="validateFlowName(flow)"
|
@blur="finishEdit(flow)"
|
||||||
|
@keyup.enter="finishEdit(flow)"
|
||||||
|
ref="inputRefs"
|
||||||
/>
|
/>
|
||||||
<q-btn
|
</template>
|
||||||
flat
|
<template v-else>
|
||||||
dense
|
<span class="flow-name-text" @dblclick="startEdit(flow)">{{
|
||||||
round
|
flow.label
|
||||||
icon="close"
|
}}</span>
|
||||||
size="xs"
|
</template>
|
||||||
@click.stop="removeFlow(flow)"
|
<q-btn
|
||||||
/>
|
flat
|
||||||
</div>
|
dense
|
||||||
</q-tab>
|
round
|
||||||
</template>
|
icon="close"
|
||||||
</q-tabs>
|
size="xs"
|
||||||
|
@click.stop="removeFlow(flow)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<q-tooltip> 双击修改名称 </q-tooltip>
|
||||||
|
</q-tab>
|
||||||
|
</template>
|
||||||
|
</q-tabs>
|
||||||
|
|
||||||
<q-btn
|
<q-icon dense name="add" class="add-btn" @click="addFlow" />
|
||||||
flat
|
|
||||||
dense
|
|
||||||
round
|
|
||||||
icon="add"
|
|
||||||
size="sm"
|
|
||||||
class="q-ml-sm add-btn"
|
|
||||||
@click="addFlow"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ComposerButtons
|
|
||||||
:generate-code="generateAllFlowCode"
|
|
||||||
:is-all-collapsed="isAllCollapsed"
|
|
||||||
:show-close-button="showCloseButton"
|
|
||||||
@action="handleAction"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ComposerButtons
|
||||||
|
:generate-code="generateAllFlowCode"
|
||||||
|
:is-all-collapsed="isAllCollapsed"
|
||||||
|
:show-close-button="showCloseButton"
|
||||||
|
@action="handleAction"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flow-container">
|
<div class="flow-container">
|
||||||
@ -88,6 +89,7 @@ import ComposerFlow from "./ComposerFlow.vue";
|
|||||||
import ComposerButtons from "./flow/ComposerButtons.vue";
|
import ComposerButtons from "./flow/ComposerButtons.vue";
|
||||||
import { generateCode } from "js/composer/generateCode";
|
import { generateCode } from "js/composer/generateCode";
|
||||||
import { findCommandByValue } from "js/composer/composerConfig";
|
import { findCommandByValue } from "js/composer/composerConfig";
|
||||||
|
import { generateUniqSuffix } from "js/composer/variableManager";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "FlowTabs",
|
name: "FlowTabs",
|
||||||
@ -107,6 +109,7 @@ export default defineComponent({
|
|||||||
{
|
{
|
||||||
id: "main",
|
id: "main",
|
||||||
name: "main",
|
name: "main",
|
||||||
|
label: "主流程",
|
||||||
commands: [],
|
commands: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -120,44 +123,43 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
generateFlowName(baseName = "flow_") {
|
||||||
|
return (
|
||||||
|
baseName +
|
||||||
|
generateUniqSuffix(
|
||||||
|
baseName,
|
||||||
|
this.flows.map((f) => f.name),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
addFlow() {
|
addFlow() {
|
||||||
const id = `flow_${this.$root.getUniqueId()}`;
|
const id = this.$root.getUniqueId();
|
||||||
const name = `flow${this.flows.length}`;
|
const name = this.generateFlowName();
|
||||||
this.flows.push({
|
this.flows.push({
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
|
label: name.replace("flow_", "子流程"),
|
||||||
commands: [],
|
commands: [],
|
||||||
});
|
});
|
||||||
this.activeTab = id;
|
this.activeTab = id;
|
||||||
this.$nextTick(this.updateWidths);
|
|
||||||
},
|
},
|
||||||
removeFlow(flow) {
|
removeFlow(flow) {
|
||||||
const index = this.flows.findIndex((f) => f.id === flow.id);
|
const index = this.flows.findIndex((f) => f.id === flow.id);
|
||||||
if (index > -1 && flow.id !== "main") {
|
if (index > -1 && flow.id !== "main") {
|
||||||
this.flows.splice(index, 1);
|
this.flows.splice(index, 1);
|
||||||
this.activeTab = this.flows[0].id;
|
this.activeTab = this.flows[0].id;
|
||||||
this.$nextTick(this.updateWidths);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
validateFlowName(flow) {
|
|
||||||
if (flow.id === "main") return;
|
|
||||||
// 移除空格并确保名字唯一
|
|
||||||
let newName = flow.name.replace(/\s+/g, "_");
|
|
||||||
let counter = 1;
|
|
||||||
const baseName = newName;
|
|
||||||
|
|
||||||
while (this.flows.some((f) => f.id !== flow.id && f.name === newName)) {
|
|
||||||
newName = `${baseName}_${counter++}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
flow.name = newName;
|
|
||||||
},
|
|
||||||
generateFlowCode(flow) {
|
generateFlowCode(flow) {
|
||||||
return generateCode(flow.commands, flow.name);
|
return generateCode(flow);
|
||||||
},
|
},
|
||||||
generateAllFlowCode() {
|
generateAllFlowCode() {
|
||||||
// 生成所有flow的代码
|
// 生成所有flow的代码
|
||||||
return this.flows.map((flow) => this.generateFlowCode(flow)).join("\n\n");
|
return this.flows
|
||||||
|
.reverse()
|
||||||
|
.map((flow) => this.generateFlowCode(flow))
|
||||||
|
.join("\n\n");
|
||||||
},
|
},
|
||||||
handleFlowAction(type, payload, flow) {
|
handleFlowAction(type, payload, flow) {
|
||||||
if (type === "close") {
|
if (type === "close") {
|
||||||
@ -255,6 +257,21 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
this.isAllCollapsed = false;
|
this.isAllCollapsed = false;
|
||||||
},
|
},
|
||||||
|
startEdit(flow) {
|
||||||
|
flow.isEditing = true;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const input = this.$refs.inputRefs?.[0];
|
||||||
|
if (input) {
|
||||||
|
input.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
finishEdit(flow) {
|
||||||
|
flow.isEditing = false;
|
||||||
|
if (!flow.label) {
|
||||||
|
flow.label = this.generateFlowName().replace("flow_", "子流程");
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -268,15 +285,11 @@ export default defineComponent({
|
|||||||
|
|
||||||
.tabs-header {
|
.tabs-header {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
height: 28px;
|
height: 30px;
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-content {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
height: 100%;
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container {
|
.tabs-container {
|
||||||
@ -285,63 +298,94 @@ export default defineComponent({
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
margin-right: 8px;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 限制在当前组件内的tabs样式 */
|
/* 限制在当前组件内的tabs样式 */
|
||||||
.tabs-container :deep(.q-tabs) {
|
.tabs-container :deep(.q-tabs) {
|
||||||
flex: 1;
|
height: 30px;
|
||||||
height: 28px;
|
min-height: 30px;
|
||||||
min-height: 28px;
|
min-width: 0;
|
||||||
overflow-x: auto;
|
width: auto;
|
||||||
overflow-y: hidden;
|
max-width: 100%;
|
||||||
margin-left: 4px; /* 与 main 按钮保持间距 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 隐藏滚动条 */
|
.body--dark .tabs-container :deep(.q-tabs) {
|
||||||
.tabs-container :deep(.q-tabs)::-webkit-scrollbar {
|
background-color: #232323 !important;
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container :deep(.q-tab) {
|
.tabs-container :deep(.q-tab) {
|
||||||
min-height: 28px;
|
min-height: 30px;
|
||||||
height: 28px;
|
height: 30px;
|
||||||
padding: 0 8px;
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-container
|
||||||
|
:deep(.q-tabs--scrollable.q-tabs__arrows--outside.q-tabs--horizontal) {
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-container :deep(.q-tabs__arrow) {
|
||||||
|
min-width: 0;
|
||||||
|
width: 15px;
|
||||||
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container :deep(.q-tab__content) {
|
.tabs-container :deep(.q-tab__content) {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flow-tab {
|
.flow-tab {
|
||||||
min-width: 80px;
|
min-width: 80px;
|
||||||
|
max-width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flow-tab-content {
|
.flow-tab-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
user-select: none;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flow-name-input {
|
.flow-name-input {
|
||||||
max-width: 100px;
|
max-width: 100%;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flow-name-input :deep(.q-field__native) {
|
.flow-name-input :deep(.q-field__control),
|
||||||
padding: 0;
|
.flow-name-input :deep(.q-field__control *) {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
height: 30px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-name-text {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 0 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 添加按钮样式 */
|
/* 添加按钮样式 */
|
||||||
.tabs-container .q-btn {
|
|
||||||
height: 28px;
|
|
||||||
min-height: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-btn {
|
.add-btn {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
height: 28px;
|
padding: 0 5px;
|
||||||
min-height: 28px;
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-btn:hover {
|
||||||
|
color: var(--q-primary);
|
||||||
|
transform: scale(1.2);
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flow-container {
|
.flow-container {
|
||||||
@ -355,13 +399,19 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.main-btn {
|
.main-btn {
|
||||||
height: 28px;
|
height: 30px;
|
||||||
min-height: 28px;
|
min-height: 30px;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
border-radius: 4px;
|
border-radius: 8px 0 0 0;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-right: 4px;
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
color: var(--q-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-btn-active {
|
||||||
|
color: var(--q-primary);
|
||||||
|
border-bottom: 2px solid var(--q-primary);
|
||||||
|
transition: all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="composer-buttons">
|
<div class="composer-buttons">
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
|
<q-separator vertical />
|
||||||
<q-btn
|
<q-btn
|
||||||
:icon="isAllCollapsed ? 'unfold_more' : 'unfold_less'"
|
:icon="isAllCollapsed ? 'unfold_more' : 'unfold_less'"
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
rounded
|
|
||||||
size="9px"
|
|
||||||
@click="$emit('action', isAllCollapsed ? 'expandAll' : 'collapseAll')"
|
@click="$emit('action', isAllCollapsed ? 'expandAll' : 'collapseAll')"
|
||||||
>
|
>
|
||||||
<q-tooltip>{{ isAllCollapsed ? "展开所有" : "折叠所有" }}</q-tooltip>
|
<q-tooltip>{{ isAllCollapsed ? "展开所有" : "折叠所有" }}</q-tooltip>
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
export function generateCode(commandFlow, functionName = null) {
|
export function generateCode(flow) {
|
||||||
|
const { commands, name, label } = flow;
|
||||||
// 检查是否包含异步函数
|
// 检查是否包含异步函数
|
||||||
const hasAsyncFunction = commandFlow.some((cmd) => cmd.isAsync);
|
const hasAsyncFunction = commands.some((cmd) => cmd.isAsync);
|
||||||
|
|
||||||
let code = [];
|
let code = [];
|
||||||
const funcName = functionName || "run";
|
const funcName = name || "func_" + new Date().getTime();
|
||||||
|
|
||||||
|
code.push(`// ${label}`);
|
||||||
// 生成函数声明
|
// 生成函数声明
|
||||||
code.push(`${hasAsyncFunction ? "async " : ""}function ${funcName}() {`);
|
code.push(`${hasAsyncFunction ? "async " : ""}function ${funcName}() {`);
|
||||||
|
|
||||||
const indent = " ";
|
const indent = " ";
|
||||||
|
|
||||||
commandFlow.forEach((cmd) => {
|
commands.forEach((cmd) => {
|
||||||
// 跳过禁用的命令
|
// 跳过禁用的命令
|
||||||
if (cmd.disabled) return;
|
if (cmd.disabled) return;
|
||||||
if (!cmd.code) return;
|
if (!cmd.code) return;
|
||||||
@ -28,7 +30,7 @@ export function generateCode(commandFlow, functionName = null) {
|
|||||||
code.push("}"); // Close the function
|
code.push("}"); // Close the function
|
||||||
|
|
||||||
// 如果是主函数,则自动执行
|
// 如果是主函数,则自动执行
|
||||||
if (functionName === "main") {
|
if (funcName === "main") {
|
||||||
code.push("\nmain();"); // Call the main function
|
code.push("\nmain();"); // Call the main function
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { validateVariableName } from "js/common/variableValidator";
|
|||||||
* @param {number} varCount - 当前变量数量
|
* @param {number} varCount - 当前变量数量
|
||||||
* @returns {string} 随机后缀
|
* @returns {string} 随机后缀
|
||||||
*/
|
*/
|
||||||
function generateRandomSuffix(varCount) {
|
function generateRandomSuffix(varCount, withPrefix = true) {
|
||||||
// 根据变量数量决定后缀长度
|
// 根据变量数量决定后缀长度
|
||||||
let length = 1; // 默认1位
|
let length = 1; // 默认1位
|
||||||
if (varCount >= 100) {
|
if (varCount >= 100) {
|
||||||
@ -15,7 +15,7 @@ function generateRandomSuffix(varCount) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
"_" +
|
(withPrefix ? "_" : "") +
|
||||||
Math.random()
|
Math.random()
|
||||||
.toString(36)
|
.toString(36)
|
||||||
.substring(2, 2 + length)
|
.substring(2, 2 + length)
|
||||||
@ -66,18 +66,19 @@ function generateValidVarName(baseName, existingVars, currentName = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果变量名重复,添加随机后缀直到不重复
|
// 如果变量名重复,添加随机后缀直到不重复
|
||||||
let finalName = baseName;
|
let finalName = baseName + generateUniqSuffix(baseName, existingVars);
|
||||||
while (existingVars.includes(finalName)) {
|
|
||||||
let suffix;
|
|
||||||
do {
|
|
||||||
suffix = generateRandomSuffix(existingVars.length);
|
|
||||||
} while (existingVars.includes(baseName + suffix));
|
|
||||||
finalName = baseName + suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalName;
|
return finalName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function generateUniqSuffix(baseName, existingVars, withPrefix = true) {
|
||||||
|
let suffix;
|
||||||
|
do {
|
||||||
|
suffix = generateRandomSuffix(existingVars.length, withPrefix);
|
||||||
|
} while (existingVars.includes(baseName + suffix));
|
||||||
|
return suffix;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理变量更新
|
* 处理变量更新
|
||||||
* @param {Object} params - 参数对象
|
* @param {Object} params - 参数对象
|
||||||
|
Loading…
x
Reference in New Issue
Block a user