|
|
@@ -5,21 +5,22 @@
|
|
|
<el-col :span="4" v-for="(card, idx) in cards" :key="idx">
|
|
|
<div
|
|
|
style="
|
|
|
- background: #fff;
|
|
|
- padding: 12px;
|
|
|
- border-radius: 6px;
|
|
|
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
|
+ background: linear-gradient(135deg, #fff 0%, #fafafa 100%);
|
|
|
+ padding: 16px;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
|
+ border: 1px solid #f0f0f0;
|
|
|
"
|
|
|
>
|
|
|
<div style="font-size: 13px; color: #888">{{ card.title }}</div>
|
|
|
- <div style="font-size: 20px; margin-top: 6px">{{ card.value }}</div>
|
|
|
+ <div style="font-size: 20px; margin-top: 6px; font-weight: 600">{{ card.value }}</div>
|
|
|
</div>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
</el-card>
|
|
|
|
|
|
<el-card style="margin-top: 16px">
|
|
|
- <el-tabs v-model:active-name="activeTab">
|
|
|
+ <el-tabs v-model="activeTab">
|
|
|
<el-tab-pane label="工作流程" name="flows"></el-tab-pane>
|
|
|
<el-tab-pane label="证书" name="certs"></el-tab-pane>
|
|
|
<el-tab-pane label="执行" name="execs"></el-tab-pane>
|
|
|
@@ -27,98 +28,644 @@
|
|
|
<el-tab-pane label="数据表" name="tables"></el-tab-pane>
|
|
|
</el-tabs>
|
|
|
|
|
|
+ <!-- 工作流程和证书 Tab 的搜索和排序 -->
|
|
|
+ <div
|
|
|
+ v-if="activeTab === 'flows' || activeTab === 'certs'"
|
|
|
+ style="display: flex; justify-content: space-between; align-items: center; margin-top: 12px"
|
|
|
+ >
|
|
|
+ <div style="display: flex; gap: 8px; align-items: center">
|
|
|
+ <el-input
|
|
|
+ placeholder="搜索"
|
|
|
+ :prefix-icon="Search"
|
|
|
+ v-model="filter"
|
|
|
+ clearable
|
|
|
+ style="width: 260px"
|
|
|
+ />
|
|
|
+ <el-select v-model="sort" placeholder="排序" style="width: 180px">
|
|
|
+ <el-option label="按更新时间排序" value="updated" />
|
|
|
+ <el-option label="按创建时间排序" value="created" />
|
|
|
+ <el-option label="按名称升序" value="name" />
|
|
|
+ <el-option label="按名称降序" value="name-desc" />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <el-button type="text">筛选</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 执行 Tab 的搜索和筛选 -->
|
|
|
<div
|
|
|
+ v-else-if="activeTab === 'execs'"
|
|
|
style="display: flex; justify-content: space-between; align-items: center; margin-top: 12px"
|
|
|
>
|
|
|
<div style="display: flex; gap: 8px; align-items: center">
|
|
|
- <el-input placeholder="搜索" v-model="filter" clearable style="width: 260px" />
|
|
|
- <el-select v-model="sort" placeholder="Sort" size="small" style="width: 160px">
|
|
|
- <el-option label="Sort by last updated" value="updated" />
|
|
|
- <el-option label="Sort by name" value="name" />
|
|
|
+ <el-input
|
|
|
+ placeholder="搜索"
|
|
|
+ :prefix-icon="Search"
|
|
|
+ v-model="filter"
|
|
|
+ clearable
|
|
|
+ style="width: 260px"
|
|
|
+ />
|
|
|
+ <el-select v-model="sort" placeholder="排序" style="width: 180px">
|
|
|
+ <el-option label="按名称升序" value="name" />
|
|
|
+ <el-option label="按名称降序" value="name-desc" />
|
|
|
</el-select>
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
- <el-button type="text" size="small">筛选</el-button>
|
|
|
+ <el-button type="text">筛选</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 变量 Tab 的搜索和添加按钮 -->
|
|
|
+ <div
|
|
|
+ v-else-if="activeTab === 'vars'"
|
|
|
+ style="display: flex; justify-content: space-between; align-items: center; margin-top: 12px"
|
|
|
+ >
|
|
|
+ <div style="display: flex; gap: 8px; align-items: center">
|
|
|
+ <el-input
|
|
|
+ placeholder="搜索变量......"
|
|
|
+ :prefix-icon="Search"
|
|
|
+ v-model="varFilter"
|
|
|
+ clearable
|
|
|
+ style="width: 260px"
|
|
|
+ />
|
|
|
+ <el-select v-model="varSort" placeholder="按名称排序" style="width: 200px">
|
|
|
+ <el-option label="按名称排序" value="name" />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div style="display: flex; gap: 8px">
|
|
|
+ <el-button type="text">筛选</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div style="margin-top: 12px">
|
|
|
- <el-empty v-if="filtered.length === 0" description="无工作流" />
|
|
|
+ <!-- 工作流程 Tab -->
|
|
|
+ <template v-if="activeTab === 'flows'">
|
|
|
+ <el-empty v-if="getTabData.length === 0" description="无工作流程" />
|
|
|
+ <div v-else>
|
|
|
+ <div v-for="item in pagedData" :key="item.id" style="margin-bottom: 12px">
|
|
|
+ <el-card>
|
|
|
+ <div style="display: flex; justify-content: space-between; align-items: center">
|
|
|
+ <div>
|
|
|
+ <div style="font-weight: 700">{{ item.title }}</div>
|
|
|
+ <div style="color: #999; font-size: 12px">上次更新时间: {{ item.created }}</div>
|
|
|
+ </div>
|
|
|
|
|
|
- <div v-else>
|
|
|
- <div v-for="item in paged" :key="item.id" style="margin-bottom: 12px">
|
|
|
- <el-card>
|
|
|
+ <div style="display: flex; gap: 8px; align-items: center">
|
|
|
+ <el-tag size="small" type="info">个人的</el-tag>
|
|
|
+ <el-dropdown>
|
|
|
+ <span class="el-dropdown-link">•••</span>
|
|
|
+ <template #dropdown>
|
|
|
+ <el-dropdown-menu>
|
|
|
+ <el-dropdown-item>打开</el-dropdown-item>
|
|
|
+ <el-dropdown-item>分享</el-dropdown-item>
|
|
|
+ <el-dropdown-item>复制</el-dropdown-item>
|
|
|
+ <el-dropdown-item>移动</el-dropdown-item>
|
|
|
+ <el-dropdown-item>档案</el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </template>
|
|
|
+ </el-dropdown>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 执行 Tab - 表格形式 -->
|
|
|
+ <template v-else-if="activeTab === 'execs'">
|
|
|
+ <div
|
|
|
+ v-if="getTabData.length === 0"
|
|
|
+ style="text-align: center; padding: 40px; color: #999"
|
|
|
+ >
|
|
|
+ 没有正在进行的执行。
|
|
|
+ </div>
|
|
|
+ <el-table v-else :data="getTabData" style="width: 100%; margin-top: 12px" stripe border>
|
|
|
+ <el-table-column type="selection" width="50" />
|
|
|
+ <el-table-column prop="workflow" label="工作流程">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button type="text" @click="$router.push(`/workflow/${scope.row.id}`)">{{
|
|
|
+ scope.row.workflow
|
|
|
+ }}</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="status" label="地位" width="100">
|
|
|
+ <template #default="scope">
|
|
|
+ <div style="display: flex; align-items: center; gap: 6px">
|
|
|
+ <el-icon style="color: #67c23a" v-if="scope.row.status === '成功'">
|
|
|
+ <svg
|
|
|
+ xmlns="http://www.w3.org/2000/svg"
|
|
|
+ viewBox="0 0 24 24"
|
|
|
+ fill="currentColor"
|
|
|
+ width="1em"
|
|
|
+ height="1em"
|
|
|
+ >
|
|
|
+ <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" />
|
|
|
+ </svg>
|
|
|
+ </el-icon>
|
|
|
+ {{ scope.row.status }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="startTime" label="开始" />
|
|
|
+ <el-table-column prop="duration" label="运行时间" width="100" />
|
|
|
+ <el-table-column prop="executionId" label="执行 ID" width="80" />
|
|
|
+ <el-table-column label="手动执行" width="100" align="center">
|
|
|
+ <template #default>
|
|
|
+ <el-button style="background: #ccc; color: #666; border: none" size="small">手动执行</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="80" align="center">
|
|
|
+ <template #default>
|
|
|
+ <el-dropdown>
|
|
|
+ <span class="el-dropdown-link">⋯</span>
|
|
|
+ <template #dropdown>
|
|
|
+ <el-dropdown-menu>
|
|
|
+ <el-dropdown-item>删除</el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </template>
|
|
|
+ </el-dropdown>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 变量 Tab - 表格或空状态 -->
|
|
|
+ <template v-else-if="activeTab === 'vars'">
|
|
|
+ <div
|
|
|
+ v-if="filteredVariables.length === 0"
|
|
|
+ style="
|
|
|
+ text-align: center;
|
|
|
+ padding: 60px 20px;
|
|
|
+ border: 2px dashed #ddd;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: #fafafa;
|
|
|
+ color: #666;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <div style="font-size: 48px; margin-bottom: 12px">👋</div>
|
|
|
+ <div style="font-size: 16px; margin-bottom: 8px">迄今,我还没设置一个变量</div>
|
|
|
+ <div style="font-size: 13px; color: #999; margin-bottom: 16px">
|
|
|
+ 变量可用于存储可在多个工作流程中轻松引用的数据。
|
|
|
+ </div>
|
|
|
+ <el-button @click="showVarDialog = true">添加第一个变量</el-button>
|
|
|
+ </div>
|
|
|
+ <el-table v-else :data="filteredVariables" style="width: 100%; margin-top: 12px">
|
|
|
+ <el-table-column prop="key" label="钥匙" />
|
|
|
+ <el-table-column prop="value" label="价值" />
|
|
|
+ <el-table-column prop="usage" label="使用语法" width="150">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-tag type="info">{{ scope.row.usage }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="scope" label="范围" width="100">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-tag size="small">{{ scope.row.scope }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="120" align="right">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button text size="small" @click="editVariable(scope.row)">编辑</el-button>
|
|
|
+ <el-button text size="small" type="danger" @click="deleteVariable(scope.row.id)"
|
|
|
+ >删除</el-button
|
|
|
+ >
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <div v-if="filteredVariables.length > 0" style="margin-top: 12px; text-align: right">
|
|
|
+ <span style="color: #999; font-size: 12px">页面大小: </span>
|
|
|
+ <el-select v-model="varPageSize" size="small" style="width: 60px">
|
|
|
+ <el-option label="25" value="25" />
|
|
|
+ <el-option label="50" value="50" />
|
|
|
+ <el-option label="100" value="100" />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 其他 Tab -->
|
|
|
+ <template v-else-if="activeTab === 'tables'">
|
|
|
+ <div
|
|
|
+ v-if="tables.length === 0"
|
|
|
+ style="
|
|
|
+ text-align: center;
|
|
|
+ padding: 60px 20px;
|
|
|
+ border: 2px dashed #ddd;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: #fafafa;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <div style="font-size: 16px; margin-bottom: 8px; color: #333">
|
|
|
+ 您目前还没有任何数据表。
|
|
|
+ </div>
|
|
|
+ <div style="font-size: 13px; color: #999; margin-bottom: 20px">
|
|
|
+ 使用数据表来保存执行结果、在工作流之间共享数据以及跨进程体估指标。
|
|
|
+ </div>
|
|
|
+ <el-button @click="showTableDialog = true">创建数据表</el-button>
|
|
|
+ </div>
|
|
|
+ <div v-else>
|
|
|
+ <div
|
|
|
+ v-for="item in tables"
|
|
|
+ :key="item.id"
|
|
|
+ style="
|
|
|
+ margin-bottom: 12px;
|
|
|
+ padding: 12px;
|
|
|
+ border: 1px solid #f0f0f0;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: #fff;
|
|
|
+ "
|
|
|
+ >
|
|
|
<div style="display: flex; justify-content: space-between; align-items: center">
|
|
|
- <div>
|
|
|
- <div style="font-weight: 700">{{ item.title }}</div>
|
|
|
- <div style="color: #999; font-size: 12px">上次更新时间: {{ item.created }}</div>
|
|
|
+ <div style="display: flex; gap: 12px; align-items: center; flex: 1">
|
|
|
+ <div style="font-size: 20px">📋</div>
|
|
|
+ <div style="flex: 1">
|
|
|
+ <div style="font-weight: 600; margin-bottom: 4px; font-size: 14px">
|
|
|
+ {{ item.name }}
|
|
|
+ </div>
|
|
|
+ <div style="color: #999; font-size: 12px; display: flex; gap: 12px">
|
|
|
+ <span>{{ item.size || '0MB' }}</span>
|
|
|
+ <span>{{ item.rows || '0' }}行</span>
|
|
|
+ <span style="color: #ff6b6b">操作更新</span>
|
|
|
+ <span style="color: #ff6b6b">操作创建</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
- <div style="display: flex; gap: 8px; align-items: center">
|
|
|
- <el-tag size="small">个人的</el-tag>
|
|
|
- <el-button type="primary" size="small">运行</el-button>
|
|
|
+ <div>
|
|
|
<el-dropdown>
|
|
|
<span class="el-dropdown-link">•••</span>
|
|
|
<template #dropdown>
|
|
|
<el-dropdown-menu>
|
|
|
- <el-dropdown-item>编辑</el-dropdown-item>
|
|
|
+ <el-dropdown-item>下载 CSV 文件</el-dropdown-item>
|
|
|
<el-dropdown-item>删除</el-dropdown-item>
|
|
|
</el-dropdown-menu>
|
|
|
</template>
|
|
|
</el-dropdown>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </el-card>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 数据表分页器 -->
|
|
|
+ <div
|
|
|
+ style="
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ align-items: center;
|
|
|
+ margin-top: 16px;
|
|
|
+ gap: 12px;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <span style="color: #666; font-size: 13px">总计 {{ tables.length }}</span>
|
|
|
+ <el-input v-model="tablePageInput" style="width: 50px" placeholder="1" />
|
|
|
+ <el-select v-model="tablePageSize" size="small" style="width: 100px">
|
|
|
+ <el-option label="50/页" value="50" />
|
|
|
+ <el-option label="100/页" value="100" />
|
|
|
+ <el-option label="200/页" value="200" />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 其他 Tab -->
|
|
|
+ <template v-else>
|
|
|
+ <el-empty v-if="getTabData.length === 0" :description="`无${getTabLabel}`" />
|
|
|
+ <div v-else>
|
|
|
+ <div v-for="item in getTabData" :key="item.id" style="margin-bottom: 12px">
|
|
|
+ <el-card>
|
|
|
+ <div style="display: flex; justify-content: space-between; align-items: center">
|
|
|
+ <div style="display: flex; gap: 12px; align-items: flex-start; flex: 1">
|
|
|
+ <div style="font-size: 24px">{{ item.icon || '📋' }}</div>
|
|
|
+ <div style="flex: 1">
|
|
|
+ <div style="font-weight: 700; margin-bottom: 4px">{{ item.title }}</div>
|
|
|
+ <div style="color: #999; font-size: 12px">{{ item.description }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div style="display: flex; gap: 8px; align-items: center">
|
|
|
+ <el-tag size="small" type="info">个人的</el-tag>
|
|
|
+ <el-dropdown>
|
|
|
+ <span class="el-dropdown-link">•••</span>
|
|
|
+ <template #dropdown>
|
|
|
+ <el-dropdown-menu>
|
|
|
+ <el-dropdown-item>打开</el-dropdown-item>
|
|
|
+ <el-dropdown-item>删除</el-dropdown-item>
|
|
|
+ <el-dropdown-item>变更所有者</el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </template>
|
|
|
+ </el-dropdown>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
</div>
|
|
|
|
|
|
- <div style="display: flex; justify-content: flex-end; margin-top: 12px">
|
|
|
+ <!-- 分页器仅在工作流程 Tab 显示 -->
|
|
|
+ <div
|
|
|
+ v-if="activeTab === 'flows'"
|
|
|
+ style="
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ align-items: center;
|
|
|
+ margin-top: 16px;
|
|
|
+ gap: 12px;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <span style="color: #666; font-size: 13px">总计 {{ getTabData.length }}</span>
|
|
|
<el-pagination
|
|
|
background
|
|
|
:page-size="pageSize"
|
|
|
- :current-page.sync="page"
|
|
|
- :total="filtered.length"
|
|
|
+ :current-page="currentPage"
|
|
|
+ @update:current-page="currentPage = $event"
|
|
|
+ :total="getTabData.length"
|
|
|
layout="prev, pager, next"
|
|
|
/>
|
|
|
+ <el-select v-model.number="pageSize" size="small" style="width: 100px">
|
|
|
+ <el-option label="10/页" :value="10" />
|
|
|
+ <el-option label="20/页" :value="20" />
|
|
|
+ <el-option label="50/页" :value="50" />
|
|
|
+ </el-select>
|
|
|
</div>
|
|
|
</el-card>
|
|
|
+
|
|
|
+ <!-- 新变量对话框 -->
|
|
|
+ <el-dialog v-model="showVarDialog" title="新变量" width="500px" @close="resetVarForm">
|
|
|
+ <el-form :model="varForm">
|
|
|
+ <el-form-item label="钥匙" required>
|
|
|
+ <el-input v-model="varForm.key" placeholder="请输入姓名" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="价值">
|
|
|
+ <el-input v-model="varForm.value" type="textarea" placeholder="请输入一个值" rows="4" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="范围" required>
|
|
|
+ <el-select v-model="varForm.scope" placeholder="选择">
|
|
|
+ <el-option label="全局的" value="全局的" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <div style="text-align: right">
|
|
|
+ <el-button @click="showVarDialog = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="submitVariable">提交</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 创建新数据表对话框 -->
|
|
|
+ <el-dialog v-model="showTableDialog" title="创建新数据表" width="500px" @close="resetTableForm">
|
|
|
+ <el-form :model="tableForm">
|
|
|
+ <el-form-item label="数据表名称" required>
|
|
|
+ <el-input v-model="tableForm.name" placeholder="输入数据表名称" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="创建方式">
|
|
|
+ <el-radio-group v-model="tableForm.method">
|
|
|
+ <el-radio value="from-scratch">从零开始</el-radio>
|
|
|
+ <el-radio value="import-csv">导入 CSV 文件</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <div style="text-align: right">
|
|
|
+ <el-button @click="showTableDialog = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="submitTable">创建</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
import { ref, computed } from 'vue'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+import { Search } from '@element-plus/icons-vue'
|
|
|
+
|
|
|
+const $router = useRouter()
|
|
|
|
|
|
const cards = [
|
|
|
{ title: '生产执行', value: 0 },
|
|
|
{ title: '执行失败', value: 0 },
|
|
|
{ title: '故障率', value: '0%' },
|
|
|
+ { title: '节省时间', value: '0%' },
|
|
|
{ title: '运行时间(平均)', value: '0s' }
|
|
|
]
|
|
|
|
|
|
const workflows = ref([
|
|
|
- { id: 1, title: '与新同事人才交流', created: '1 月 23 日' },
|
|
|
- { id: 2, title: 'RAG 同步机器人,基于 Supabase', created: '1 月 23 日' },
|
|
|
- { id: 3, title: '利用 Gemini AI, OCR 和 Google Sheets', created: '1 月 23 日' }
|
|
|
+ { id: 1, title: '与新同事人才交流', created: '1 月 23 日', description: '', icon: '' },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ title: 'RAG 同步机器人,基于 Supabase',
|
|
|
+ created: '1 月 23 日',
|
|
|
+ description: '',
|
|
|
+ icon: ''
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 3,
|
|
|
+ title: '利用 Gemini AI, OCR 和 Google Sheets',
|
|
|
+ created: '1 月 23 日',
|
|
|
+ description: '',
|
|
|
+ icon: ''
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
+const certificates = ref([
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ title: 'n8n 免费 OpenAI API 额度',
|
|
|
+ description: 'OpenAI 创建更新了 3 天前 | 创建时间:1 月 23 日',
|
|
|
+ created: '1 月 23 日',
|
|
|
+ icon: '🤖'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ title: 'Google Sheets 帐户',
|
|
|
+ description: 'Google Sheets OAuth2 API | 上次更新时间:3 天前 | 创建时间:1 月 23 日',
|
|
|
+ created: '1 月 23 日',
|
|
|
+ icon: '📊'
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
+const executions = ref([
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ workflow: '我的工作流程',
|
|
|
+ status: '成功',
|
|
|
+ startTime: '1 月 26 10:39:03',
|
|
|
+ duration: '35 秒',
|
|
|
+ executionId: '1'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ workflow: '工作流程 2',
|
|
|
+ status: '成功',
|
|
|
+ startTime: '1 月 26 10:30:00',
|
|
|
+ duration: '2 分钟',
|
|
|
+ executionId: '2'
|
|
|
+ }
|
|
|
])
|
|
|
|
|
|
+const variables = ref([])
|
|
|
+
|
|
|
+const tables = ref([])
|
|
|
+
|
|
|
+// 数据表相关
|
|
|
+const showTableDialog = ref(false)
|
|
|
+const tablePageInput = ref('')
|
|
|
+const tablePageSize = ref('50')
|
|
|
+const tableForm = ref({
|
|
|
+ name: '',
|
|
|
+ method: 'from-scratch'
|
|
|
+})
|
|
|
+
|
|
|
+const resetTableForm = () => {
|
|
|
+ tableForm.value = {
|
|
|
+ name: '',
|
|
|
+ method: 'from-scratch'
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const submitTable = () => {
|
|
|
+ if (!tableForm.value.name.trim()) {
|
|
|
+ ElMessage.error('请输入数据表名称')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const newTable = {
|
|
|
+ id: Math.max(...tables.value.map((t: any) => t.id), 0) + 1,
|
|
|
+ name: tableForm.value.name,
|
|
|
+ description: tableForm.value.method === 'from-scratch' ? '从零开始创建' : '从 CSV 导入',
|
|
|
+ method: tableForm.value.method
|
|
|
+ }
|
|
|
+ tables.value.push(newTable)
|
|
|
+ showTableDialog.value = false
|
|
|
+ resetTableForm()
|
|
|
+ ElMessage.success('数据表已创建')
|
|
|
+}
|
|
|
+
|
|
|
const filter = ref('')
|
|
|
-const sort = ref('updated')
|
|
|
+const sort = ref('name')
|
|
|
const activeTab = ref('flows')
|
|
|
-const page = ref(1)
|
|
|
-const pageSize = 10
|
|
|
+const pageSize = ref(10)
|
|
|
+
|
|
|
+// 变量表单相关
|
|
|
+const showVarDialog = ref(false)
|
|
|
+const varFilter = ref('')
|
|
|
+const varSort = ref('name')
|
|
|
+const varPageSize = ref('25')
|
|
|
+const varForm = ref({
|
|
|
+ key: '',
|
|
|
+ value: '',
|
|
|
+ scope: '全局的'
|
|
|
+})
|
|
|
+
|
|
|
+const resetVarForm = () => {
|
|
|
+ varForm.value = {
|
|
|
+ key: '',
|
|
|
+ value: '',
|
|
|
+ scope: '全局的'
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const submitVariable = () => {
|
|
|
+ if (!varForm.value.key.trim()) {
|
|
|
+ ElMessage.error('请输入钥匙')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const newVar = {
|
|
|
+ id: Math.max(...variables.value.map((v: any) => v.id), 0) + 1,
|
|
|
+ key: varForm.value.key,
|
|
|
+ value: varForm.value.value,
|
|
|
+ usage: `$vars.${varForm.value.key}`,
|
|
|
+ scope: varForm.value.scope
|
|
|
+ }
|
|
|
+ variables.value.push(newVar)
|
|
|
+ showVarDialog.value = false
|
|
|
+ resetVarForm()
|
|
|
+ ElMessage.success('变量已添加')
|
|
|
+}
|
|
|
+
|
|
|
+const editVariable = (variable: any) => {
|
|
|
+ varForm.value = { ...variable, scope: variable.scope }
|
|
|
+ showVarDialog.value = true
|
|
|
+}
|
|
|
|
|
|
-const filtered = computed(() => {
|
|
|
+const deleteVariable = (id: number) => {
|
|
|
+ ElMessageBox.confirm('确定删除该变量吗?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ variables.value = variables.value.filter((v: any) => v.id !== id)
|
|
|
+ ElMessage.success('变量已删除')
|
|
|
+ })
|
|
|
+ .catch(() => {})
|
|
|
+}
|
|
|
+
|
|
|
+const pageMap = ref({
|
|
|
+ flows: 1,
|
|
|
+ certs: 1,
|
|
|
+ execs: 1,
|
|
|
+ vars: 1,
|
|
|
+ tables: 1
|
|
|
+})
|
|
|
+
|
|
|
+const currentPage = computed({
|
|
|
+ get: () => pageMap.value[activeTab.value as keyof typeof pageMap.value] || 1,
|
|
|
+ set: (val: number) => {
|
|
|
+ pageMap.value[activeTab.value as keyof typeof pageMap.value] = val
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const getTabLabel = computed(() => {
|
|
|
+ const labels: Record<string, string> = {
|
|
|
+ flows: '工作流程',
|
|
|
+ certs: '证书',
|
|
|
+ execs: '执行',
|
|
|
+ vars: '变量',
|
|
|
+ tables: '数据表'
|
|
|
+ }
|
|
|
+ return labels[activeTab.value] || '项目'
|
|
|
+})
|
|
|
+
|
|
|
+const getTabData = computed(() => {
|
|
|
+ const tabs: Record<string, any> = {
|
|
|
+ flows: workflows.value,
|
|
|
+ certs: certificates.value,
|
|
|
+ execs: executions.value,
|
|
|
+ vars: variables.value,
|
|
|
+ tables: tables.value
|
|
|
+ }
|
|
|
+ const data = tabs[activeTab.value] || []
|
|
|
const q = filter.value.trim().toLowerCase()
|
|
|
- if (!q) return workflows.value
|
|
|
- return workflows.value.filter((w) => w.title.toLowerCase().includes(q))
|
|
|
+ if (!q) return data
|
|
|
+ return data.filter((item: any) => item.title.toLowerCase().includes(q))
|
|
|
})
|
|
|
|
|
|
-const paged = computed(() => {
|
|
|
- const start = (page.value - 1) * pageSize
|
|
|
- return filtered.value.slice(start, start + pageSize)
|
|
|
+const pagedData = computed(() => {
|
|
|
+ const start = (currentPage.value - 1) * pageSize.value
|
|
|
+ return getTabData.value.slice(start, start + pageSize.value)
|
|
|
})
|
|
|
+
|
|
|
+const filteredVariables = computed(() => {
|
|
|
+ let result = variables.value
|
|
|
+ const q = varFilter.value.trim().toLowerCase()
|
|
|
+ if (q) {
|
|
|
+ result = result.filter(
|
|
|
+ (v: any) => v.key.toLowerCase().includes(q) || v.value.toLowerCase().includes(q)
|
|
|
+ )
|
|
|
+ }
|
|
|
+ // 排序
|
|
|
+ if (varSort.value === 'name') {
|
|
|
+ result.sort((a: any, b: any) => a.key.localeCompare(b.key))
|
|
|
+ } else if (varSort.value === 'name-desc') {
|
|
|
+ result.sort((a: any, b: any) => b.key.localeCompare(a.key))
|
|
|
+ }
|
|
|
+ return result
|
|
|
+})
|
|
|
+
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
</script>
|
|
|
|
|
|
<style lang="less" scoped></style>
|