|
@@ -48,6 +48,9 @@
|
|
|
</template>
|
|
</template>
|
|
|
{{ $t('searchTable.operation.create') }}
|
|
{{ $t('searchTable.operation.create') }}
|
|
|
</a-button>
|
|
</a-button>
|
|
|
|
|
+ <a-checkbox v-model="showECSInfo" @change="onECSInfoChange">
|
|
|
|
|
+ 显示ECS主机信息
|
|
|
|
|
+ </a-checkbox>
|
|
|
</a-space>
|
|
</a-space>
|
|
|
</a-col>
|
|
</a-col>
|
|
|
<a-col :span="12" style="
|
|
<a-col :span="12" style="
|
|
@@ -108,7 +111,9 @@
|
|
|
|
|
|
|
|
<a-table row-key="id" :loading="loading" :pagination="pagination"
|
|
<a-table row-key="id" :loading="loading" :pagination="pagination"
|
|
|
:columns="(cloneColumns as TableColumnData[])" :data="renderData" :bordered="{ headerCell: true }"
|
|
:columns="(cloneColumns as TableColumnData[])" :data="renderData" :bordered="{ headerCell: true }"
|
|
|
- :size="size" @page-change="onPageChange">
|
|
|
|
|
|
|
+ :size="size" @page-change="onPageChange"
|
|
|
|
|
+ :expandable="tableExpandable" :row-class="getRowClass"
|
|
|
|
|
+ :class="{ 'show-ecs-info': showECSInfo }">
|
|
|
<template #index="{ record }">
|
|
<template #index="{ record }">
|
|
|
{{ record.id }}
|
|
{{ record.id }}
|
|
|
</template>
|
|
</template>
|
|
@@ -141,7 +146,16 @@
|
|
|
record.balance_alert_threshold }}
|
|
record.balance_alert_threshold }}
|
|
|
</a-tag>
|
|
</a-tag>
|
|
|
<a-tag color="blue" v-else>{{ record.balance_alert_threshold }} </a-tag>
|
|
<a-tag color="blue" v-else>{{ record.balance_alert_threshold }} </a-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
|
|
|
|
|
+ <template #ecs="{ record }">
|
|
|
|
|
+ <span v-if="record.ecs_list && record.ecs_list.length > 0">
|
|
|
|
|
+ <a-tag color="green">{{ record.ecs_list.length }} 台</a-tag>
|
|
|
|
|
+ <a-button type="text" size="mini" @click="expandECSDetails(record)">
|
|
|
|
|
+ <icon-eye />
|
|
|
|
|
+ </a-button>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <a-tag v-else color="gray">无</a-tag>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<template #status="{ record }">
|
|
<template #status="{ record }">
|
|
@@ -164,6 +178,70 @@
|
|
|
<div>更新:{{ dayjs(record.last_time).format('MM-DD HH:mm') }}</div>
|
|
<div>更新:{{ dayjs(record.last_time).format('MM-DD HH:mm') }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
+ <template #expandIcon="{ record }">
|
|
|
|
|
+ <a-button type="text" size="mini" @click="toggleRowExpand(record.id)">
|
|
|
|
|
+ <icon-plus v-if="!expandedRows.includes(record.id)" />
|
|
|
|
|
+ <icon-minus v-else />
|
|
|
|
|
+ </a-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <template #expand-row="{ record }">
|
|
|
|
|
+ <div v-if="!record.ecs_list || record.ecs_list.length === 0" class="no-ecs-message">
|
|
|
|
|
+ 该账号下无ECS实例
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-else class="ecs-table-wrapper">
|
|
|
|
|
+ <a-table
|
|
|
|
|
+ :data="record.ecs_list"
|
|
|
|
|
+ :pagination="false"
|
|
|
|
|
+ bordered="cell"
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ stripe
|
|
|
|
|
+ row-key="instance_id"
|
|
|
|
|
+ >
|
|
|
|
|
+ <template #columns>
|
|
|
|
|
+ <a-table-column title="实例ID" data-index="instance_id" :width="170" />
|
|
|
|
|
+ <a-table-column title="主机名" data-index="host_name" :width="150" />
|
|
|
|
|
+ <a-table-column title="系统" data-index="os_name" :width="120" />
|
|
|
|
|
+ <a-table-column title="配置" :width="100">
|
|
|
|
|
+ <template #cell="{ record }">
|
|
|
|
|
+ {{ record.cpu }}核 {{ record.memory }}GB
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </a-table-column>
|
|
|
|
|
+ <a-table-column title="IP地址" :width="350">
|
|
|
|
|
+ <template #cell="{ record }">
|
|
|
|
|
+ <div v-if="!record.addressIps || record.addressIps.length === 0">-</div>
|
|
|
|
|
+ <div v-else class="ip-pairs-container">
|
|
|
|
|
+ <div v-for="(ip, index) in record.addressIps" :key="index" class="ip-pair">
|
|
|
|
|
+ <a-tag class="ip-tag private-ip" color="red">{{ ip.privateIp }}</a-tag>
|
|
|
|
|
+ <template v-if="ip.publicIp">
|
|
|
|
|
+ <span class="ip-arrow">→</span>
|
|
|
|
|
+ <a-tag class="ip-tag public-ip" color="green">{{ ip.publicIp }}</a-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </a-table-column>
|
|
|
|
|
+ <a-table-column title="到期时间" :width="150">
|
|
|
|
|
+ <template #cell="{ record }">
|
|
|
|
|
+ <!-- 计算到期时间 -->
|
|
|
|
|
+ <div v-if="getDaysLeft(record.expired_time) <= 0" class="expired-critical">
|
|
|
|
|
+ <span>已过期</span>
|
|
|
|
|
+ <div class="expired-date">{{ formatDate(record.expired_time) }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-else-if="getDaysLeft(record.expired_time) <= 7" class="expired-urgent">
|
|
|
|
|
+ <span>剩余{{ getDaysLeft(record.expired_time) }}天</span>
|
|
|
|
|
+ <div class="expired-date">{{ formatDate(record.expired_time) }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-else-if="getDaysLeft(record.expired_time) <= 30" class="expired-warning">
|
|
|
|
|
+ <span>剩余{{ getDaysLeft(record.expired_time) }}天</span>
|
|
|
|
|
+ <div class="expired-date">{{ formatDate(record.expired_time) }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <span v-else>{{ formatDate(record.expired_time) }}</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </a-table-column>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </a-table>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
</a-table>
|
|
</a-table>
|
|
|
</a-card>
|
|
</a-card>
|
|
|
</div>
|
|
</div>
|
|
@@ -171,7 +249,7 @@
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
|
<script lang="ts" setup>
|
|
|
import { useRouter } from 'vue-router';
|
|
import { useRouter } from 'vue-router';
|
|
|
-import { computed, ref, reactive, watch, nextTick } from 'vue';
|
|
|
|
|
|
|
+import { computed, ref, reactive, watch, nextTick, onMounted } from 'vue';
|
|
|
import { useI18n } from 'vue-i18n';
|
|
import { useI18n } from 'vue-i18n';
|
|
|
import dayjs from 'dayjs';
|
|
import dayjs from 'dayjs';
|
|
|
import useLoading from '@/hooks/loading';
|
|
import useLoading from '@/hooks/loading';
|
|
@@ -183,15 +261,44 @@ import cloneDeep from 'lodash/cloneDeep';
|
|
|
import Sortable from 'sortablejs';
|
|
import Sortable from 'sortablejs';
|
|
|
import { numberFormat, amountFormat } from '@/utils/util';
|
|
import { numberFormat, amountFormat } from '@/utils/util';
|
|
|
import WorkHourModel from '@/components/WorkHourModel.vue';
|
|
import WorkHourModel from '@/components/WorkHourModel.vue';
|
|
|
|
|
+import { h } from 'vue';
|
|
|
|
|
+import { Table as ATable } from '@arco-design/web-vue';
|
|
|
|
|
|
|
|
import {
|
|
import {
|
|
|
- AliyunAccountParams, AliyunAccountRecord, queryAccountList, UpdateAccountModel, UpdateAccount
|
|
|
|
|
|
|
+ AliyunAccountParams, queryAccountList, UpdateAccountModel, UpdateAccount
|
|
|
} from '@/api/aliyun';
|
|
} from '@/api/aliyun';
|
|
|
|
|
|
|
|
|
|
|
|
|
type SizeProps = 'mini' | 'small' | 'medium' | 'large';
|
|
type SizeProps = 'mini' | 'small' | 'medium' | 'large';
|
|
|
type Column = TableColumnData & { checked?: true };
|
|
type Column = TableColumnData & { checked?: true };
|
|
|
|
|
|
|
|
|
|
+interface ECSInstance {
|
|
|
|
|
+ id: number;
|
|
|
|
|
+ account_id: number;
|
|
|
|
|
+ name: string;
|
|
|
|
|
+ description: string;
|
|
|
|
|
+ create_time: string;
|
|
|
|
|
+ last_time: string;
|
|
|
|
|
+ instance_id: string;
|
|
|
|
|
+ instance_name: string;
|
|
|
|
|
+ host_name: string;
|
|
|
|
|
+ os_name: string;
|
|
|
|
|
+ region_id: string;
|
|
|
|
|
+ instance_type: string;
|
|
|
|
|
+ cpu: number;
|
|
|
|
|
+ memory: number;
|
|
|
|
|
+ expired_time: string;
|
|
|
|
|
+ creation_time: string;
|
|
|
|
|
+ public_ip?: string;
|
|
|
|
|
+ addressIps: {
|
|
|
|
|
+ networkInterfaceId: string;
|
|
|
|
|
+ privateIp: string;
|
|
|
|
|
+ }[];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type ExtendedAliyunAccountRecord = import('@/api/aliyun').AliyunAccountRecord & {
|
|
|
|
|
+ ecs_list?: ECSInstance[];
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
const generateFormModel = () => {
|
|
const generateFormModel = () => {
|
|
|
return {
|
|
return {
|
|
@@ -206,7 +313,7 @@ const router = useRouter();
|
|
|
|
|
|
|
|
const { loading, setLoading } = useLoading(true);
|
|
const { loading, setLoading } = useLoading(true);
|
|
|
const { t } = useI18n();
|
|
const { t } = useI18n();
|
|
|
-const renderData = ref<AliyunAccountRecord[]>([]);
|
|
|
|
|
|
|
+const renderData = ref<ExtendedAliyunAccountRecord[]>([]);
|
|
|
const formModel = ref(generateFormModel());
|
|
const formModel = ref(generateFormModel());
|
|
|
const cloneColumns = ref<Column[]>([]);
|
|
const cloneColumns = ref<Column[]>([]);
|
|
|
const showColumns = ref<Column[]>([]);
|
|
const showColumns = ref<Column[]>([]);
|
|
@@ -239,7 +346,12 @@ const densityList = computed(() => [
|
|
|
},
|
|
},
|
|
|
]);
|
|
]);
|
|
|
|
|
|
|
|
-const columns = computed<TableColumnData[]>(() => [
|
|
|
|
|
|
|
+const columns = computed<any[]>(() => [
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '',
|
|
|
|
|
+ width: 50,
|
|
|
|
|
+ slotName: 'expandIcon',
|
|
|
|
|
+ },
|
|
|
{
|
|
{
|
|
|
title: '#',
|
|
title: '#',
|
|
|
dataIndex: 'index',
|
|
dataIndex: 'index',
|
|
@@ -268,6 +380,17 @@ const columns = computed<TableColumnData[]>(() => [
|
|
|
align: 'right',
|
|
align: 'right',
|
|
|
width: 60,
|
|
width: 60,
|
|
|
},
|
|
},
|
|
|
|
|
+ {
|
|
|
|
|
+ title: 'ECS数量',
|
|
|
|
|
+ dataIndex: 'ecs_count',
|
|
|
|
|
+ width: 80,
|
|
|
|
|
+ render: ({ record }: any) => {
|
|
|
|
|
+ const count = record.ecs_list?.length || 0;
|
|
|
|
|
+ return count > 0
|
|
|
|
|
+ ? h('a-tag', { color: 'green' }, `${count} 台`)
|
|
|
|
|
+ : h('a-tag', { color: 'gray' }, '无');
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
{
|
|
{
|
|
|
title: '状态',
|
|
title: '状态',
|
|
|
slotName: 'status',
|
|
slotName: 'status',
|
|
@@ -308,6 +431,7 @@ const fetchData = async (
|
|
|
try {
|
|
try {
|
|
|
const { data } = await queryAccountList(params);
|
|
const { data } = await queryAccountList(params);
|
|
|
if (!data) return;
|
|
if (!data) return;
|
|
|
|
|
+ console.log('API返回数据:', data.list);
|
|
|
renderData.value = data.list;
|
|
renderData.value = data.list;
|
|
|
pagination.current = data.page;
|
|
pagination.current = data.page;
|
|
|
pagination.total = data.count;
|
|
pagination.total = data.count;
|
|
@@ -435,6 +559,123 @@ watch(
|
|
|
},
|
|
},
|
|
|
{ deep: true, immediate: true }
|
|
{ deep: true, immediate: true }
|
|
|
);
|
|
);
|
|
|
|
|
+
|
|
|
|
|
+// 新增状态来跟踪行的展开状态
|
|
|
|
|
+const expandedRows = ref<number[]>([]);
|
|
|
|
|
+
|
|
|
|
|
+// 切换行的展开状态
|
|
|
|
|
+const toggleRowExpand = (id: number) => {
|
|
|
|
|
+ const index = expandedRows.value.indexOf(id);
|
|
|
|
|
+ if (index === -1) {
|
|
|
|
|
+ expandedRows.value.push(id);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ expandedRows.value.splice(index, 1);
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 监听expandedRows变化
|
|
|
|
|
+watch(expandedRows, (newVal) => {
|
|
|
|
|
+ console.log('expandedRows变化:', newVal);
|
|
|
|
|
+}, { deep: true });
|
|
|
|
|
+
|
|
|
|
|
+// 在mounted钩子中添加调试信息
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ console.log('初始showECSInfo状态:', showECSInfo.value);
|
|
|
|
|
+
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ if (renderData.value.length > 0 && showECSInfo.value) {
|
|
|
|
|
+ expandedRows.value = renderData.value.map(item => item.id);
|
|
|
|
|
+ console.log('初始化设置展开行:', expandedRows.value);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// 在表格数据加载后展开所有行
|
|
|
|
|
+watch(() => renderData.value, (newVal) => {
|
|
|
|
|
+ if (newVal.length > 0 && showECSInfo.value) {
|
|
|
|
|
+ expandedRows.value = newVal.map(item => item.id);
|
|
|
|
|
+ }
|
|
|
|
|
+}, { deep: true });
|
|
|
|
|
+
|
|
|
|
|
+// 控制显示/隐藏ECS信息的状态
|
|
|
|
|
+const showECSInfo = ref(localStorage.getItem('aliyun_show_ecs') === 'true');
|
|
|
|
|
+
|
|
|
|
|
+// 监听状态变化,更新localStorage
|
|
|
|
|
+watch(showECSInfo, (value) => {
|
|
|
|
|
+ localStorage.setItem('aliyun_show_ecs', String(value));
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// 添加用于日期计算的辅助函数
|
|
|
|
|
+const getDaysLeft = (dateStr: string) => {
|
|
|
|
|
+ const expiredTime = dayjs(dateStr);
|
|
|
|
|
+ const now = dayjs();
|
|
|
|
|
+ return expiredTime.diff(now, 'day');
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const formatDate = (dateStr: string) => {
|
|
|
|
|
+ return dayjs(dateStr).format('YYYY-MM-DD');
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 修改expandable配置
|
|
|
|
|
+const tableExpandable = computed(() => {
|
|
|
|
|
+ console.log('计算expandable属性,当前showECSInfo:', showECSInfo.value, '当前expandedRows:', expandedRows.value);
|
|
|
|
|
+ return {
|
|
|
|
|
+ expandedRowKeys: expandedRows.value,
|
|
|
|
|
+ // 不使用expandRowByClick,避免与单击事件冲突
|
|
|
|
|
+ expandRowByClick: false,
|
|
|
|
|
+ // 确保即使showECSInfo为false时也能展开,但默认状态是根据showECSInfo来设置
|
|
|
|
|
+ rowExpandable: () => true
|
|
|
|
|
+ };
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+const onECSInfoChange = (val: boolean) => {
|
|
|
|
|
+ console.log('ECS信息显示状态改变:', val);
|
|
|
|
|
+ localStorage.setItem('aliyun_show_ecs', String(val));
|
|
|
|
|
+
|
|
|
|
|
+ // 强制渲染更新
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ if (val) {
|
|
|
|
|
+ // 如果选中了显示ECS信息,则展开所有行
|
|
|
|
|
+ expandedRows.value = renderData.value.map(item => item.id);
|
|
|
|
|
+ console.log('已设置展开所有行:', expandedRows.value);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 如果取消了显示ECS信息,则收起所有行
|
|
|
|
|
+ expandedRows.value = [];
|
|
|
|
|
+ console.log('已设置收起所有行');
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const getRowClass = (record: any) => {
|
|
|
|
|
+ console.log('getRowClass called for:', record.id, 'isECSInfo:', record.isECSInfo);
|
|
|
|
|
+ if (record.isECSInfo) {
|
|
|
|
|
+ return 'ecs-info-row';
|
|
|
|
|
+ }
|
|
|
|
|
+ return '';
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const expandECSDetails = (record: any) => {
|
|
|
|
|
+ console.log('点击展开按钮,记录ID:', record.id);
|
|
|
|
|
+
|
|
|
|
|
+ // 使用对象解构获取event
|
|
|
|
|
+ const { event } = window;
|
|
|
|
|
+ if (event) {
|
|
|
|
|
+ event.stopPropagation();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确保ID存在
|
|
|
|
|
+ if (!record || !record.id) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 切换展开状态
|
|
|
|
|
+ const index = expandedRows.value.indexOf(record.id);
|
|
|
|
|
+ if (index === -1) {
|
|
|
|
|
+ expandedRows.value.push(record.id);
|
|
|
|
|
+ console.log('展开行:', record.id, '当前展开行:', expandedRows.value);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ expandedRows.value.splice(index, 1);
|
|
|
|
|
+ console.log('收起行:', record.id, '当前展开行:', expandedRows.value);
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<script lang="ts">
|
|
<script lang="ts">
|
|
@@ -476,4 +717,153 @@ export default {
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.arco-table-tr-expand) {
|
|
|
|
|
+ background-color: #f0f9ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.arco-table-expanded-row-cell) {
|
|
|
|
|
+ padding: 12px 20px;
|
|
|
|
|
+ border: 1px solid #d9ecff;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ margin: 8px 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.no-ecs-message {
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+ padding: 12px 0;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.no-ecs-message.error {
|
|
|
|
|
+ color: #f56c6c;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.arco-table-container) {
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.arco-table-stripe) .arco-table-tr:nth-child(even) {
|
|
|
|
|
+ background-color: #fafafa;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 更改展开行单元格样式 */
|
|
|
|
|
+:deep(.arco-table-expand-content) {
|
|
|
|
|
+ padding: 16px;
|
|
|
|
|
+ background-color: #f9f9f9;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.arco-table-td.arco-table-expand-cell) {
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.arco-table-expanded-row) {
|
|
|
|
|
+ .arco-table-td {
|
|
|
|
|
+ border-bottom: 1px solid #e9e9e9;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 确保嵌套表格样式正确 */
|
|
|
|
|
+.ecs-table-wrapper {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+
|
|
|
|
|
+ .arco-table {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 改变展开行的背景颜色,让它与主表区分开 */
|
|
|
|
|
+:deep(.arco-table-tr-expand) {
|
|
|
|
|
+ background-color: #f0f9ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 确保展开行中的表格占满整行 */
|
|
|
|
|
+:deep(.arco-table-expanded-row-cell) {
|
|
|
|
|
+ padding: 0 !important;
|
|
|
|
|
+ /* 重要:这会使展开行内容横跨整行 */
|
|
|
|
|
+ display: table-cell;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 控制展开行的显示/隐藏 */
|
|
|
|
|
+:deep(.arco-table-expanded-row) {
|
|
|
|
|
+ display: v-bind('showECSInfo ? "table-row" : "none"');
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 控制ECS信息行的显示/隐藏 - 更简单的版本 */
|
|
|
|
|
+:deep(.ecs-info-row) {
|
|
|
|
|
+ display: none;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.show-ecs-info .ecs-info-row) {
|
|
|
|
|
+ display: table-row;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 改进IP地址显示样式 */
|
|
|
|
|
+.ip-pairs-container {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ip-pair {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ip-tag {
|
|
|
|
|
+ font-family: monospace; /* 使用等宽字体更适合显示IP地址 */
|
|
|
|
|
+ min-width: 120px;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ padding: 2px 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ip-arrow {
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 修改到期时间样式,移除动画效果 */
|
|
|
|
|
+.expired-critical {
|
|
|
|
|
+ color: #f56c6c; /* 红色 */
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ /* 移除 animation: expired-pulse 1s infinite; */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.expired-urgent {
|
|
|
|
|
+ color: #f56c6c; /* 红色 */
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ /* 移除 animation: expired-soon-pulse 1.5s infinite; */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.expired-warning {
|
|
|
|
|
+ color: #e6a23c; /* 橙色/黄色 */
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.expired-date {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ margin-top: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 删除这些动画关键帧定义,因为不再需要 */
|
|
|
|
|
+/*
|
|
|
|
|
+@keyframes expired-pulse {
|
|
|
|
|
+ 0% { transform: scale(1); opacity: 1; }
|
|
|
|
|
+ 50% { transform: scale(1.05); opacity: 0.7; }
|
|
|
|
|
+ 100% { transform: scale(1); opacity: 1; }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@keyframes expired-soon-pulse {
|
|
|
|
|
+ 0% { opacity: 1; }
|
|
|
|
|
+ 50% { opacity: 0.6; }
|
|
|
|
|
+ 100% { opacity: 1; }
|
|
|
|
|
+}
|
|
|
|
|
+*/
|
|
|
</style>
|
|
</style>
|