Files
admin-ui/src/views/settings/workflow/component/WorkflowSelector.vue
T
2910410219andClaude dd4e719f55 工作流功能完善:子例程弹窗角色分支与模板名称修复、模板子工作流递归补全、首页工作流名称省略+tooltip
- 引入子例程弹窗:按 isAdmin 分支读取 listFlowTemplateRes/listFlowUserRes,名称兼容 flowTemplateName
- 模板补全弹窗:递归展开子工作流模型节点,一次 PUT 更新整棵树(updateWorkflowFromTemplate)
- 首页工作流胶囊/占位卡片:名称单行省略 + el-tooltip 悬停显示全名

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 18:09:34 +08:00

236 lines
6.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<el-dialog v-model="visible" title="选择要引入的工作流" width="900px" :close-on-click-modal="false" @close="handleClose">
<div class="search-bar">
<el-input v-model="searchParams.keyword" placeholder="搜索工作流名称或描述" clearable @clear="handleSearch">
<template #prefix
><el-icon><Search /></el-icon
></template>
</el-input>
<el-button type="primary" @click="handleSearch">搜索</el-button>
</div>
<div class="workflow-list" v-loading="loading">
<el-empty v-if="!loading && workflowList.length === 0" description="暂无工作流数据" :image-size="100" />
<div v-else class="workflow-grid">
<div
v-for="workflow in workflowList"
:key="workflow.id"
class="workflow-card"
:class="{ selected: selectedWorkflow?.id === workflow.id }"
@click="handleSelectWorkflow(workflow)"
>
<div class="workflow-card-header">
<span class="workflow-badge">我的工作流</span>
<el-icon v-if="selectedWorkflow?.id === workflow.id" class="check-icon" color="#67c23a"><CircleCheck /></el-icon>
</div>
<div class="workflow-card-body">
<h3 class="workflow-name">{{ workflow.flowName || workflow.flowTemplateName || workflow.name || '未命名工作流' }}</h3>
<p class="workflow-desc">{{ workflow.description || '暂无描述' }}</p>
</div>
</div>
</div>
</div>
<div v-if="pagination.total > 0" class="pagination-wrap">
<el-pagination
v-model:current-page="pagination.pageNum"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
layout="total, prev, pager, next"
small
@current-change="handlePageChange"
/>
</div>
<template #footer>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleConfirm" :disabled="!selectedWorkflow">确定</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, watch } from 'vue';
import { Search, CircleCheck } from '@element-plus/icons-vue';
import { getSubFlowWorkflowList, type SubFlowWorkflowItem } from '/@/api/settings/workflow';
interface Props {
modelValue: boolean;
defaultWorkflow?: SubFlowWorkflowItem | null;
}
interface Emits {
(e: 'update:modelValue', value: boolean): void;
(e: 'confirm', workflow: SubFlowWorkflowItem): void;
}
const props = withDefaults(defineProps<Props>(), {
modelValue: false,
defaultWorkflow: null,
});
const emit = defineEmits<Emits>();
const visible = ref(false);
const searchParams = reactive({ keyword: '' });
const pagination = reactive({ pageNum: 1, pageSize: 10, total: 0 });
const workflowList = ref<SubFlowWorkflowItem[]>([]);
const loading = ref(false);
const selectedWorkflow = ref<SubFlowWorkflowItem | null>(null);
watch(
() => props.modelValue,
(val) => {
visible.value = val;
if (val) {
selectedWorkflow.value = props.defaultWorkflow || null;
pagination.pageNum = 1;
fetchWorkflowList();
}
}
);
watch(visible, (val) => {
if (!val) {
emit('update:modelValue', false);
}
});
const fetchWorkflowList = async () => {
loading.value = true;
try {
const params = {
pageNum: pagination.pageNum,
pageSize: pagination.pageSize,
keyword: searchParams.keyword || undefined,
IsOwn: true,
};
const res = await getSubFlowWorkflowList(params);
const data = res.data || {};
// 按登录角色分支读取:isAdmin=true(管理员)工作流在 listFlowTemplateRes,普通用户在 listFlowUserRes
// 兼容扁平 list 结构
const isAdmin = !!data.isAdmin;
const source = isAdmin ? data.listFlowTemplateRes : data.listFlowUserRes;
const list = data.list ?? source?.list ?? [];
workflowList.value = list || [];
pagination.total = data.total ?? source?.total ?? workflowList.value.length;
// 预选项仅为部分信息(切换节点后无 id)时,按名称匹配当前页以高亮
// 名称字段兼容 flowName / flowTemplateName / name 三形态
if (selectedWorkflow.value && !selectedWorkflow.value.id) {
const selName = selectedWorkflow.value.flowName || selectedWorkflow.value.flowTemplateName || selectedWorkflow.value.name;
if (selName) {
const matched = workflowList.value.find((w) => (w.flowName || w.flowTemplateName || w.name) === selName);
if (matched) selectedWorkflow.value = matched;
}
}
} catch {
workflowList.value = [];
pagination.total = 0;
} finally {
loading.value = false;
}
};
const handleSearch = () => {
pagination.pageNum = 1;
fetchWorkflowList();
};
const handlePageChange = () => {
fetchWorkflowList();
};
const handleSelectWorkflow = (workflow: SubFlowWorkflowItem) => {
selectedWorkflow.value = workflow;
};
const handleConfirm = () => {
if (selectedWorkflow.value) {
emit('confirm', selectedWorkflow.value);
handleClose();
}
};
const handleClose = () => {
visible.value = false;
selectedWorkflow.value = null;
};
</script>
<style scoped lang="scss">
.search-bar {
display: flex;
gap: 12px;
margin-bottom: 20px;
}
.workflow-list {
min-height: 300px;
max-height: 400px;
overflow-y: auto;
}
.workflow-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 16px;
}
.workflow-card {
background: #f8fafc;
border-radius: 8px;
padding: 16px;
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid transparent;
}
.workflow-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.workflow-card.selected {
border-color: #67c23a;
background: #f0f9ff;
}
.workflow-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.workflow-badge {
display: inline-block;
padding: 2px 8px;
background: #eff6ff;
color: #3b82f6;
border-radius: 4px;
font-size: 12px;
font-weight: 600;
}
.check-icon {
font-size: 20px;
}
.workflow-card-body {
flex: 1;
}
.workflow-name {
font-size: 16px;
font-weight: 600;
color: #1f2937;
margin: 0 0 8px 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.workflow-desc {
font-size: 13px;
color: #64748b;
line-height: 1.5;
margin: 0;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
min-height: 40px;
}
.pagination-wrap {
display: flex;
justify-content: center;
margin-top: 20px;
}
</style>