feat(alarm): ICC 报警事件订阅与设备报警监控/报警派单 - 重建 device_alarm 表(迁移20261024_icc_event_alarm.sql:alarm_code 唯一、uuid、line 关联、客户、等级、事件标记、alarm_time 等 19 字段与 4 索引)并新建 alarm_dispatch 派单表(15 字段);订阅服务 IccEventSubscribeService 复用 OAuth2 鉴权向 ICC 注册报警事件回调(POST subscribe/mqinfo,monitor 带 token、同名覆盖保活),每 30 分钟保活任务(sys_job 种子);回调 POST /api/icc-event/callback 无鉴权仅 URL token 校验,兼容单条/数组事件解析、按 alarmCode 幂等 upsert、匹配 electric_line.line_code 回填线路客户;新增 GET /api/device-alarms(列表+今日/严重/未派单统计+客户/等级/关键词筛选)与 POST /api/device-alarms/:id/dispatch(AD 派单号、同报警重复派单拦截、电工在岗审核校验、NoticeService 站内通知);GET /api/alarm-dispatches 台账(联电工/客户名);前端设备报警监控页(统计卡/筛选/表格/发起报警派单弹窗/30 秒轮询+严重报警红灯闪烁与 Web Audio 蜂鸣/订阅状态弹窗)与报警派单台账页+路由;菜单 63/64、role_menu、sys_job 种子;同步 db_init/db_seed 与设计文档;端到端验证通过
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
This commit is contained in:
1 parent
c8a4330f3b
commit
44fc9a1d54
24 files changed
+2825
-126
No files matched your search
@@ -0,0 +1,136 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// ==================== 设备报警 ====================
|
||||
|
||||
export interface DeviceAlarmRow {
|
||||
id: number;
|
||||
alarmCode?: string;
|
||||
uuid?: string;
|
||||
lineId?: number;
|
||||
lineCode?: string;
|
||||
lineName?: string;
|
||||
customerId?: number;
|
||||
nodeCode?: string;
|
||||
deviceCode?: string;
|
||||
deviceName?: string;
|
||||
orgCode?: string;
|
||||
orgName?: string;
|
||||
alarmType?: number | string;
|
||||
// 报警等级:1 严重 / 2 一般 / 3 提示
|
||||
alarmGrade?: number;
|
||||
alarmStat?: number;
|
||||
// 是否事件(1 事件 / 0 报警)
|
||||
isEvent?: number;
|
||||
alarmTime?: string;
|
||||
alarmValue?: string;
|
||||
syncedAt?: string;
|
||||
// 联表补全字段(存在性不确定,使用时防御性取用)
|
||||
customerName?: string;
|
||||
}
|
||||
|
||||
export interface DeviceAlarmSummary {
|
||||
todayCount: number;
|
||||
gradeOneCount: number;
|
||||
undispatchedCount: number;
|
||||
}
|
||||
|
||||
export interface DeviceAlarmListResult {
|
||||
list: DeviceAlarmRow[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
summary?: DeviceAlarmSummary;
|
||||
}
|
||||
|
||||
export interface DeviceAlarmQuery {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
customerId?: number;
|
||||
alarmGrade?: number;
|
||||
keyword?: string;
|
||||
}
|
||||
|
||||
export interface DeviceAlarmDispatchParams {
|
||||
workerId: number;
|
||||
faultDesc?: string;
|
||||
}
|
||||
|
||||
export interface DeviceAlarmDispatchResult {
|
||||
id?: number;
|
||||
alarmId?: number;
|
||||
dispatchNo?: string;
|
||||
workerId?: number;
|
||||
workerName?: string;
|
||||
status?: number;
|
||||
faultDesc?: string;
|
||||
dispatchedAt?: string;
|
||||
}
|
||||
|
||||
export const deviceAlarmApi = {
|
||||
getList: (params: DeviceAlarmQuery) =>
|
||||
request.get<DeviceAlarmListResult>('/device-alarms', { params }),
|
||||
|
||||
dispatch: (id: number, body: DeviceAlarmDispatchParams) =>
|
||||
request.post<DeviceAlarmDispatchResult>(`/device-alarms/${id}/dispatch`, body),
|
||||
};
|
||||
|
||||
// ==================== 报警派单台账(Task7 使用) ====================
|
||||
|
||||
export interface AlarmDispatchRow {
|
||||
id: number;
|
||||
alarmId?: number;
|
||||
dispatchNo?: string;
|
||||
lineId?: number;
|
||||
lineCode?: string;
|
||||
lineName?: string;
|
||||
customerId?: number;
|
||||
customerName?: string;
|
||||
alarmTime?: string;
|
||||
// 报警等级:1 严重 / 2 一般 / 3 提示
|
||||
alarmGrade?: number;
|
||||
faultDesc?: string;
|
||||
dispatcher?: string;
|
||||
workerId?: number;
|
||||
workerName?: string;
|
||||
// 状态:0 已派发 / 1 已处理
|
||||
status?: number;
|
||||
dispatchedAt?: string;
|
||||
createdAt?: string;
|
||||
completedAt?: string;
|
||||
}
|
||||
|
||||
export interface AlarmDispatchQuery {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
status?: number | string;
|
||||
keyword?: string;
|
||||
}
|
||||
|
||||
export interface AlarmDispatchListResult {
|
||||
list: AlarmDispatchRow[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
export const alarmDispatchApi = {
|
||||
getList: (params: AlarmDispatchQuery) =>
|
||||
request.get<AlarmDispatchListResult>('/alarm-dispatches', { params }),
|
||||
};
|
||||
|
||||
// ==================== ICC 事件订阅(本页订阅状态提示,可选) ====================
|
||||
|
||||
export interface IccEventStatus {
|
||||
name?: string;
|
||||
monitorUrl?: string;
|
||||
magic?: string;
|
||||
status?: string;
|
||||
registerResult?: string;
|
||||
registeredAt?: string;
|
||||
lastError?: string;
|
||||
}
|
||||
|
||||
export const iccEventApi = {
|
||||
subscribe: () => request.post('/icc-event/subscribe'),
|
||||
status: () => request.get<IccEventStatus>('/icc-event/subscribe-status'),
|
||||
};
|
||||
@@ -175,6 +175,12 @@ const routes: RouteRecordRaw[] = [
|
||||
component: () => import("@/views/dispatch/maintenance/index.vue"),
|
||||
meta: { requiresAuth: true, title: "维保派单", permission: "dispatch:maintenance" }
|
||||
},
|
||||
{
|
||||
path: "dispatch/alarm",
|
||||
name: "AlarmDispatchLedger",
|
||||
component: () => import("@/views/dispatch/alarm/index.vue"),
|
||||
meta: { requiresAuth: true, title: "报警派单台账", permission: "alarm-dispatch:list" }
|
||||
},
|
||||
{
|
||||
path: "device",
|
||||
name: "Device",
|
||||
@@ -199,6 +205,12 @@ const routes: RouteRecordRaw[] = [
|
||||
component: () => import("@/views/device/gateway/index.vue"),
|
||||
meta: { requiresAuth: true, title: "智电改造网关台账", permission: "device:list" }
|
||||
},
|
||||
{
|
||||
path: "device/alarm",
|
||||
name: "DeviceAlarm",
|
||||
component: () => import("@/views/device/alarm/index.vue"),
|
||||
meta: { requiresAuth: true, title: "设备报警监控", permission: "device:list" }
|
||||
},
|
||||
{
|
||||
path: "base",
|
||||
name: "Base",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,511 @@
|
||||
<template>
|
||||
<div class="alarm-dispatch-page">
|
||||
<!-- 顶部标题 -->
|
||||
<div class="page-header">
|
||||
<div class="breadcrumb">
|
||||
<span>运营管控体系</span>
|
||||
<span class="sep">/</span>
|
||||
<span>运营派单</span>
|
||||
<span class="sep">/</span>
|
||||
<span class="cur">报警派单台账</span>
|
||||
</div>
|
||||
<div class="header-main">
|
||||
<div class="header-left">
|
||||
<div class="header-row">
|
||||
<span class="title-bar"></span>
|
||||
<span class="main-title">报警派单台账</span>
|
||||
<span class="sub-title">ALARM DISPATCH LEDGER</span>
|
||||
</div>
|
||||
<p class="desc">统一管理报警派单全过程台账,支持按派单状态检索,跟踪派发、处理与完成情况。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主体卡片 -->
|
||||
<div class="content-card">
|
||||
<!-- 筛选区 -->
|
||||
<div class="filter-card">
|
||||
<div class="filter-grid">
|
||||
<div class="filter-item">
|
||||
<label class="filter-label">派单状态</label>
|
||||
<a-select
|
||||
v-model:value="searchForm.status"
|
||||
placeholder="全部状态"
|
||||
allow-clear
|
||||
:options="statusOptions"
|
||||
/>
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<label class="filter-label">关键词检索</label>
|
||||
<a-input
|
||||
v-model:value="searchForm.keyword"
|
||||
placeholder="派单号 / 线路名称 / 线路编码"
|
||||
allow-clear
|
||||
@pressEnter="handleSearch"
|
||||
>
|
||||
<template #prefix><SearchOutlined /></template>
|
||||
</a-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-bottom">
|
||||
<div class="filter-actions">
|
||||
<a-button type="primary" @click="handleSearch">
|
||||
<template #icon><SearchOutlined /></template>
|
||||
查询
|
||||
</a-button>
|
||||
<a-button @click="handleReset">
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
重置
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 列表区 -->
|
||||
<div class="list-section">
|
||||
<div class="list-header">
|
||||
<div class="list-title">
|
||||
<span class="dot"></span>
|
||||
<span class="t">报警派单数据台账</span>
|
||||
<span class="count">{{ total }} 条</span>
|
||||
</div>
|
||||
<div class="list-actions">
|
||||
<a-button type="text" size="small" title="刷新列表" @click="handleRefresh">
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-wrapper" ref="wrapperRef">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="dataSource"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
row-key="id"
|
||||
:scroll="{ x: 'max-content', y: tableHeight }"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'dispatchNo'">
|
||||
<span class="mono dispatch-no">{{ record.dispatchNo || '-' }}</span>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'alarmTime'">
|
||||
<span>{{ formatTime(record.alarmTime) }}</span>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'lineName'">
|
||||
<span class="line-name">{{ record.lineName || record.lineCode || '-' }}</span>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'lineCode'">
|
||||
<span class="mono">{{ record.lineCode || '-' }}</span>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'customerId'">
|
||||
{{ record.customerName || record.customerId || '-' }}
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'workerName'">
|
||||
<span>{{ record.workerName || '未指派' }}</span>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'alarmGrade'">
|
||||
<a-tag :color="gradeMeta(record.alarmGrade).color">
|
||||
{{ gradeMeta(record.alarmGrade).label }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<a-tag :color="statusMeta(record.status).color">
|
||||
{{ statusMeta(record.status).label }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'dispatcher'">
|
||||
<span>{{ record.dispatcher || '-' }}</span>
|
||||
</template>
|
||||
|
||||
<template v-else-if="column.key === 'createdAt'">
|
||||
<span>{{ formatTime(record.createdAt) }}</span>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template #emptyText>
|
||||
<a-empty description="暂无报警派单数据" />
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted } from 'vue';
|
||||
import { SearchOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
||||
import { alarmDispatchApi, type AlarmDispatchRow, type AlarmDispatchQuery } from '@/api/alarm';
|
||||
import { useTableScroll } from '@/composables/useTableScroll';
|
||||
|
||||
const { wrapperRef, tableHeight } = useTableScroll();
|
||||
|
||||
// ==================== 列表数据 ====================
|
||||
const loading = ref(false);
|
||||
const dataSource = ref<AlarmDispatchRow[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
const searchForm = reactive<AlarmDispatchQuery>({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: '',
|
||||
status: undefined as any,
|
||||
});
|
||||
|
||||
const statusOptions = [
|
||||
{ label: '已派发', value: 0 },
|
||||
{ label: '已处理', value: 1 },
|
||||
];
|
||||
|
||||
const STATUS_META: Record<number, { label: string; color: string }> = {
|
||||
0: { label: '已派发', color: 'processing' },
|
||||
1: { label: '已处理', color: 'success' },
|
||||
};
|
||||
|
||||
function statusMeta(v?: number) {
|
||||
return STATUS_META[v ?? -1] || { label: '未知', color: 'default' };
|
||||
}
|
||||
|
||||
const GRADE_META: Record<number, { label: string; color: string }> = {
|
||||
1: { label: '严重', color: 'error' },
|
||||
2: { label: '一般', color: 'warning' },
|
||||
3: { label: '提示', color: 'default' },
|
||||
};
|
||||
|
||||
function gradeMeta(v?: number) {
|
||||
return GRADE_META[v ?? 0] || { label: '未知', color: 'default' };
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{ title: '派单号', dataIndex: 'dispatchNo', key: 'dispatchNo', ellipsis: true, width: 160 },
|
||||
{ title: '报警时间', dataIndex: 'alarmTime', key: 'alarmTime', ellipsis: true, width: 170 },
|
||||
{ title: '线路名称', dataIndex: 'lineName', key: 'lineName', ellipsis: true },
|
||||
{ title: '线路编码', dataIndex: 'lineCode', key: 'lineCode', ellipsis: true, width: 150 },
|
||||
{ title: '所属客户', dataIndex: 'customerId', key: 'customerId', ellipsis: true, width: 180 },
|
||||
{ title: '指派电工', dataIndex: 'workerName', key: 'workerName', ellipsis: true, width: 120 },
|
||||
{ title: '报警等级', dataIndex: 'alarmGrade', key: 'alarmGrade', align: 'center' as const, width: 90 },
|
||||
{ title: '状态', dataIndex: 'status', key: 'status', align: 'center' as const, width: 100 },
|
||||
{ title: '派发人', dataIndex: 'dispatcher', key: 'dispatcher', ellipsis: true, width: 120 },
|
||||
{ title: '创建时间', dataIndex: 'createdAt', key: 'createdAt', ellipsis: true, width: 170 },
|
||||
];
|
||||
|
||||
const pagination = computed(() => ({
|
||||
current: searchForm.page,
|
||||
pageSize: searchForm.pageSize,
|
||||
total: total.value,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (t: number) => `共 ${t} 条数据`,
|
||||
}));
|
||||
|
||||
// ==================== 数据请求 ====================
|
||||
async function fetchList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await alarmDispatchApi.getList(searchForm);
|
||||
dataSource.value = res.data.list || [];
|
||||
total.value = res.data.total || 0;
|
||||
} catch {
|
||||
// 拦截器已提示
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchList();
|
||||
});
|
||||
|
||||
// ==================== 筛选与分页 ====================
|
||||
function handleSearch() {
|
||||
searchForm.page = 1;
|
||||
fetchList();
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
searchForm.keyword = '';
|
||||
searchForm.status = undefined as any;
|
||||
searchForm.page = 1;
|
||||
fetchList();
|
||||
}
|
||||
|
||||
function handleRefresh() {
|
||||
fetchList();
|
||||
}
|
||||
|
||||
function handleTableChange(pag: any) {
|
||||
searchForm.page = pag.current;
|
||||
searchForm.pageSize = pag.pageSize;
|
||||
fetchList();
|
||||
}
|
||||
|
||||
// ==================== 展示辅助 ====================
|
||||
function formatTime(val?: string) {
|
||||
if (!val) return '-';
|
||||
const d = new Date(val);
|
||||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.alarm-dispatch-page {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
background: #f4f7fb;
|
||||
padding: 20px 24px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 顶部标题 */
|
||||
.page-header {
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: #5b6b7e;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.breadcrumb .sep {
|
||||
color: #c2c6d8;
|
||||
}
|
||||
|
||||
.breadcrumb .cur {
|
||||
color: #0054cd;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.header-main {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.title-bar {
|
||||
width: 5px;
|
||||
height: 22px;
|
||||
background: #0054cd;
|
||||
border-radius: 3px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.main-title {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #0d1c2c;
|
||||
line-height: 1.4;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #0054cd;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin: 6px 0 0;
|
||||
font-size: 13px;
|
||||
color: #5b6b7e;
|
||||
}
|
||||
|
||||
/* 主体卡片 */
|
||||
.content-card {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
background: #fff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
|
||||
padding: 16px 18px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 筛选区 */
|
||||
.filter-card {
|
||||
flex-shrink: 0;
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 10px;
|
||||
padding: 14px 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.filter-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr minmax(220px, 2fr);
|
||||
gap: 12px 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.filter-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.filter-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #5b6b7e;
|
||||
}
|
||||
|
||||
.filter-bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 14px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #eef1f6;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 列表区 */
|
||||
.list-section {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.list-header {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 2px 4px 12px;
|
||||
}
|
||||
|
||||
.list-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.list-title .dot {
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
background: #0054cd;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.list-title .t {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #0d1c2c;
|
||||
}
|
||||
|
||||
.list-title .count {
|
||||
padding: 1px 8px;
|
||||
border-radius: 6px;
|
||||
background: #eef4ff;
|
||||
color: #0054cd;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
font-family: Consolas, Menlo, monospace;
|
||||
}
|
||||
|
||||
.list-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 单元格 */
|
||||
.mono {
|
||||
font-family: Consolas, Menlo, monospace;
|
||||
font-size: 13px;
|
||||
color: #0d1c2c;
|
||||
}
|
||||
|
||||
.dispatch-no {
|
||||
color: #0054cd;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.line-name {
|
||||
font-weight: 600;
|
||||
color: #0d1c2c;
|
||||
}
|
||||
|
||||
/* AntD 覆盖 */
|
||||
:deep(.ant-table) {
|
||||
font-size: 14px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-table-thead > tr > th) {
|
||||
background: #f8fafc;
|
||||
color: #5b6b7e;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.ant-btn-primary) {
|
||||
background: #0054cd;
|
||||
border-color: #0054cd;
|
||||
}
|
||||
|
||||
:deep(.ant-btn-primary:not(:disabled):hover),
|
||||
:deep(.ant-btn-primary:not(:disabled):focus) {
|
||||
background: #136bfb;
|
||||
border-color: #136bfb;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user