|
|
@@ -0,0 +1,344 @@
|
|
|
+<template>
|
|
|
+ <div class="crossing-list-panel">
|
|
|
+
|
|
|
+ <div class="header-section">
|
|
|
+ <TechFilterBar :config="filterConfig" v-model="searchParams" @search="handleSearch" @reset="handleReset" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="table-section">
|
|
|
+
|
|
|
+ <div class="tech-loading-mask" v-show="loading">
|
|
|
+ <div class="spinner"></div>
|
|
|
+ <span class="loading-text">正在同步路口数据...</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <TechTable :columns="tableColumns" :data="tableList" height="100%">
|
|
|
+ <template #phaseStatus="{ row }">
|
|
|
+ <div class="mini-chart-wrapper">
|
|
|
+ <SignalTimingChart :phaseData="row.phaseData" :cycleLength="row.cycle" :currentTime="0"
|
|
|
+ :isMiniMode="true" />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #actions="{ row }">
|
|
|
+ <div class="action-buttons">
|
|
|
+ <span class="btn primary" @click="handleAction('upgrade', row)">升级</span>
|
|
|
+ <span class="btn primary" @click="handleAction('restart', row)">重启</span>
|
|
|
+ <span class="btn primary" @click="handleAction('view', row)">查看</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </TechTable>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="footer-section">
|
|
|
+ <TechPagination :total="pagination.total" :currentPage.sync="pagination.currentPage"
|
|
|
+ :pageSize.sync="pagination.pageSize" @current-change="fetchData" @size-change="handleSizeChange" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+// 引入四个核心组件 (请确保这里的路径与你的实际项目路径一致)
|
|
|
+import TechFilterBar from '@/components/ui/TechFilterBar.vue';
|
|
|
+import TechTable from '@/components/ui/TechTable.vue';
|
|
|
+import SignalTimingChart from '@/components/ui/SignalTimingChart.vue';
|
|
|
+import TechPagination from '@/components/ui/TechPagination.vue';
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'IntersectionManage',
|
|
|
+ components: {
|
|
|
+ TechFilterBar,
|
|
|
+ TechTable,
|
|
|
+ SignalTimingChart,
|
|
|
+ TechPagination
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ loading: false, // 控制加载遮罩层的显示与隐藏
|
|
|
+
|
|
|
+ // === 表单查询相关 ===
|
|
|
+ searchParams: {
|
|
|
+ name: '',
|
|
|
+ subArea: '',
|
|
|
+ status: '',
|
|
|
+ timeOffset: '',
|
|
|
+ isKey: ''
|
|
|
+ },
|
|
|
+ // 查询栏的动态配置
|
|
|
+ filterConfig: [
|
|
|
+ { type: 'input', label: '路口名称', key: 'name', placeholder: '请输入路口名' },
|
|
|
+ {
|
|
|
+ type: 'select', label: '子区', key: 'subArea',
|
|
|
+ options: [
|
|
|
+ { label: '全部', value: '' },
|
|
|
+ { label: '石景山区', value: 'shijingshan' },
|
|
|
+ { label: '海淀区', value: 'haidian' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'select', label: '状态', key: 'status',
|
|
|
+ options: [
|
|
|
+ { label: '全部', value: '' },
|
|
|
+ { label: '在线', value: 'online' },
|
|
|
+ { label: '离线', value: 'offline' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'select', label: '时间偏差', key: 'timeOffset',
|
|
|
+ options: [
|
|
|
+ { label: '无偏差', value: 'none' },
|
|
|
+ { label: '有偏差', value: 'has' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'select', label: '关键路口', key: 'isKey',
|
|
|
+ options: [
|
|
|
+ { label: '请选择', value: '' },
|
|
|
+ { label: '是', value: 'yes' },
|
|
|
+ { label: '否', value: 'no' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ],
|
|
|
+
|
|
|
+ // === 表格与分页相关 ===
|
|
|
+ tableColumns: [
|
|
|
+ { label: '序号', key: 'index', width: '60px' },
|
|
|
+ { label: '路口名', key: 'name', width: '150px' },
|
|
|
+ { label: '子区', key: 'subArea', width: '100px' },
|
|
|
+ { label: 'IP地址', key: 'ip', width: '130px' },
|
|
|
+ { label: '状态', key: 'status', width: '80px' },
|
|
|
+ { label: '时间偏差', key: 'timeOffset', width: '100px' },
|
|
|
+ { label: '周期', key: 'cycle', width: '80px' },
|
|
|
+ { label: '相位状态', key: 'phaseStatus', width: '280px' },
|
|
|
+ { label: '版本信息', key: 'version', width: '180px' },
|
|
|
+ { label: '操作', key: 'actions', width: '150px' }
|
|
|
+ ],
|
|
|
+ tableList: [],
|
|
|
+ pagination: {
|
|
|
+ total: 0,
|
|
|
+ currentPage: 1,
|
|
|
+ pageSize: 10
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.fetchData(); // 初始化拉取数据
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ // 模拟接口请求
|
|
|
+ async fetchData() {
|
|
|
+ this.loading = true; // 开启 Loading 遮罩
|
|
|
+ try {
|
|
|
+ const apiParams = {
|
|
|
+ ...this.searchParams,
|
|
|
+ page: this.pagination.currentPage,
|
|
|
+ size: this.pagination.pageSize
|
|
|
+ };
|
|
|
+ console.log('发起请求,参数:', apiParams);
|
|
|
+
|
|
|
+ // 模拟网络延迟 (500ms),让你能看清 Loading 动画
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 500));
|
|
|
+
|
|
|
+ // 模拟生成列表数据
|
|
|
+ this.tableList = Array.from({ length: this.pagination.pageSize }).map((_, index) => {
|
|
|
+ const actualIndex = (this.pagination.currentPage - 1) * this.pagination.pageSize + index + 1;
|
|
|
+ return {
|
|
|
+ id: `uuid-${Date.now()}-${index}`,
|
|
|
+ index: actualIndex,
|
|
|
+ name: '北京路南京路',
|
|
|
+ subArea: '石景山区',
|
|
|
+ ip: '41.32.32.131',
|
|
|
+ status: '在线',
|
|
|
+ timeOffset: '无偏差',
|
|
|
+ cycle: 120,
|
|
|
+ version: '54827345623452756',
|
|
|
+ // 模拟相位图表数据
|
|
|
+ phaseData: [
|
|
|
+ [0, 0, 30, '阶段1', 30, 'green', 'UP'],
|
|
|
+ [0, 30, 35, '阶段2', 5, 'yellow', ''],
|
|
|
+ [0, 35, 60, '阶段3', 25, 'green', 'TURN_LEFT']
|
|
|
+ ]
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ this.pagination.total = 32;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取列表失败', error);
|
|
|
+ } finally {
|
|
|
+ this.loading = false; // 关闭 Loading 遮罩
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 搜索事件
|
|
|
+ handleSearch() {
|
|
|
+ this.pagination.currentPage = 1;
|
|
|
+ this.fetchData();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 重置事件
|
|
|
+ handleReset() {
|
|
|
+ this.pagination.currentPage = 1;
|
|
|
+ this.fetchData();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 切换每页条数
|
|
|
+ handleSizeChange() {
|
|
|
+ this.pagination.currentPage = 1;
|
|
|
+ this.fetchData();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 统一处理操作列按钮点击
|
|
|
+ handleAction(type, row) {
|
|
|
+ if (type === 'upgrade') {
|
|
|
+ console.log('执行升级:', row.name);
|
|
|
+ } else if (type === 'restart') {
|
|
|
+ console.log('执行重启:', row.name);
|
|
|
+ } else if (type === 'view') {
|
|
|
+ console.log('查看详情:', row.name);
|
|
|
+ this.$emit('crossing-view-detail', row);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+/* 页面主容器:深色背景,铺满视口 */
|
|
|
+.crossing-list-panel {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ box-sizing: border-box;
|
|
|
+ color: #fff;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+/* 顶部查询区域 */
|
|
|
+.header-section {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* ================= 表格主体区域与 Loading 遮罩 ================= */
|
|
|
+.table-section {
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+ padding: 10px 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+/* 科技风 Loading 遮罩 */
|
|
|
+.tech-loading-mask {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background-color: rgba(11, 22, 44, 0.7);
|
|
|
+ /* 半透明深色底 */
|
|
|
+ backdrop-filter: blur(3px);
|
|
|
+ /* 增加背景模糊质感 */
|
|
|
+ z-index: 50;
|
|
|
+ /* 层级高于表格内容 */
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.spinner {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ border: 3px solid rgba(77, 168, 255, 0.1);
|
|
|
+ border-top-color: #4da8ff;
|
|
|
+ /* 蓝色高亮条 */
|
|
|
+ border-radius: 50%;
|
|
|
+ animation: spin 1s linear infinite;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ box-shadow: 0 0 15px rgba(77, 168, 255, 0.3);
|
|
|
+}
|
|
|
+
|
|
|
+.loading-text {
|
|
|
+ color: #4da8ff;
|
|
|
+ font-size: 14px;
|
|
|
+ letter-spacing: 2px;
|
|
|
+ animation: pulse 1.5s ease-in-out infinite;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes spin {
|
|
|
+ to {
|
|
|
+ transform: rotate(360deg);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes pulse {
|
|
|
+
|
|
|
+ 0%,
|
|
|
+ 100% {
|
|
|
+ opacity: 0.5;
|
|
|
+ text-shadow: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ 50% {
|
|
|
+ opacity: 1;
|
|
|
+ text-shadow: 0 0 8px rgba(77, 168, 255, 0.8);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 底部分页区域 */
|
|
|
+.footer-section {
|
|
|
+ margin-top: 16px;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* ================= 针对插槽内容的样式 ================= */
|
|
|
+
|
|
|
+/* 控制相位图表在表格内的高度 */
|
|
|
+.mini-chart-wrapper {
|
|
|
+ height: 28px;
|
|
|
+ width: 100%;
|
|
|
+ border-radius: 2px;
|
|
|
+ overflow: hidden;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+/* 操作列按钮布局 */
|
|
|
+.action-buttons {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.action-buttons .btn {
|
|
|
+ font-size: 13px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+ user-select: none;
|
|
|
+}
|
|
|
+
|
|
|
+/* 主操作按钮 (蓝色) */
|
|
|
+.action-buttons .btn.primary {
|
|
|
+ color: #4da8ff;
|
|
|
+}
|
|
|
+
|
|
|
+.action-buttons .btn.primary:hover {
|
|
|
+ color: #80c4ff;
|
|
|
+ text-decoration: underline;
|
|
|
+}
|
|
|
+
|
|
|
+/* 默认操作按钮 (灰色/淡蓝色) */
|
|
|
+.action-buttons .btn.default {
|
|
|
+ color: #a3b8cc;
|
|
|
+}
|
|
|
+
|
|
|
+.action-buttons .btn.default:hover {
|
|
|
+ color: #ffffff;
|
|
|
+ text-decoration: underline;
|
|
|
+}
|
|
|
+</style>
|