Browse Source

feat: 任务管理表单控件切换到 TDesign

main
xiaosi 3 weeks ago
parent
commit
8d5e3ca016
  1. 130
      src/views/TasksView/TasksView.vue

130
src/views/TasksView/TasksView.vue

@ -25,14 +25,13 @@
<!-- 任务计划 --> <!-- 任务计划 -->
<div class="business-panel" :class="{ active: view === 'plans' }"> <div class="business-panel" :class="{ active: view === 'plans' }">
<div class="business-toolbar"> <div class="business-toolbar">
<label class="business-search"><svg><use href="#i-search" /></svg><input v-model="search" type="text" placeholder="搜索任务名称、编号或设备" /></label>
<div>
<select v-model="planFilter">
<option value="">全部状态</option>
<option value="mission">执行中</option>
<option value="pending">待执行</option>
<option value="offline">已暂停</option>
</select>
<div class="business-search">
<t-input v-model="search" clearable placeholder="搜索任务名称、编号或设备">
<template #prefix-icon><t-icon name="search" /></template>
</t-input>
</div>
<div class="toolbar-group">
<t-select v-model="planFilter" :options="planFilterOptions" placeholder="全部状态" style="width: 140px" />
<button class="plain-command" @click="resetPlanFilter">重置</button> <button class="plain-command" @click="resetPlanFilter">重置</button>
</div> </div>
</div> </div>
@ -70,12 +69,16 @@
<!-- 执行记录 --> <!-- 执行记录 -->
<div class="business-panel" :class="{ active: view === 'records' }"> <div class="business-panel" :class="{ active: view === 'records' }">
<div class="business-toolbar"> <div class="business-toolbar">
<div>
<select v-model="execFilter"><option value="">全部设备</option><option v-for="d in devices" :key="d.id" :value="d.dockId || d.droneSn">{{ d.name }}</option></select>
<div class="business-search"><svg><use href="#i-search" /></svg><input v-model="execSearch" type="text" placeholder="搜索执行记录" /></div>
<div class="toolbar-group">
<t-select v-model="execFilter" :options="execFilterOptions" placeholder="全部设备" style="width: 160px" />
<div class="business-search">
<t-input v-model="execSearch" clearable placeholder="搜索执行记录">
<template #prefix-icon><t-icon name="search" /></template>
</t-input>
</div>
</div> </div>
</div> </div>
<div class="record-header"><div><h2>执行记录</h2><span>最近 30 天执行记录</span></div><select v-model="execResultFilter"><option value="">全部执行结果</option><option value="succeeded">成功</option><option value="failed">失败</option><option value="cancelled">已取消</option></select></div>
<div class="record-header"><div><h2>执行记录</h2><span>最近 30 天执行记录</span></div><t-select v-model="execResultFilter" :options="execResultFilterOptions" placeholder="全部执行结果" style="width: 160px" /></div>
<div class="business-table-wrap"> <div class="business-table-wrap">
<table class="business-table execution-record-table"> <table class="business-table execution-record-table">
<thead> <thead>
@ -111,7 +114,11 @@
<div class="business-panel route-library" :class="{ active: view === 'routes' }"> <div class="business-panel route-library" :class="{ active: view === 'routes' }">
<div class="route-library-view" :hidden="routeEditorOpen"> <div class="route-library-view" :hidden="routeEditorOpen">
<div class="business-toolbar"> <div class="business-toolbar">
<label class="business-search"><svg><use href="#i-search" /></svg><input v-model="routeSearch" type="text" placeholder="搜索航线名称或区域" /></label>
<div class="business-search">
<t-input v-model="routeSearch" clearable placeholder="搜索航线名称或区域">
<template #prefix-icon><t-icon name="search" /></template>
</t-input>
</div>
<button class="plain-command" @click="openRouteEditor"><svg><use href="#i-plus" /></svg>新建航线</button> <button class="plain-command" @click="openRouteEditor"><svg><use href="#i-plus" /></svg>新建航线</button>
</div> </div>
<div class="route-grid"> <div class="route-grid">
@ -137,11 +144,11 @@
</header> </header>
<div class="route-editor-body"> <div class="route-editor-body">
<aside class="route-config-panel"> <aside class="route-config-panel">
<label><span>航线名称 <b>*</b></span><input v-model="routeForm.name" maxlength="128" placeholder="请输入航线名称" /></label>
<label><span>航线名称 <b>*</b></span><t-input v-model="routeForm.name" :maxlength="128" placeholder="请输入航线名称" /></label>
<div class="route-default-title"><strong>新增航点默认参数</strong></div> <div class="route-default-title"><strong>新增航点默认参数</strong></div>
<div class="route-default-row"> <div class="route-default-row">
<label><span>高度</span><div class="number-input"><input v-model.number="routeForm.altitude" type="number" min="20" max="120" /><em>m</em></div></label>
<label><span>速度</span><div class="number-input"><input v-model.number="routeForm.speed" type="number" min="1" max="20" step="0.1" /><em>m/s</em></div></label>
<label><span>高度</span><div class="number-input"><t-input-number v-model="routeForm.altitude" theme="normal" :min="20" :max="120" style="width: 100%" /><em>m</em></div></label>
<label><span>速度</span><div class="number-input"><t-input-number v-model="routeForm.speed" theme="normal" :min="1" :max="20" :step="0.1" style="width: 100%" /><em>m/s</em></div></label>
</div> </div>
<label><span>转弯方式</span><div class="mode-segment route-turn-segment"><button type="button" :class="{ active: routeForm.turnMode === 'auto' }" @click="routeForm.turnMode = 'auto'">自动转弯</button><button type="button" :class="{ active: routeForm.turnMode === 'stop' }" @click="routeForm.turnMode = 'stop'">定点转弯</button></div></label> <label><span>转弯方式</span><div class="mode-segment route-turn-segment"><button type="button" :class="{ active: routeForm.turnMode === 'auto' }" @click="routeForm.turnMode = 'auto'">自动转弯</button><button type="button" :class="{ active: routeForm.turnMode === 'stop' }" @click="routeForm.turnMode = 'stop'">定点转弯</button></div></label>
<button class="apply-all-waypoints" type="button" :disabled="!routePoints.length" @click="applyRouteDefaults"><svg><use href="#i-settings" /></svg>应用到全部航点</button> <button class="apply-all-waypoints" type="button" :disabled="!routePoints.length" @click="applyRouteDefaults"><svg><use href="#i-settings" /></svg>应用到全部航点</button>
@ -168,19 +175,13 @@
<button class="icon-button" type="button" @click="createTaskOpen = false"><svg><use href="#i-x" /></svg></button> <button class="icon-button" type="button" @click="createTaskOpen = false"><svg><use href="#i-x" /></svg></button>
</header> </header>
<form novalidate @submit.prevent="submitCreateTask"> <form novalidate @submit.prevent="submitCreateTask">
<label><span>任务名称<b>*</b></span><input v-model="taskForm.name" type="text" maxlength="40" placeholder="例如:园区西侧道路巡检" /></label>
<label><span>任务名称<b>*</b></span><t-input v-model="taskForm.name" :maxlength="40" placeholder="例如:园区西侧道路巡检" /></label>
<div class="form-row"> <div class="form-row">
<label><span>执行机巢<b>*</b></span> <label><span>执行机巢<b>*</b></span>
<select v-model="taskForm.dockId">
<option value="">请选择机巢</option>
<option v-for="dock in deviceStore.docks" :key="dock.id" :value="dock.dockId">{{ dock.name }} · {{ dock.dockId }}</option>
</select>
<t-select v-model="taskForm.dockId" :options="dockOptions" placeholder="请选择机巢" style="width: 100%" />
</label> </label>
<label><span>飞行航线<b>*</b></span> <label><span>飞行航线<b>*</b></span>
<select v-model="taskForm.routeId">
<option value="">请选择航线</option>
<option v-for="taskRoute in routes" :key="taskRoute.id" :value="taskRoute.id">{{ taskRoute.name }}</option>
</select>
<t-select v-model="taskForm.routeId" :options="routeOptions" placeholder="请选择航线" style="width: 100%" />
</label> </label>
</div> </div>
<div class="task-schedule-settings"> <div class="task-schedule-settings">
@ -192,14 +193,14 @@
<button type="button" :class="{ active: taskForm.scheduleType === 'cron' && taskForm.schedulePeriod === 'custom' }" @click="setScheduleType('custom')">自定义 Cron</button> <button type="button" :class="{ active: taskForm.scheduleType === 'cron' && taskForm.schedulePeriod === 'custom' }" @click="setScheduleType('custom')">自定义 Cron</button>
</div> </div>
<div v-if="taskForm.scheduleType === 'cron' && taskForm.schedulePeriod !== 'custom'" class="form-row"> <div v-if="taskForm.scheduleType === 'cron' && taskForm.schedulePeriod !== 'custom'" class="form-row">
<label v-if="taskForm.schedulePeriod === 'week'"><span>星期</span><select v-model.number="taskForm.scheduleWeekday"><option v-for="day in weekdays" :key="day.value" :value="day.value">{{ day.label }}</option></select></label>
<label><span>执行时间</span><input v-model="taskForm.scheduleTime" type="time" /></label>
<label v-if="taskForm.schedulePeriod === 'week'"><span>星期</span><t-select v-model="taskForm.scheduleWeekday" :options="weekdayOptions" style="width: 100%" /></label>
<label><span>执行时间</span><t-time-picker v-model="taskForm.scheduleTime" format="HH:mm" allow-input style="width: 100%" /></label>
</div> </div>
<label v-if="taskForm.scheduleType === 'cron' && taskForm.schedulePeriod === 'custom'"><span>Cron 表达式<b>*</b></span><input v-model.trim="taskForm.scheduleCron" type="text" maxlength="64" placeholder="例如:0 8 * * 1-5" /><small>标准五段式 不支持秒级</small></label>
<label v-if="taskForm.scheduleType === 'cron' && taskForm.schedulePeriod === 'custom'"><span>Cron 表达式<b>*</b></span><t-input v-model.trim="taskForm.scheduleCron" :maxlength="64" placeholder="例如:0 8 * * 1-5" /><small>标准五段式 不支持秒级</small></label>
</div> </div>
<div class="task-video-settings"> <div class="task-video-settings">
<div class="task-video-title"><strong>视频策略</strong><span>按任务配置</span></div> <div class="task-video-title"><strong>视频策略</strong><span>按任务配置</span></div>
<label class="task-video-option"><span><strong>原始视频录制</strong><small>任务结束后自动上传原始文件</small></span><input v-model="taskForm.rawRecordingEnabled" type="checkbox" /><i></i></label>
<label class="task-video-option"><span><strong>原始视频录制</strong><small>任务结束后自动上传原始文件</small></span><t-switch v-model="taskForm.rawRecordingEnabled" /></label>
</div> </div>
<div class="form-error">{{ taskFormError }}</div> <div class="form-error">{{ taskFormError }}</div>
<footer> <footer>
@ -215,8 +216,8 @@
<header><div><span>航线编辑</span><h3>编辑航点 {{ editingWaypointIndex + 1 }}</h3></div><button class="icon-button" type="button" @click="closeWaypointEditor"><svg><use href="#i-x" /></svg></button></header> <header><div><span>航线编辑</span><h3>编辑航点 {{ editingWaypointIndex + 1 }}</h3></div><button class="icon-button" type="button" @click="closeWaypointEditor"><svg><use href="#i-x" /></svg></button></header>
<form novalidate @submit.prevent="saveWaypoint"> <form novalidate @submit.prevent="saveWaypoint">
<div class="waypoint-id-row"><span>航点 ID</span><strong>{{ editingWaypointIndex + 1 }}</strong></div> <div class="waypoint-id-row"><span>航点 ID</span><strong>{{ editingWaypointIndex + 1 }}</strong></div>
<div class="form-row"><label><span>经度 <b>*</b></span><input v-model.number="waypointForm.longitude" type="number" step="any" /></label><label><span>纬度 <b>*</b></span><input v-model.number="waypointForm.latitude" type="number" step="any" /></label></div>
<div class="form-row"><label><span>高度 <b>*</b></span><div class="number-input"><input v-model.number="waypointForm.altitude" type="number" min="20" max="120" /><em>m</em></div></label><label><span>速度 <b>*</b></span><div class="number-input"><input v-model.number="waypointForm.speed" type="number" min="1" max="20" step="0.1" /><em>m/s</em></div></label></div>
<div class="form-row"><label><span>经度 <b>*</b></span><t-input-number v-model="waypointForm.longitude" theme="normal" :min="-180" :max="180" :step="0.000001" style="width: 100%" /></label><label><span>纬度 <b>*</b></span><t-input-number v-model="waypointForm.latitude" theme="normal" :min="-90" :max="90" :step="0.000001" style="width: 100%" /></label></div>
<div class="form-row"><label><span>高度 <b>*</b></span><div class="number-input"><t-input-number v-model="waypointForm.altitude" theme="normal" :min="20" :max="120" style="width: 100%" /><em>m</em></div></label><label><span>速度 <b>*</b></span><div class="number-input"><t-input-number v-model="waypointForm.speed" theme="normal" :min="1" :max="20" :step="0.1" style="width: 100%" /><em>m/s</em></div></label></div>
<label><span>转弯方式</span><div class="mode-segment waypoint-turn-segment"><button type="button" :class="{ active: waypointForm.turnMode === 'auto' }" @click="waypointForm.turnMode = 'auto'">自动转弯</button><button type="button" :class="{ active: waypointForm.turnMode === 'stop' }" @click="waypointForm.turnMode = 'stop'">定点转弯</button></div></label> <label><span>转弯方式</span><div class="mode-segment waypoint-turn-segment"><button type="button" :class="{ active: waypointForm.turnMode === 'auto' }" @click="waypointForm.turnMode = 'auto'">自动转弯</button><button type="button" :class="{ active: waypointForm.turnMode === 'stop' }" @click="waypointForm.turnMode = 'stop'">定点转弯</button></div></label>
<div class="route-form-error">{{ waypointFormError }}</div> <div class="route-form-error">{{ waypointFormError }}</div>
<footer><button class="plain-command" type="button" @click="closeWaypointEditor">取消</button><button class="primary-command" type="submit">保存航点</button></footer> <footer><button class="plain-command" type="button" @click="closeWaypointEditor">取消</button><button class="primary-command" type="submit">保存航点</button></footer>
@ -250,9 +251,21 @@ const deviceStore = useDeviceStore()
const view = ref(route.query.view || 'plans') const view = ref(route.query.view || 'plans')
const search = ref('') const search = ref('')
const planFilter = ref('') const planFilter = ref('')
const planFilterOptions = [
{ label: '全部状态', value: '' },
{ label: '执行中', value: 'mission' },
{ label: '待执行', value: 'pending' },
{ label: '已暂停', value: 'offline' }
]
const execSearch = ref('') const execSearch = ref('')
const execFilter = ref('') const execFilter = ref('')
const execResultFilter = ref('') const execResultFilter = ref('')
const execResultFilterOptions = [
{ label: '全部执行结果', value: '' },
{ label: '成功', value: 'succeeded' },
{ label: '失败', value: 'failed' },
{ label: '已取消', value: 'cancelled' }
]
const routeSearch = ref('') const routeSearch = ref('')
const routeEditorOpen = ref(false) const routeEditorOpen = ref(false)
const routeSaving = ref(false) const routeSaving = ref(false)
@ -276,6 +289,22 @@ const records = ref([])
let refreshTimer = null let refreshTimer = null
const routes = ref([]) const routes = ref([])
const devices = computed(() => [...deviceStore.drones, ...deviceStore.docks]) const devices = computed(() => [...deviceStore.drones, ...deviceStore.docks])
const execFilterOptions = computed(() => [
{ label: '全部设备', value: '' },
...devices.value.map((d) => ({ label: d.name, value: d.dockId || d.droneSn }))
])
const dockOptions = computed(() =>
deviceStore.docks.map((dock) => ({
label: `${dock.name} · ${dock.dockId}`,
value: dock.dockId
}))
)
const routeOptions = computed(() =>
routes.value.map((taskRoute) => ({
label: taskRoute.name,
value: taskRoute.id
}))
)
const createTaskOpen = ref(false) const createTaskOpen = ref(false)
const creatingTask = ref(false) const creatingTask = ref(false)
@ -292,7 +321,7 @@ const taskForm = reactive({
rawRecordingEnabled: false rawRecordingEnabled: false
}) })
const weekdays = [
const weekdayOptions = [
{ value: 1, label: '星期一' }, { value: 1, label: '星期一' },
{ value: 2, label: '星期二' }, { value: 2, label: '星期二' },
{ value: 3, label: '星期三' }, { value: 3, label: '星期三' },
@ -930,3 +959,40 @@ async function downloadRawVideo(record) {
} }
} }
</script> </script>
<style scoped>
.business-search :deep(.t-input) {
width: 100%;
}
.business-search {
border: 0;
padding: 0;
height: auto;
background: transparent;
}
.toolbar-group {
display: flex;
align-items: center;
gap: 8px;
}
.toolbar-group :deep(.t-select) {
flex-shrink: 0;
}
.add-dock-dialog :deep(.t-input),
.add-dock-dialog :deep(.t-input-number),
.add-dock-dialog :deep(.t-select),
.add-dock-dialog :deep(.t-time-picker),
.route-config-panel :deep(.t-input),
.route-config-panel :deep(.t-input-number) {
width: 100%;
}
.number-input :deep(.t-input-number) {
width: 100%;
}
.number-input :deep(.t-input) {
padding-right: 34px;
}
.task-video-option :deep(.t-switch) {
justify-self: end;
}
</style>

Loading…
Cancel
Save