| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- <template>
- <div class="panel">
- <div class="toolbar">
- <div class="filters">
- <el-button @click="load">刷新</el-button>
- </div>
- </div>
- <el-table :data="screens.items" border stripe>
- <el-table-column prop="id" label="ID" width="80" />
- <el-table-column prop="interface_name" label="界面名称" min-width="180" />
- <el-table-column prop="description" label="描述" min-width="320" show-overflow-tooltip />
- <el-table-column label="类型" width="180">
- <template #default="{ row }">
- <el-tag v-if="row.is_windows_desktop" type="success">Windows 桌面</el-tag>
- <el-tag v-if="row.is_browser_webpage" type="primary">浏览器网页</el-tag>
- <el-tag v-if="!row.is_windows_desktop && !row.is_browser_webpage" type="info">窗口界面</el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="element_count" label="元素" width="90" />
- <el-table-column prop="created_at" label="识别时间" min-width="180" />
- <el-table-column label="操作" width="180" fixed="right">
- <template #default="{ row }">
- <el-button size="small" @click="openDetail(row)">详情</el-button>
- <el-button size="small" type="danger" @click="remove(row)">删除</el-button>
- </template>
- </el-table-column>
- </el-table>
- <el-dialog v-model="dialog" title="界面详情" width="980px">
- <div v-if="detail" class="screen-detail-layout">
- <div>
- <div class="screenshot-stage small">
- <div v-if="detailImageSrc" class="screenshot-canvas" :style="canvasStyle">
- <img class="screenshot-image" :src="detailImageSrc" alt="已识别界面截图" />
- <button
- v-for="element in detail.elements || []"
- :key="element.id"
- class="element-marker"
- :style="markerStyle(element)"
- >
- {{ element.element_index }}
- </button>
- </div>
- </div>
- </div>
- <div>
- <el-descriptions :column="1" border>
- <el-descriptions-item label="名称">{{ detail.interface_name }}</el-descriptions-item>
- <el-descriptions-item label="描述">{{ detail.description || '-' }}</el-descriptions-item>
- <el-descriptions-item label="分辨率">{{ detail.width }} x {{ detail.height }}</el-descriptions-item>
- </el-descriptions>
- <el-table :data="detail.elements || []" height="360" border stripe style="margin-top: 12px">
- <el-table-column prop="element_index" label="#" width="54" />
- <el-table-column prop="name" label="名称" min-width="140" show-overflow-tooltip />
- <el-table-column label="坐标" width="120">
- <template #default="{ row }">{{ row.x }}, {{ row.y }}</template>
- </el-table-column>
- </el-table>
- </div>
- </div>
- </el-dialog>
- </div>
- </template>
- <script setup>
- import { computed, onMounted, ref } from 'vue'
- import { ElMessage, ElMessageBox } from 'element-plus'
- import { api } from '../api'
- const screens = ref({ items: [] })
- const detail = ref(null)
- const dialog = ref(false)
- const detailImageSrc = computed(() => {
- if (!detail.value?.image_base64) return ''
- return `data:${detail.value.mime_type || 'image/png'};base64,${detail.value.image_base64}`
- })
- const canvasStyle = computed(() => {
- if (!detail.value?.width || !detail.value?.height) return {}
- return { aspectRatio: `${detail.value.width} / ${detail.value.height}` }
- })
- async function load() {
- const { data } = await api.get('/api/automation/screens')
- screens.value = data
- }
- async function openDetail(row) {
- const { data } = await api.get(`/api/automation/screens/${row.id}`, { params: { include_image: true } })
- detail.value = data
- dialog.value = true
- }
- function markerStyle(element) {
- const left = detail.value?.width ? (element.x / detail.value.width) * 100 : element.x_percent
- const top = detail.value?.height ? (element.y / detail.value.height) * 100 : element.y_percent
- return { left: `${left}%`, top: `${top}%` }
- }
- async function remove(row) {
- await ElMessageBox.confirm(`确认删除界面“${row.interface_name}”?`, '删除界面', { type: 'warning' })
- await api.delete(`/api/automation/screens/${row.id}`)
- ElMessage.success('已删除')
- await load()
- }
- defineExpose({ load })
- onMounted(load)
- </script>
|