| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- <template>
- <div class="taskList">
- <a-spin :spinning="state.onlineDockListLoading" v-if="state.collapsed">
- <div class="taskList-left">
- <div class="taskList-left-title">
- <div>
- 当前机场
- </div>
- <a-checkbox v-model:checked="checkState.checkAll" :indeterminate="checkState.indeterminate"
- @change="onCheckAllChange">
- 全选
- </a-checkbox>
- </div>
- <div v-for="(dock, index) in state.onlineDockList" :key="dock.sn">
- <div :class="[
- 'taskList-left-item',
- checkState.checkSnList.includes(dock.sn) ? 'taskList-left-item-selected' : ''
- ]" @click="onClickCheckItem(dock.sn)">
- <Airport :dock="dock" :look-info="false" />
- </div>
- </div>
- <div class="taskList-left-fill"></div>
- </div>
- </a-spin>
- <div :style="{ width: state.collapsed ? 'calc(100% - 250px)' : '100%' }">
- <Search :onClickCollapsed="() => { state.collapsed = !state.collapsed }"
- :onClickCreateTask="() => { state.visible = true }" :onClickSearch="onClickSearch"
- :onClickReset="onClickReset" />
- <a-table :scroll="{ x: '100%', y: 500 }" rowKey="job_id" :loading="state.listLoading" :columns="columns"
- :dataSource="state.list" @change="refreshData" :rowClassName="rowClassName" :pagination="paginationConfig">
- <!-- 计划|实际时间 -->
- <template #duration="{ record }">
- <div class="flex-row" style="white-space: pre-wrap">
- <div>
- <div>
- {{ record.begin_time }}
- </div>
- <div>
- {{ record.end_time }}
- </div>
- </div>
- <div class="ml10">
- <div>
- {{ record.execute_time }}
- </div>
- <div>
- {{ record.completed_time }}
- </div>
- </div>
- </div>
- </template>
- <!-- 执行状态 -->
- <template #status="{ record }">
- <div style="color: #2B85E4;" v-if="record.status === 1">
- 待执行
- </div>
- <div style="color: #2B85E4;" v-else-if="record.status === 2">
- 执行中
- </div>
- <div style="color: #19BE6B;" v-else-if="record.status === 3">
- 完成
- </div>
- <div style="color: #E02020;" v-else-if="record.status === 4">
- 取消
- </div>
- <div style="color: #E02020;" v-else-if="record.status === 5">
- 失败
- </div>
- <div style="color: #2B85E4;" v-else-if="record.status === 6">
- 暂停
- </div>
- </template>
- <!-- 媒体上传 -->
- <template #media_upload="{ record }">
- <div class="flex-display flex-align-center">
- <span class="circle-icon" :style="{ backgroundColor: formatMediaTaskStatus(record).color }"></span>
- {{ formatMediaTaskStatus(record).text }}
- </div>
- <div class="pl15">
- {{ formatMediaTaskStatus(record).number }}
- </div>
- </template>
- <!-- 操作 -->
- <template #action="{ record }">
- <div class="flex-align-center flex-row" style="color: #2d8cf0">
- <a-tooltip title="复制任务">
- <CopyOutlined style="margin-right: 10px;" />
- </a-tooltip>
- <a-tooltip title="查看轨迹" v-if="false">
- <GatewayOutlined style="margin-right: 10px;" />
- </a-tooltip>
- <a-tooltip title="删除">
- <DeleteOutlined @click="onClickDelete(record.job_id, record.job_name)" />
- </a-tooltip>
- </div>
- </template>
- </a-table>
- </div>
- <CreateTaskModal :jobId="''" :visible="state.visible" :onClickConfirm="createTaskModalOnClickConfirm"
- :onClickCancel="createTaskModalOnClickCancel" v-if="state.visible" />
- </div>
- </template>
- <script lang="ts" setup>
- import { reactive, onMounted, watch, computed } from 'vue';
- import { Modal, message } from 'ant-design-vue';
- import { CopyOutlined, GatewayOutlined, DeleteOutlined } from '@ant-design/icons-vue';
- import Search from './components/Search.vue';
- import Airport from '/@/components/airport/index.vue';
- import CreateTaskModal from './components/CreateTaskModal.vue';
- import { useMyStore } from '/@/store';
- import { useFormatTask } from '/@/components/task/use-format-task';
- import { apis } from '/@/api/custom';
- import { getDeviceTopo, getUnreadDeviceHms } from '/@/api/manage';
- import { getWorkspaceId } from '/@/utils';
- import { OnlineDevice, EModeCode } from '/@/types/device';
- import { EDeviceTypeName } from '/@/types';
- interface State {
- visible: boolean,
- collapsed: boolean,
- onlineDockListLoading: boolean,
- onlineDockList: OnlineDevice[],
- query: any,
- listLoading: boolean,
- list: any[],
- };
- const state: State = reactive({
- visible: false,
- collapsed: true,
- onlineDockListLoading: false,
- onlineDockList: [],
- query: undefined,
- listLoading: false,
- list: [],
- });
- const checkState = reactive({
- checkAll: false as boolean,
- indeterminate: false as boolean,
- checkSnList: [] as string[],
- });
- watch(() => checkState.checkSnList, val => {
- checkState.indeterminate = !!val.length && val.length < state.onlineDockList.length;
- checkState.checkAll = val.length === state.onlineDockList.length;
- }, { deep: true });
- const store = useMyStore();
- const { formatMediaTaskStatus } = useFormatTask();
- const deviceInfo = computed(() => store.state.deviceState.deviceInfo)
- const dockInfo = computed(() => store.state.deviceState.dockInfo)
- const hmsInfo = computed({
- get: () => store.state.hmsInfo,
- set: (val) => {
- return val
- }
- })
- const fetchOnlineDock = async () => {
- state.onlineDockListLoading = true;
- try {
- const res = await getDeviceTopo(getWorkspaceId());
- if (res.code !== 0) {
- return;
- }
- const list = state.onlineDockList;
- res.data.forEach((gateway: any) => {
- const child = gateway.children
- const device: OnlineDevice = {
- model: child?.device_name,
- callsign: child?.nickname,
- sn: child?.device_sn,
- mode: EModeCode.Disconnected,
- gateway: {
- model: gateway?.device_name,
- callsign: gateway?.nickname,
- sn: gateway?.device_sn,
- domain: gateway?.domain
- },
- payload: []
- }
- child?.payloads_list.forEach((payload: any) => {
- device.payload.push({
- index: payload.index,
- model: payload.model,
- payload_name: payload.payload_name,
- payload_sn: payload.payload_sn,
- control_source: payload.control_source,
- payload_index: payload.payload_index
- })
- })
- if (EDeviceTypeName.Dock === gateway.domain) {
- list.push(device)
- }
- })
- state.onlineDockList = list;
- checkState.checkAll = true;
- checkState.checkSnList = list.map(item => item.sn);
- } catch (error) {
- console.error(error);
- } finally {
- state.onlineDockListLoading = false;
- }
- }
- const fetchList = async () => {
- state.listLoading = true;
- try {
- const res = await apis.fetchJobList({
- ...state.query,
- snList: checkState.checkSnList.join(','),
- page: paginationConfig.current,
- page_size: paginationConfig.pageSize
- });
- if (res.code === 0) {
- state.list = res.data.list;
- paginationConfig.total = res.data.pagination.total
- paginationConfig.current = res.data.pagination.page
- paginationConfig.pageSize = res.data.pagination.page_size
- }
- } catch (e) {
- console.error(e);
- } finally {
- state.listLoading = false;
- }
- }
- function getUnreadHms(sn: string) {
- getUnreadDeviceHms(getWorkspaceId(), sn).then(res => {
- if (res.data.length !== 0) {
- hmsInfo.value[sn] = res.data
- }
- })
- }
- function getOnlineDeviceHms() {
- const snList = Object.keys(dockInfo.value)
- if (snList.length === 0) {
- return
- }
- snList.forEach(sn => {
- getUnreadHms(sn)
- })
- const deviceSnList = Object.keys(deviceInfo.value)
- if (deviceSnList.length === 0) {
- return
- }
- deviceSnList.forEach(sn => {
- getUnreadHms(sn)
- })
- }
- onMounted(async () => {
- await fetchOnlineDock();
- setTimeout(() => {
- watch(() => store.state.deviceStatusEvent, async data => {
- await fetchOnlineDock()
- if (data.deviceOnline.sn) {
- getUnreadHms(data.deviceOnline.sn)
- }
- }, { deep: true })
- getOnlineDeviceHms()
- }, 1000)// 默认3秒,此时改成1秒
- await fetchList();
- });
- // 全选
- const onCheckAllChange = async (e: any) => {
- Object.assign(checkState, {
- checkSnList: e.target.checked ? state.onlineDockList.map(item => item.sn) : [],
- indeterminate: false,
- });
- await fetchList();
- }
- // 点击勾选条
- const onClickCheckItem = async (sn: string) => {
- const list = checkState.checkSnList;
- if (list.includes(sn)) {
- checkState.checkSnList = list.filter(item => item !== sn);
- } else {
- list.push(sn);
- checkState.checkSnList = list;
- }
- await fetchList();
- }
- // 新建机场任务弹出层-点击确定
- const createTaskModalOnClickConfirm = async () => {
- state.visible = false;
- }
- // 新建机场任务弹出层-点击取消
- const createTaskModalOnClickCancel = () => {
- state.visible = false;
- }
- const paginationConfig = reactive({
- pageSizeOptions: ['20', '50', '100'],
- showQuickJumper: true,
- showSizeChanger: true,
- pageSize: 20,
- current: 1,
- total: 0
- });
- const columns = [
- {
- title: '计划|实际时间',
- dataIndex: 'duration',
- width: 200,
- slots: { customRender: 'duration' },
- },
- {
- title: '执行状态',
- dataIndex: 'status',
- width: 100,
- slots: { customRender: 'status' },
- },
- {
- title: '任务名称',
- dataIndex: 'job_name',
- width: 150,
- ellipsis: true,
- },
- {
- title: '任务策略',
- dataIndex: 'task_type',
- width: 120,
- customRender: ({ text }: any) => {
- let content = '';
- switch (text) {
- case 0:
- content = '立即';
- break;
- case 1:
- content = '定时';
- break;
- case 2:
- content = '循环';
- break;
- default:
- break;
- }
- return content;
- }
- },
- {
- title: '航线名称',
- dataIndex: 'file_name',
- width: 150,
- ellipsis: true,
- },
- {
- title: '设备名称',
- dataIndex: 'dock_name',
- width: 200,
- ellipsis: true,
- },
- {
- title: '创建人',
- dataIndex: 'username',
- width: 150,
- },
- {
- title: '媒体上传',
- dataIndex: 'media_upload',
- width: 200,
- slots: { customRender: 'media_upload' },
- },
- {
- title: '操作',
- dataIndex: 'actions',
- fixed: 'right',
- width: 100,
- slots: { customRender: 'action' },
- },
- ];
- const rowClassName = (record: any, index: number) => {
- const className = []
- if ((index & 1) === 0) {
- className.push('table-striped')
- }
- return className.toString().replaceAll(',', ' ')
- }
- const refreshData = async (page: any) => {
- paginationConfig.current = page?.current!
- paginationConfig.pageSize = page?.pageSize!
- await fetchList();
- }
- // 点击搜索
- const onClickSearch = async (query: any) => {
- state.query = query;
- await fetchList();
- }
- // 点击重置
- const onClickReset = async (query: any) => {
- state.query = query;
- await fetchList();
- }
- // 点击删除
- const onClickDelete = (id: string, name: string) => {
- Modal.confirm({
- title: '删除任务',
- content: `确定删除${name}吗?`,
- okType: 'danger',
- onOk: async () => {
- try {
- await apis.deleteJob({ job_id: id });
- await fetchList();
- message.success('删除成功');
- } catch (error) {
- message.error('删除失败: ' + error);
- }
- },
- })
- }
- </script>
- <style lang="scss">
- .taskList {
- padding: 20px;
- display: flex;
- &-left {
- width: 230px;
- height: calc(100vh - 146px);
- padding: 10px 8px 0;
- background-color: #232323;
- overflow-y: auto;
- margin-right: 20px;
- &-title {
- display: flex;
- justify-content: space-between;
- align-items: center;
- color: #FFFFFF;
- .ant-checkbox-wrapper {
- color: #FFFFFF !important;
- }
- }
- &-item {
- border-radius: 4px;
- border: 2px solid transparent;
- overflow: hidden;
- margin-top: 10px;
- &-selected {
- border-color: #1fa3f6;
- }
- }
- &-fill {
- width: 100%;
- height: 20px;
- }
- }
- }
- .ant-table {
- border-top: 1px solid rgb(0, 0, 0, 0.06);
- border-bottom: 1px solid rgb(0, 0, 0, 0.06);
- }
- .ant-table-tbody tr td {
- border: 0;
- }
- .table-striped {
- background-color: #f7f9fa;
- }
- </style>
|