Commit 4c265c8e by liucan

feat:完成卫星编号弹窗相关功能,修复部分爆红组件

parent 0b581533
......@@ -13,6 +13,7 @@
"@types/node": "^22.15.17",
"@types/nprogress": "^0.2.3",
"@types/uuid": "^10.0.0",
"animate.css": "^4.1.1",
"axios": "^1.9.0",
"cron-parser": "^5.4.0",
"echarts": "^5.6.0",
......@@ -2293,6 +2294,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/animate.css": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/animate.css/-/animate.css-4.1.1.tgz",
"integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==",
"license": "MIT"
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
......
......@@ -14,6 +14,7 @@
"@types/node": "^22.15.17",
"@types/nprogress": "^0.2.3",
"@types/uuid": "^10.0.0",
"animate.css": "^4.1.1",
"axios": "^1.9.0",
"cron-parser": "^5.4.0",
"echarts": "^5.6.0",
......
......@@ -7,7 +7,8 @@
</template>
<style>
html, body {
html,
body {
margin: 0;
padding: 0;
height: 100%;
......
// API路径配置文件
// 这个文件确保所有API路径在一处统一管理,方便修改
import { pauseJob } from "./schedule"
import { pauseJob } from "./schedule";
export const authApi = {
login: '/auth/login',
logout: '/auth/logout'
} as const
login: "/auth/login",
logout: "/auth/logout",
} as const;
export const scrapydApi = {
// scrapyd相关接口
listProjects: '/scrapyd/listProjects',
listVersions: '/scrapyd/listVersions',
listSpiders: '/scrapyd/listSpiders',
listJobsMerge: '/scrapyd/listJobsMerge',
cancel: '/scrapyd/cancel',
cancelAllJob: '/scrapyd/cancelAllJob',
deleteVersion: '/scrapyd/deleteVersion',
deleteProject: '/scrapyd/deleteProject',
schedule: '/scrapyd/schedule',
daemonStatus: '/scrapyd/daemonStatus',
addVersion: '/scrapyd/addVersion',
listProjects: "/scrapyd/listProjects",
listVersions: "/scrapyd/listVersions",
listSpiders: "/scrapyd/listSpiders",
listJobsMerge: "/scrapyd/listJobsMerge",
cancel: "/scrapyd/cancel",
cancelAllJob: "/scrapyd/cancelAllJob",
deleteVersion: "/scrapyd/deleteVersion",
deleteProject: "/scrapyd/deleteProject",
schedule: "/scrapyd/schedule",
daemonStatus: "/scrapyd/daemonStatus",
addVersion: "/scrapyd/addVersion",
// 日志相关
logs: '/scrapyd/logs',
projectLogs: '/scrapyd/projectLogs',
spiderLogs: '/scrapyd/spiderLogs',
jobLog: '/scrapyd/jobLog'
} as const
logs: "/scrapyd/logs",
projectLogs: "/scrapyd/projectLogs",
spiderLogs: "/scrapyd/spiderLogs",
jobLog: "/scrapyd/jobLog",
} as const;
export const scheduleApi = {
// 调度任务相关
addJob: '/schedule/addJob',
getJobs: '/schedule/getJobs',
pauseJob: '/schedule/pauseJob',
resumeJob: '/schedule/resumeJob',
removeJob: '/schedule/removeJob',
jobDetail: '/schedule/jobDetail',
state: '/schedule/state',
start: '/schedule/start',
shutdown: '/schedule/shutdown',
pause: '/schedule/pause',
resume: '/schedule/resume',
removeAllJobs: '/schedule/removeAllJobs',
scheduleLogs: '/schedule/scheduleLogs',
removeScheduleLogs: '/schedule/removeScheduleLogs',
} as const
addJob: "/schedule/addJob",
getJobs: "/schedule/getJobs",
pauseJob: "/schedule/pauseJob",
resumeJob: "/schedule/resumeJob",
removeJob: "/schedule/removeJob",
jobDetail: "/schedule/jobDetail",
state: "/schedule/state",
start: "/schedule/start",
shutdown: "/schedule/shutdown",
pause: "/schedule/pause",
resume: "/schedule/resume",
removeAllJobs: "/schedule/removeAllJobs",
scheduleLogs: "/schedule/scheduleLogs",
removeScheduleLogs: "/schedule/removeScheduleLogs",
} as const;
export const scrapydServerApi = {
getScrapydServerPage: '/scrapydServer/getScrapydServerPage',
addScrapydServer: '/scrapydServer/addScrapydServer',
updateScrapydServer: '/scrapydServer/updateScrapydServer',
updateScrapydServerStatus: '/scrapydServer/updateScrapydServerStatus',
deleteScrapydServer: '/scrapydServer/deleteScrapydServer'
} as const
// 系统相关
getScrapydServerPage: "/scrapydServer/getScrapydServerPage",
addScrapydServer: "/scrapydServer/addScrapydServer",
updateScrapydServer: "/scrapydServer/updateScrapydServer",
updateScrapydServerStatus: "/scrapydServer/updateScrapydServerStatus",
deleteScrapydServer: "/scrapydServer/deleteScrapydServer",
} as const;
// 系统相关
export const systemApi = {
systemInfo: '/system/systemInfo',
systemData: '/system/systemData',
systemConfig: '/system/systemConfig',
loginHistoryList: '/actionHistory/loginHistoryList',
scrapydServerList: '/system/scrapydServerList' // 没用
} as const
// 统计相关
systemInfo: "/system/systemInfo",
systemData: "/system/systemData",
systemConfig: "/system/systemConfig",
loginHistoryList: "/actionHistory/loginHistoryList",
scrapydServerList: "/system/scrapydServerList", // 没用
} as const;
// 统计相关
export const statsApi = {
statsList: '/statsCollection/listItem',
statsDetail: '/statsCollection/detail',
removeStats: '/statsCollection/delete',
clearAllStats: '/statsCollection/clearAll' // TODO 未实现
} as const
statsList: "/statsCollection/listItem",
statsDetail: "/statsCollection/detail",
removeStats: "/statsCollection/delete",
clearAllStats: "/statsCollection/clearAll", // TODO 未实现
} as const;
// 系统用户相关接口
// 系统用户相关接口
export const userApi = {
userList: '/user/list',
addUser: '/user/insert',
updateUser: '/user/update',
deleteUser: '/user/delete',
batchDelete: '/user/batchDelete',
} as const
// 系统用户相关接口
userList: "/user/list",
addUser: "/user/insert",
updateUser: "/user/update",
deleteUser: "/user/delete",
batchDelete: "/user/batchDelete",
} as const;
// 系统用户相关接口
export const spiderApi = {
spiderList: '/scrapyd/listSpiders',
} as const
spiderList: "/scrapyd/listSpiders",
} as const;
// 爬虫任务相关接口
// 爬虫任务相关接口
export const spiderTaskApi = {
taskList: '/schedule/getJobs',
taskRecord: '/schedule/scheduleLogs',
addTask: '/schedule/addJob',
deleteTask: '/schedule/removeJob',
pauseJob: '/schedule/pauseJob',
resumeJob: '/schedule/resumeJob',
jobDetail: '/schedule/jobDetail',
taskCount:'/schedule/getAllJobCount'
} as const
// 爬虫数据相关接口
taskList: "/schedule/getJobs",
taskRecord: "/schedule/scheduleLogs",
addTask: "/schedule/addJob",
deleteTask: "/schedule/removeJob",
pauseJob: "/schedule/pauseJob",
resumeJob: "/schedule/resumeJob",
jobDetail: "/schedule/jobDetail",
taskCount: "/schedule/getAllJobCount",
addSateNo: "/schedule/resetNoradID",
getSateIdList: "/schedule/getNoradIDList",
} as const;
// 爬虫数据相关接口
export const spiderDataApi = {
dsnList: '/dsn/list',
dsnDetail: '/dsn/detail',
dsnDataDelete: '/dsn/delete',
ituList: '/itu/list',
ituDetail: '/itu/detail',
ituDataDelete: '/itu/delete',
stList: '/spaceTrack/list',
stDetail: '/spaceTrack/detail',
stDataDelete: '/sspaceTrackt/delete',
exportSpiderData: '/export/downloadFile',
esaList: '/esa/list',
esaMissionDetail: '/esa/missionDetail',
esaStationDetail: '/esa/stationDetail',
} as const
// 爬虫数据相关接口
dsnList: "/dsn/list",
dsnDetail: "/dsn/detail",
dsnDataDelete: "/dsn/delete",
ituList: "/itu/list",
ituDetail: "/itu/detail",
ituDataDelete: "/itu/delete",
stList: "/spaceTrack/list",
stDetail: "/spaceTrack/detail",
stDataDelete: "/sspaceTrackt/delete",
exportSpiderData: "/export/downloadFile",
esaList: "/esa/list",
esaMissionDetail: "/esa/missionDetail",
esaStationDetail: "/esa/stationDetail",
} as const;
// 爬虫数据相关接口
export const dataApi = {
dataStatistics: '/statistic/dataStatistics',
taskStatistics: '/statistic/taskStatistics',
performanceStatistics: '/statistic/performanceStatistics',
allSpiderTaskStatistics: '/statistic/getAllTaskStatistics',
} as const
dataStatistics: "/statistic/dataStatistics",
taskStatistics: "/statistic/taskStatistics",
performanceStatistics: "/statistic/performanceStatistics",
allSpiderTaskStatistics: "/statistic/getAllTaskStatistics",
} as const;
import { request, POST } from '@/utils/request'
import type { ApiResponse, QueryParams ,UserQueryParams } from '@/utils/request'
import { systemApi, scrapydServerApi, spiderApi } from './apiPaths'
import { request, POST } from "@/utils/request";
import type { ApiResponse, QueryParams, UserQueryParams } from "@/utils/request";
import { systemApi, scrapydServerApi, spiderApi, spiderTaskApi } from "./apiPaths";
// 获取系统信息
export function getSystemInfo(): Promise<ApiResponse> {
return request({
url: systemApi.systemInfo,
method: POST
}) as unknown as Promise<ApiResponse>
method: POST,
}) as unknown as Promise<ApiResponse>;
}
// 获取系统数据
......@@ -15,16 +15,16 @@ export function getSystemData(data: QueryParams): Promise<ApiResponse> {
return request({
url: systemApi.systemData,
method: POST,
data
}) as unknown as Promise<ApiResponse>
data,
}) as unknown as Promise<ApiResponse>;
}
// 获取系统配置
export function getSystemConfig(): Promise<ApiResponse> {
return request({
url: systemApi.systemConfig,
method: POST
}) as unknown as Promise<ApiResponse>
method: POST,
}) as unknown as Promise<ApiResponse>;
}
// 更新系统配置
......@@ -32,8 +32,8 @@ export function updateSystemConfig(data: Record<string, any>): Promise<ApiRespon
return request({
url: systemApi.systemConfig,
method: POST,
data
}) as unknown as Promise<ApiResponse>
data,
}) as unknown as Promise<ApiResponse>;
}
// 获取登录历史
......@@ -41,22 +41,39 @@ export function getLoginHistory(params: QueryParams): Promise<ApiResponse> {
return request({
url: systemApi.loginHistoryList,
method: POST,
params
}) as unknown as Promise<ApiResponse>
params,
}) as unknown as Promise<ApiResponse>;
}
export const getScrapydServerList = (): Promise<ApiResponse> => {
return request({
url: scrapydServerApi.getScrapydServerPage,
method: POST,
}) as unknown as Promise<ApiResponse>
}
}) as unknown as Promise<ApiResponse>;
};
// 爬虫列表
export function getSpiderList(data: UserQueryParams) {
return request({
url: spiderApi.spiderList,
method: POST,
data
}) as unknown as Promise<ApiResponse>
}
\ No newline at end of file
data,
}) as unknown as Promise<ApiResponse>;
}
//添加卫星编号
export function addSateNo(data: { id: string[] }) {
return request({
url: spiderTaskApi.addSateNo,
method: POST,
data,
}) as unknown as Promise<ApiResponse>;
}
//获取编号列表
export function getSateIdList() {
return request({
url: spiderTaskApi.getSateIdList,
method: POST,
}) as unknown as Promise<ApiResponse>;
}
<script setup lang="ts">
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import { computed } from "vue";
import { useRoute } from "vue-router";
const route = useRoute()
const key = computed(() => route.path)
const route = useRoute();
const key = computed(() => route.path);
</script>
<template>
......@@ -28,7 +28,7 @@ const key = computed(() => route.path)
overflow-y: auto; /* 修改为auto允许垂直滚动 */
overflow-x: hidden; /* 防止水平滚动 */
}
.fixed-header+.app-main {
.fixed-header + .app-main {
padding-top: 50px;
}
</style>
<script setup lang="ts">
import settings from '@/settings'
import settings from "@/settings";
const props = defineProps({
collapse: {
type: Boolean,
required: true
}
})
required: true,
},
});
const title = settings.title || 'fk spider web'
const title = settings.title || "fk spider web";
// Logo 图片可在后续使用实际的 logo 文件
// const logo = require('@/assets/logo.png')
const logo = '' // 暂时为空
const logo = ""; // 暂时为空
</script>
<template>
<div class="sidebar-logo-container" :class="{'collapse': collapse}">
<div class="sidebar-logo-container" :class="{ collapse: collapse }">
<transition name="sidebarLogoFade">
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo" alt="logo">
<img v-if="logo" :src="logo" class="sidebar-logo" alt="logo" />
<h1 v-else class="sidebar-title">{{ title }}</h1>
</router-link>
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo" alt="logo">
<img v-if="logo" :src="logo" class="sidebar-logo" alt="logo" />
<h1 class="sidebar-title">{{ title }}</h1>
</router-link>
</transition>
......
import { createApp } from 'vue'
import 'virtual:uno.css'
import App from './App.vue'
import router from './router'
import pinia from './store'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { createApp } from "vue";
import "virtual:uno.css";
import App from "./App.vue";
import router from "./router";
import pinia from "./store";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import {
UserFilled,
Lock,
......@@ -31,46 +31,47 @@ import {
Message,
DataAnalysis,
Notebook,
Setting
} from '@element-plus/icons-vue'
import 'normalize.css/normalize.css'
import './styles/index.scss'
import './style.css'
import './permission' // 权限控制
Setting,
} from "@element-plus/icons-vue";
import "normalize.css/normalize.css";
import "./styles/index.scss";
import "./style.css";
import "./permission"; // 权限控制
import "animate.css";
const app = createApp(App)
const app = createApp(App);
// 注册 Element Plus 图标
app.component('Monitor', Monitor)
app.component('Document', Document)
app.component('List', List)
app.component('Key', Key)
app.component('House', House)
app.component('Clock', Clock)
app.component('Message', Message)
app.component('DataAnalysis', DataAnalysis)
app.component('Notebook', Notebook)
app.component('Setting', Setting)
app.component("Monitor", Monitor);
app.component("Document", Document);
app.component("List", List);
app.component("Key", Key);
app.component("House", House);
app.component("Clock", Clock);
app.component("Message", Message);
app.component("DataAnalysis", DataAnalysis);
app.component("Notebook", Notebook);
app.component("Setting", Setting);
app.component('UserFilled', UserFilled)
app.component('Lock', Lock)
app.component('View', View)
app.component('Hide', Hide)
app.component('CaretBottom', CaretBottom)
app.component('HomeFilled', HomeFilled)
app.component('Menu', Menu)
app.component('Tickets', Tickets)
app.component('Folder', Folder)
app.component('Files', Files)
app.component('Loading', Loading)
app.component('VideoPlay', VideoPlay)
app.component('Refresh', Refresh)
app.component('Back', Back)
app.component('Upload', Upload)
app.component('Plus', Plus)
app.component("UserFilled", UserFilled);
app.component("Lock", Lock);
app.component("View", View);
app.component("Hide", Hide);
app.component("CaretBottom", CaretBottom);
app.component("HomeFilled", HomeFilled);
app.component("Menu", Menu);
app.component("Tickets", Tickets);
app.component("Folder", Folder);
app.component("Files", Files);
app.component("Loading", Loading);
app.component("VideoPlay", VideoPlay);
app.component("Refresh", Refresh);
app.component("Back", Back);
app.component("Upload", Upload);
app.component("Plus", Plus);
app.use(router)
app.use(pinia)
app.use(ElementPlus, { size: 'default' })
app.use(router);
app.use(pinia);
app.use(ElementPlus, { size: "default" });
app.mount('#app')
app.mount("#app");
......@@ -58,3 +58,7 @@ body {
display: table;
clear: both;
}
:root {
--animate-duration: 0.15s !important;
}
\ No newline at end of file
import axios from 'axios'
import { ElMessage } from 'element-plus'
import { getToken, removeToken } from '@/utils/auth'
import axios from "axios";
import { ElMessage } from "element-plus";
import { getToken, removeToken } from "@/utils/auth";
// 创建axios实例
const instance = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API || '',
baseURL: import.meta.env.VITE_APP_BASE_API || "",
timeout: 10000, // 毫秒
headers: {
'Content-Type': 'application/zip; charset=utf-8'
}
})
"Content-Type": "application/zip; charset=utf-8",
},
});
// 请求拦截器
instance.interceptors.request.use(
(config) => {
const token = getToken()
const token = getToken();
if (token) {
// 保持与原项目相同的Token头设置
config.headers['Token'] = token
config.headers["Token"] = token;
}
return config
return config;
},
(err) => {
console.log('请求出错')
return Promise.reject(err)
console.log("请求出错");
return Promise.reject(err);
}
)
);
// 响应拦截器
instance.interceptors.response.use(
(res) => {
let result = res.data
let result = res.data;
if (typeof res.data === 'string') {
if (typeof res.data === "string") {
try {
result = JSON.parse(res.data)
result = JSON.parse(res.data);
} catch (e) {
// 不是JSON格式,保持原样
}
} else {
// 4000 token无效或者过期
if (result.code === 4000) {
removeToken()
window.location.reload()
removeToken();
window.location.reload();
}
if (result.code === 0) {
result.ok = true
result.ok = true;
} else {
result.ok = false
ElMessage.error(result.msg || '请求失败')
result.ok = false;
ElMessage.error(result.msg || "请求失败");
}
}
return result
return result;
},
(err) => {
ElMessage.error('网络请求出错, 请检查网络')
return Promise.reject(err)
ElMessage.error("网络请求出错, 请检查网络");
return Promise.reject(err);
}
)
);
// HTTP 请求方法常量
export const GET = 'get'
export const POST = 'post'
export const PUT = 'put'
export const DELETE = 'delete'
export const GET = "get";
export const POST = "post";
export const PUT = "put";
export const DELETE = "delete";
// 请求函数类型
export interface RequestConfig {
url: string;
method: 'get' | 'post' | 'put' | 'delete';
method: "get" | "post" | "put" | "delete";
params?: any;
data?: any;
headers?: Record<string, string>;
}
export interface ApiResponse<T = any> {
code: number
data: T
message: string
code: number;
data: T;
message: string;
}
export interface QueryParams {
page?: number
size?: number
project?: string
spider?: string
scrapydServerId?: string
order_prop?: string
order_type?: string
status?: any
job?:any
page?: number;
size?: number;
project?: string;
spider?: string;
scrapydServerId?: string;
order_prop?: string;
order_type?: string;
status?: any;
job?: any;
}
// 请求函数
export const request = (config: RequestConfig) => {
return instance(config);
}
};
export interface UserQueryParams {
page?: number
size?: number
username?: string
nickname?: string
password?: string
role?: number
status?: any
id?: number
ids?: any
scrapydServerId?: string
project?: string
spiders?: any
times?: any
job_id?: string
scrapyd_server_id?: string
schedule_type?: string
spider?: string
cron?: string
options?: string
sat_name?: string
ntc_id?: string
filters?: any
norad_cat_id?: string
object_name?: string
name?:string
spacecraft?:string
station?:string
}
\ No newline at end of file
page?: number;
size?: number;
username?: string;
nickname?: string;
password?: string;
role?: number;
status?: any;
id?: number;
ids?: any;
scrapydServerId?: string;
project?: string;
spiders?: any;
times?: any;
job_id?: string;
scrapyd_server_id?: string;
schedule_type?: string;
spider?: string;
cron?: string;
options?: string;
sat_name?: string;
ntc_id?: string;
filters?: any;
norad_cat_id?: string;
object_name?: string;
name?: string;
spacecraft?: string;
station?: string;
interval?: number[];
}
......@@ -11,16 +11,15 @@
<!-- <allDataTab v-if="mode === '综合数据'">
</allDataTab> -->
<!-- DSN数据页面组件 -->
<dsnDataTab v-if="mode === 'DSN数据'"> </dsnDataTab>
<dsnDataTab v-if="mode === 'DSN数据'"></dsnDataTab>
<!-- ITU数据页面组件 -->
<ituDataTab v-if="mode === 'ITU数据'"> </ituDataTab>
<ituDataTab v-if="mode === 'ITU数据'"></ituDataTab>
<!-- ST数据页面组件 -->
<stDataTab v-if="mode === 'ST数据'"> </stDataTab>
<stDataTab v-if="mode === 'ST数据'"></stDataTab>
<!-- ESA数据页面组件 -->
<esDataTab v-if="mode === 'ESA数据'"> </esDataTab>
<esDataTab v-if="mode === 'ESA数据'"></esDataTab>
<exportDialog v-model:dialogVisible="showDeleteDialog" @confirm="handleExportConfirm" />
</div>
<exportDialog v-model:dialogVisible="showDeleteDialog" @confirm="handleExportConfirm" />
</template>
<script setup lang="ts">
......
<template>
<div class="No-Item" :class="{ selected: isSelected }" @click="selectNo">
<!-- 新增选中标识,增强视觉反馈 -->
<span class="selected-icon" v-if="isSelected"></span>
{{ props.No }}
</div>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
const props = defineProps<{
No: string | number;
resetSelected?: boolean;
}>();
const isSelected = ref(false);
const emit = defineEmits<{
(e: "addNo", sateNo: string | number): void;
(e: "delNo", sateNo: string | number): void;
}>();
const selectNo = () => {
isSelected.value = !isSelected.value;
isSelected.value ? emit("addNo", props.No) : emit("delNo", props.No);
};
watch(
() => props.resetSelected,
(newVal) => {
if (newVal) isSelected.value = false;
},
{ immediate: true }
);
</script>
<style scoped>
.No-Item {
width: 100% !important;
letter-spacing: 6px;
background-color: #e8f4f8;
font-size: 16px;
font-weight: 600;
color: #192a3e;
height: 32px;
border-radius: 8px;
display: flex;
align-items: center;
padding: 0 16px;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
box-sizing: border-box;
user-select: none;
border: 1px solid transparent;
}
.No-Item:not(.selected):hover {
background-color: #f0f8fb;
border-color: #4299e1;
}
.selected {
background-color: #4299e1;
color: #ffffff;
box-shadow: 0 4px 12px rgba(66, 153, 225, 0.3);
border-color: #2563eb;
}
.selected-icon {
margin-right: 8px;
font-size: 14px;
font-weight: bold;
color: #ffffff;
}
</style>
......@@ -13,13 +13,19 @@
:header-cell-style="{ textAlign: 'center' }"
:cell-style="{ textAlign: 'center' }"
:row-style="{ height: '60px' }"
v-loading="tableData.length == 0"
element-loading-background="rgba(48, 65, 86, 0.7)"
>
<!-- <el-table-column type="selection" width="40" height="40" /> -->
<el-table-column property="number" label="序号" type="index" width="80" />
<el-table-column property="spider" label="爬虫名称" show-overflow-tooltip />
<el-table-column label="查看任务">
<el-table-column property="spider" label="爬虫代号" show-overflow-tooltip />
<el-table-column property="spider_name" label="爬虫名称" show-overflow-tooltip />
<el-table-column property="spider_info" label="任务描述" show-overflow-tooltip />
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" plain @click="handleDetails(scope.row)"> 任务 </el-button>
<div class="btn-group">
<el-button type="primary" plain @click="handleDetails(scope.row)">查看任务</el-button>
<el-button type="primary" plain @click="handleEditSateId">编辑</el-button>
</div>
</template>
</el-table-column>
</el-table>
......@@ -32,14 +38,56 @@
@pagination="getData"
/>
</div>
//编辑弹窗
<el-dialog style="z-index: 999999" draggable v-model="editDialogVisible" title="指定下载以下卫星" width="400">
<div class="No-Content" ref="noContentRef">
<TransitionGroup
enter-active-class="animate__animated animate__slideInRight"
leave-active-class="animate__animated animate__slideOutRight"
enter-from-class="animate__opacity-0"
leave-to-class="animate__opacity-0"
>
<NoItem
v-for="(no, index) in sateNoList"
:key="index"
class="no-item-row"
:-no="no"
@add-no="handleSateAdd"
@del-no="handleSateDel"
:reset-selected="resetSelected"
></NoItem>
/>
</TransitionGroup>
</div>
<div class="footer">
<div class="No">
<span style="color: white; font-weight: 500">NORAD编号:</span>
<el-input
@input="handleNumInput"
maxlength="5"
placeholder="请输入编号"
v-model="curSateNo"
style="width: 100px"
></el-input>
</div>
<div class="btn">
<el-button :disabled="curSateNo == ''" plain type="primary" @click="addNoToList">添加</el-button>
<el-button plain type="danger" @click="delNoList">删除</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { ref, onMounted, nextTick } from "vue";
import Pagination from "@/components/pagination/index.vue";
import { useRouter } from "vue-router";
import { getSpiderList } from "@/api/system.ts";
import { getSpiderList, addSateNo, getSateIdList } from "@/api/system.ts";
import NoItem from "./components/NoItem.vue";
import { ElMessage } from "element-plus";
const router = useRouter();
const tableData = ref([]);
const pageObj = ref({
......@@ -48,7 +96,6 @@ const pageObj = ref({
pageNo: 1,
});
const handleDetails = (row: any) => {
// console.log(row);
router.push({
path: "/osTaskInformation/list",
query: {
......@@ -64,6 +111,100 @@ const getData = async () => {
tableData.value = res.data;
};
const editDialogVisible = ref(false);
const sateNoList = ref<string[]>([]);
const curSateNo = ref("");
const curSelSateNo = ref<(string | number)[]>([]);
const resetSelected = ref(false);
//添加选中项到当前列表
const handleSateAdd = (no: string | number) => {
curSelSateNo.value.push(padZeroTo5Digits(no));
};
//从当前列表删除选中项
const handleSateDel = (no: string | number) => {
curSelSateNo.value = curSelSateNo.value.filter((item) => item != no);
};
//添加编号到列表并整体发到后端
const addNoToList = async () => {
try {
if (sateNoList.value.includes(padZeroTo5Digits(curSateNo.value))) {
ElMessage.error("当前编号已存在,请重新输入");
curSateNo.value = "";
return;
}
sateNoList.value.push(padZeroTo5Digits(curSateNo.value));
const res = await addSateNo({ id: sateNoList.value });
if (res.code === 0) {
ElMessage.success("添加编号成功");
} else {
sateNoList.value.pop();
ElMessage.error(res.message);
}
curSateNo.value = "";
scrollToBottom();
} catch (error: any) {
ElMessage.error(error.message);
}
};
//删除选中编号
const delNoList = async () => {
try {
sateNoList.value = sateNoList.value.filter((item) => !curSelSateNo.value.includes(item));
const res = await addSateNo({ id: sateNoList.value as string[] });
if (res.code === 0) {
}
resetSelected.value = true;
curSelSateNo.value = [];
nextTick(() => {
resetSelected.value = false;
});
} catch (error: any) {}
};
const noContentRef = ref<HTMLDivElement | null>(null);
//新添加滚动到底部
const scrollToBottom = () => {
nextTick(() => {
const container = noContentRef.value;
if (container) {
container.scrollTop = container.scrollHeight - container.clientHeight;
}
});
};
const handleNumInput = (value: string) => {
const filtered = value.replace(/\D/g, "");
curSateNo.value = filtered.slice(0, 5);
};
//编号格式化
const padZeroTo5Digits = (num: number | string): string => {
const numStr = String(num);
const pureNumStr = numStr.replace(/\D/g, "");
return pureNumStr.padStart(5, "0").slice(-5);
};
const handleEditSateId = async () => {
editDialogVisible.value = true;
try {
const res = await getSateIdList();
if (res.code === 0) {
sateNoList.value = res.data || [];
}
console.log(res);
} catch (error: any) {
ElMessage.error(error.message);
}
};
onMounted(() => {
getData();
});
......@@ -94,4 +235,68 @@ onMounted(() => {
.divider {
margin-top: 15px;
}
.No-Content {
width: 100%;
height: 200px;
background-color: #1d5484;
border: 4px solid #083b67;
display: flex;
padding: 10px;
box-sizing: border-box;
flex-direction: column;
gap: 10px;
overflow-y: auto;
overflow-x: hidden;
}
.footer {
margin-top: 20px;
display: flex;
justify-content: space-between;
}
.No {
display: flex;
align-items: center;
}
.No-Content::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.No-Content::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.No-Content::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.1);
}
.No-Content::-webkit-scrollbar-thumb:hover {
background: #999;
}
.No-Content::-webkit-scrollbar-button {
display: none;
}
.No-Content {
scrollbar-width: thin;
scrollbar-color: #c1c1c1 #f1f1f1;
}
.btn-group {
display: flex;
gap: 8px;
white-space: nowrap;
justify-content: center;
}
.btn-group .el-button {
flex-shrink: 0;
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment