DeviceAlarmBomList.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. <template>
  2. <Dialog v-model="dialogVisible"
  3. :title="t('monitor.details')"
  4. :width="dialogWidth"
  5. class="fixed-height-dialog"
  6. @close="handleClose" >
  7. <ContentWrap>
  8. <!-- 添加设备信息展示区域 -->
  9. <div v-if="deviceInfo" class="device-info-card">
  10. <div class="info-item">
  11. <span class="info-label">{{ t('iotDevice.code') }}:</span>
  12. <span class="info-value">{{ deviceInfo.deviceCode }}</span>
  13. </div>
  14. <div class="info-item">
  15. <span class="info-label">{{ t('iotDevice.name') }}:</span>
  16. <span class="info-value">{{ deviceInfo.deviceName }}</span>
  17. </div>
  18. <div class="info-item" v-if="deviceInfo.model">
  19. <span class="info-label">{{ t('deviceForm.model') }}:</span>
  20. <span class="info-value">{{ deviceInfo.model }}</span>
  21. </div>
  22. </div>
  23. <div class="table-container">
  24. <!-- 添加表格容器并设置滚动 -->
  25. <el-table
  26. v-loading="loading"
  27. :data="paginatedList"
  28. :stripe="true"
  29. :show-overflow-tooltip="true"
  30. style="width: auto"
  31. height="100%"
  32. class="scrollable-table"
  33. ref="tableRef"
  34. >
  35. <el-table-column :label="t('iotDevice.code')" align="center" prop="deviceCode"
  36. :width="columnWidths.deviceCode" v-if="false"/>
  37. <el-table-column :label="t('iotDevice.name')" align="center" prop="deviceName"
  38. :width="columnWidths.deviceName" v-if="false"/>
  39. <el-table-column :label="t('mainPlan.MaintItems')" align="center" prop="name"
  40. :width="columnWidths.name"/>
  41. <el-table-column :label="t('operationFillForm.sumTime')" align="center" prop="totalRunTime"
  42. :width="columnWidths.totalRunTime">
  43. <template #default="{ row }">
  44. {{ row.totalRunTime ?? row.tempTotalRunTime }}
  45. </template>
  46. </el-table-column>
  47. <el-table-column :label="t('operationFillForm.sumKil')" align="center" prop="totalMileage"
  48. :width="columnWidths.totalMileage">
  49. <template #default="{ row }">
  50. {{ row.totalMileage ?? row.tempTotalMileage }}
  51. </template>
  52. </el-table-column>
  53. <!-- 时间分组列 -->
  54. <el-table-column v-if="showTimeColumns" label="保养时长"
  55. align="center"
  56. :width="columnWidths.timeGroup">
  57. <el-table-column :label="t('mainPlan.lastMaintenanceOperationTime')" align="center" prop="lastRunningTime"
  58. :width="columnWidths.lastRunningTime"/>
  59. <el-table-column :label="t('mainPlan.RunTimeCycle')" align="center" prop="nextRunningTime"
  60. :width="columnWidths.nextRunningTime"/>
  61. <el-table-column :label="t('mainPlan.nextMaintTime')" align="center" prop="timePeriod"
  62. :width="columnWidths.timePeriod">
  63. <template #default="{ row }">
  64. <span :class="{ 'negative-value': isNegative(row.timePeriod) }">
  65. {{ row.timePeriod }}
  66. </span>
  67. </template>
  68. </el-table-column>
  69. </el-table-column>
  70. <!-- 里程分组列 -->
  71. <el-table-column v-if="showMileageColumns" label="保养里程"
  72. align="center"
  73. :width="columnWidths.mileageGroup">
  74. <el-table-column :label="t('mainPlan.lastMaintenanceMileage')" align="center" prop="lastRunningKilometers"
  75. :width="columnWidths.lastRunningKilometers"/>
  76. <el-table-column :label="t('mainPlan.operatingMileageCycle')" align="center" prop="nextRunningKilometers"
  77. :width="columnWidths.nextRunningKilometers"/>
  78. <el-table-column :label="t('mainPlan.nextMaintKil')" align="center" prop="kilometerCycle"
  79. :width="columnWidths.kilometerCycle">
  80. <template #default="{ row }">
  81. <span :class="{ 'negative-value': isNegative(row.kilometerCycle) }">
  82. {{ row.kilometerCycle }}
  83. </span>
  84. </template>
  85. </el-table-column>
  86. </el-table-column>
  87. <!-- 日期分组列 -->
  88. <el-table-column v-if="showNaturalDateColumns" label="保养日期"
  89. align="center"
  90. :width="columnWidths.dateGroup">
  91. <el-table-column :label="t('mainPlan.lastMaintenanceNaturalDate')" align="center" prop="lastNaturalDate"
  92. :width="columnWidths.lastNaturalDate">
  93. <template #default="scope">
  94. <el-date-picker
  95. v-model="scope.row.lastNaturalDate"
  96. type="date"
  97. placeholder="选择日期"
  98. format="YYYY-MM-DD"
  99. value-format="YYYY-MM-DD"
  100. style="width: 100%"
  101. :disabled="true"
  102. />
  103. </template>
  104. </el-table-column>
  105. <el-table-column :label="t('mainPlan.NaturalDailyCycle')" align="center" prop="nextNaturalDate"
  106. :width="columnWidths.nextNaturalDate"/>
  107. <el-table-column :label="t('mainPlan.nextMaintDate')" align="center" prop="naturalDatePeriod"
  108. :width="columnWidths.naturalDatePeriod"/>
  109. </el-table-column>
  110. </el-table>
  111. </div>
  112. <Pagination
  113. :total="total"
  114. v-model:page="queryParams.pageNo"
  115. v-model:limit="queryParams.pageSize"
  116. @pagination="handlePagination"
  117. />
  118. </ContentWrap>
  119. </Dialog>
  120. </template>
  121. <script setup lang="ts">
  122. import { DictDataVO } from '@/api/system/dict/dict.data'
  123. import dayjs from 'dayjs'
  124. import { IotMainWorkOrderBomApi, IotMainWorkOrderBomVO } from '@/api/pms/iotmainworkorderbom'
  125. import { IotMaintenanceBomApi, IotMaintenanceBomVO } from '@/api/pms/iotmaintenancebom'
  126. import {propTypes} from "@/utils/propTypes";
  127. const { t } = useI18n() // 国际化
  128. const emit = defineEmits(['close']) // 定义 success 事件,用于操作成功后的回调
  129. const dialogVisible = ref(false) // 弹窗的是否展示
  130. const loading = ref(true) // 列表的加载中
  131. const queryFormRef = ref() // 搜索的表单
  132. const list = ref<IotMaintenanceBomVO[]>([]) // 列表的数据
  133. const total = ref(0) // 列表的总页数
  134. // 分页重置标志
  135. const shouldResetPagination = ref(false)
  136. // 添加外部传入的设备信息
  137. const externalDeviceInfo = ref(null)
  138. const dialogWidth = '1500px';
  139. const tableRef = ref(null) // 表格实例引用
  140. const columnWidths = ref({}) // 存储列宽的对象
  141. const queryParams = reactive({
  142. pageNo: 1,
  143. pageSize: 10,
  144. workOrderId: undefined,
  145. planId: undefined,
  146. deviceId: undefined
  147. })
  148. const props = defineProps({
  149. flag: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
  150. })
  151. const selectedRow = ref(null)
  152. const getTextWidth = (str: string) => {
  153. if (!str) return 0;
  154. if (widthCache.has(str)) return widthCache.get(str)!;
  155. const span = document.createElement('span');
  156. span.style.visibility = 'hidden';
  157. span.style.position = 'absolute';
  158. span.style.whiteSpace = 'nowrap';
  159. span.style.font = '14px Microsoft YaHei';
  160. span.textContent = str;
  161. document.body.appendChild(span);
  162. const width = span.offsetWidth;
  163. document.body.removeChild(span);
  164. widthCache.set(str, width);
  165. return width;
  166. };
  167. // 添加列宽计算函数
  168. const widthCache = new Map<string, number>();
  169. const flexColumnWidth = (label: string, prop: keyof IotMaintenanceBomVO) => {
  170. // 确保有数据时才计算
  171. if (!paginatedList.value.length) return "auto";
  172. // 基础内边距30px
  173. const basePadding = 20;
  174. const labelWidth = getActualWidth(label) + basePadding; // 文本宽度 + 内边距
  175. // 计算内容最大宽度
  176. let maxContentWidth = 0;
  177. // 获取该列所有内容的宽度
  178. for (const row of paginatedList.value) {
  179. let value = "";
  180. // 特殊列处理
  181. if (prop === "totalRunTime") {
  182. value = (row.totalRunTime ?? row.tempTotalRunTime)?.toString() || "";
  183. } else if (prop === "totalMileage") {
  184. value = (row.totalMileage ?? row.tempTotalMileage)?.toString() || "";
  185. } else {
  186. value = row[prop]?.toString() || "";
  187. }
  188. // 数值格式化
  189. if (value && !isNaN(Number(value))) {
  190. value = Number(value).toLocaleString();
  191. }
  192. const contentWidth = getActualWidth(value) + basePadding;
  193. if (contentWidth > maxContentWidth) {
  194. maxContentWidth = contentWidth;
  195. }
  196. }
  197. // 返回较大值加上安全边距
  198. return Math.max(labelWidth, maxContentWidth, 120) + "px";
  199. };
  200. // 处理单选逻辑
  201. const selectRow = (row) => {
  202. selectedRow.value = selectedRow.value?.id === row.id ? null : row
  203. emit('choose', row)
  204. dialogVisible.value = false
  205. }
  206. // 分页事件处理
  207. const handlePagination = () => {
  208. console.log("分页变化,当前页:", queryParams.pageNo);
  209. };
  210. // 改进的宽度计算函数
  211. const getActualWidth = (text: string) => {
  212. if (!text) return 0;
  213. // 更精准的字符宽度计算
  214. const chineseChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length;
  215. const otherChars = text.length - chineseChars;
  216. // 中文16px,英文9px
  217. return chineseChars * 16 + otherChars * 9;
  218. };
  219. // 设置列宽
  220. const setColumnWidths = () => {
  221. const widths: Record<string, string> = {};
  222. // 固定列
  223. // widths.deviceCode = flexColumnWidth(t('iotDevice.code'), 'deviceCode');
  224. // widths.deviceName = flexColumnWidth(t('iotDevice.name'), 'deviceName');
  225. widths.totalRunTime = flexColumnWidth(t('operationFillForm.sumTime'), 'totalRunTime');
  226. widths.totalMileage = flexColumnWidth(t('operationFillForm.sumKil'), 'totalMileage');
  227. widths.name = flexColumnWidth(t('mainPlan.MaintItems'), 'name');
  228. // 动态列
  229. if (showTimeColumns.value) {
  230. widths.lastRunningTime = flexColumnWidth(t('mainPlan.lastMaintenanceOperationTime'), 'lastRunningTime');
  231. widths.nextRunningTime = flexColumnWidth(t('mainPlan.RunTimeCycle'), 'nextRunningTime');
  232. widths.timePeriod = flexColumnWidth(t('mainPlan.nextMaintTime'), 'timePeriod');
  233. // 分组列宽度 = 子列宽度之和 + 边框补偿
  234. widths.timeGroup = `${[
  235. parseFloat(widths.lastRunningTime),
  236. parseFloat(widths.nextRunningTime),
  237. parseFloat(widths.timePeriod)
  238. ].reduce((sum, w) => sum + w, 0) + 2}px`;
  239. }
  240. if (showMileageColumns.value) {
  241. widths.lastRunningKilometers = flexColumnWidth(t('mainPlan.lastMaintenanceMileage'), 'lastRunningKilometers');
  242. widths.nextRunningKilometers = flexColumnWidth(t('mainPlan.operatingMileageCycle'), 'nextRunningKilometers');
  243. widths.kilometerCycle = flexColumnWidth(t('mainPlan.nextMaintKil'), 'kilometerCycle');
  244. widths.mileageGroup = `${[
  245. parseFloat(widths.lastRunningKilometers),
  246. parseFloat(widths.nextRunningKilometers),
  247. parseFloat(widths.kilometerCycle)
  248. ].reduce((sum, w) => sum + w, 0) + 2}px`;
  249. }
  250. if (showNaturalDateColumns.value) {
  251. widths.lastNaturalDate = flexColumnWidth(t('mainPlan.lastMaintenanceOperationTime'), 'lastNaturalDate'); // 固定日期选择器的宽度
  252. widths.nextNaturalDate = flexColumnWidth(t('mainPlan.NaturalDailyCycle'), 'nextNaturalDate');
  253. widths.naturalDatePeriod = flexColumnWidth(t('mainPlan.nextMaintDate'), 'naturalDatePeriod');
  254. widths.dateGroup = `${[
  255. parseFloat(widths.lastNaturalDate),
  256. parseFloat(widths.nextNaturalDate),
  257. parseFloat(widths.naturalDatePeriod)
  258. ].reduce((sum, w) => sum + w, 0) + 2}px`;
  259. }
  260. columnWidths.value = widths;
  261. };
  262. // 分页计算属性
  263. const paginatedList = computed(() => {
  264. const start = (queryParams.pageNo - 1) * queryParams.pageSize;
  265. const end = start + queryParams.pageSize;
  266. return list.value.slice(start, end);
  267. });
  268. const open = async (id?: number, flag?: string, deviceInfo?: any) => {
  269. // 重置分页参数
  270. queryParams.pageNo = 1
  271. queryParams.pageSize = 10
  272. list.value = [] // 清空列表避免显示旧数据
  273. total.value = 0
  274. await nextTick() // 确保DOM更新完成
  275. if (deviceInfo) {
  276. externalDeviceInfo.value = deviceInfo
  277. queryParams.deviceId = deviceInfo.deviceId // 如果需要的话
  278. }
  279. if('workOrder' === flag) {
  280. // 加载保养工单 BOM
  281. queryParams.workOrderId = id
  282. queryParams.planId = undefined
  283. await getWorkOrderList()
  284. } else if ('plan' === flag) {
  285. queryParams.planId = id
  286. queryParams.workOrderId = undefined
  287. await getPlanList()
  288. }
  289. dialogVisible.value = true
  290. }
  291. defineExpose({ open }) // 提供 open 方法,用于打开弹窗
  292. const getWorkOrderList = async () => {
  293. loading.value = true
  294. try {
  295. const data = await IotMainWorkOrderBomApi.getWorkOrderBOMs(queryParams)
  296. // 格式化日期字段
  297. data.forEach(item => {
  298. if (item.lastNaturalDate) {
  299. // 将时间戳转换为 YYYY-MM-DD 格式
  300. item.lastNaturalDate = dayjs(item.lastNaturalDate).format('YYYY-MM-DD')
  301. } else {
  302. // 处理空值情况
  303. item.lastNaturalDate = ''
  304. }
  305. // 计算下次保养运行时长 H
  306. item.timePeriod = calculateTimePeriod(item);
  307. // 计算下次保养公里数 KM
  308. item.kilometerCycle = calculateKiloPeriod(item);
  309. // 计算下次保养日期
  310. item.naturalDatePeriod = calculateNextNaturalDate(item)
  311. })
  312. list.value = data
  313. total.value = data.total
  314. } finally {
  315. loading.value = false
  316. }
  317. }
  318. const getPlanList = async () => {
  319. widthCache.clear();
  320. loading.value = true
  321. try {
  322. const data = await IotMaintenanceBomApi.getMainPlanBOMs(queryParams)
  323. // 格式化日期字段
  324. data.forEach(item => {
  325. if (item.lastNaturalDate) {
  326. // 将时间戳转换为 YYYY-MM-DD 格式
  327. item.lastNaturalDate = dayjs(item.lastNaturalDate).format('YYYY-MM-DD')
  328. } else {
  329. // 处理空值情况
  330. item.lastNaturalDate = ''
  331. }
  332. // 计算下次保养运行时长 H
  333. item.timePeriod = calculateTimePeriod(item);
  334. // 计算下次保养公里数 KM
  335. item.kilometerCycle = calculateKiloPeriod(item);
  336. // 计算下次保养日期
  337. item.naturalDatePeriod = calculateNextNaturalDate(item)
  338. })
  339. list.value = data
  340. total.value = data.length
  341. } finally {
  342. loading.value = false
  343. nextTick(setColumnWidths); // 数据加载后计算列宽
  344. }
  345. }
  346. // 添加设备信息计算属性
  347. const deviceInfo = computed(() => {
  348. // 优先使用外部传入的设备信息
  349. if (externalDeviceInfo.value) {
  350. return externalDeviceInfo.value;
  351. }
  352. if (list.value.length > 0) {
  353. const firstRecord = list.value[0];
  354. return {
  355. deviceCode: firstRecord.deviceCode,
  356. deviceName: firstRecord.deviceName,
  357. model: firstRecord.model // 确保列表数据中也有 model
  358. };
  359. }
  360. return null;
  361. });
  362. const handleClose = () => {
  363. // 重置状态避免多个弹窗出现
  364. dialogVisible.value = false
  365. loading.value = false
  366. list.value = []
  367. total.value = 0
  368. queryParams.pageNo = 1
  369. queryParams.pageSize = 10
  370. // 通知父组件弹窗已关闭
  371. emit('close')
  372. }
  373. // 计算 距离下次保养运行时长 H
  374. const calculateTimePeriod = (item: IotMaintenanceBomVO) => {
  375. if (item.runningTimeRule === 0) {
  376. const totalRunVal = item.totalRunTime ?? item.tempTotalRunTime;
  377. const next = Number(item.nextRunningTime) || 0;
  378. const totalRun = totalRunVal != null ? Number(totalRunVal) : 0;
  379. const lastRun = Number(item.lastRunningTime) || 0;
  380. const result = next - (totalRun - lastRun);
  381. return Number(result.toFixed(2));
  382. }
  383. return typeof item.timePeriod === 'number'
  384. ? Number(item.timePeriod.toFixed(2))
  385. : item.timePeriod;
  386. };
  387. // 计算 距离下次保养公里数 KM
  388. const calculateKiloPeriod = (item: IotMaintenanceBomVO) => {
  389. if (item.mileageRule === 0) {
  390. const totalRunVal = item.totalMileage ?? item.tempTotalMileage;
  391. const next = Number(item.nextRunningKilometers) || 0;
  392. const totalRun = totalRunVal != null ? Number(totalRunVal) : 0;
  393. const lastRun = Number(item.lastRunningKilometers) || 0;
  394. const result = next - (totalRun - lastRun);
  395. return Number(result.toFixed(2));
  396. }
  397. return typeof item.kilometerCycle === 'number'
  398. ? Number(item.kilometerCycle.toFixed(2))
  399. : item.kilometerCycle;
  400. };
  401. // 计算下次保养日期
  402. const calculateNextNaturalDate = (item: IotMaintenanceBomVO): string => {
  403. if (item.naturalDateRule !== 0 || !item.lastNaturalDate || !item.nextNaturalDate) {
  404. return '-'
  405. }
  406. return dayjs(item.lastNaturalDate).add(item.nextNaturalDate, 'day').format('YYYY-MM-DD')
  407. }
  408. // 计算属性:控制时间相关列的显示
  409. const showTimeColumns = computed(() => {
  410. return paginatedList.value.some(item => item.runningTimeRule === 0);
  411. });
  412. // 计算属性:控制里程相关列的显示
  413. const showMileageColumns = computed(() => {
  414. return paginatedList.value.some(item => item.mileageRule === 0);
  415. });
  416. // 计算属性:自然日期相关列的显示
  417. const showNaturalDateColumns = computed(() => {
  418. return paginatedList.value.some(item => item.naturalDateRule === 0);
  419. });
  420. /** 搜索按钮操作 */
  421. const handleQuery = () => {
  422. queryParams.pageNo = 1
  423. // getList()
  424. }
  425. const choose = (row: DictDataVO) => {
  426. emit('choose', row)
  427. dialogVisible.value = false
  428. }
  429. /** 重置按钮操作 */
  430. const resetQuery = () => {
  431. queryFormRef.value.resetFields()
  432. handleQuery()
  433. }
  434. // 判断是否为负数的辅助函数
  435. const isNegative = (value: any): boolean => {
  436. if (value === null || value === undefined || value === '') return false;
  437. const num = Number(value);
  438. return !isNaN(num) && num < 0;
  439. };
  440. // 监听分页数据变化,重新设置列宽
  441. watch(paginatedList, () => {
  442. nextTick(() => {
  443. setColumnWidths();
  444. });
  445. });
  446. // 监听动态列的变化
  447. watch([showTimeColumns, showMileageColumns, showNaturalDateColumns], () => {
  448. nextTick(setColumnWidths);
  449. });
  450. </script>
  451. <style lang="scss" scoped>
  452. /* 设备信息卡片样式 */
  453. .device-info-card {
  454. display: flex;
  455. flex-wrap: wrap;
  456. gap: 24px; /* 项间距 */
  457. margin-bottom: 16px;
  458. padding: 16px;
  459. background-color: #f8f9fa;
  460. border-radius: 8px;
  461. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  462. .info-item {
  463. display: flex;
  464. align-items: center;
  465. .info-label {
  466. font-weight: 600;
  467. color: #606266;
  468. margin-right: 8px;
  469. white-space: nowrap;
  470. }
  471. .info-value {
  472. font-weight: 500;
  473. color: #303133;
  474. padding: 4px 12px;
  475. background: #ffffff;
  476. border-radius: 4px;
  477. border: 1px solid #ebeef5;
  478. min-width: 200px;
  479. }
  480. }
  481. }
  482. /* 分组表头样式 */
  483. :deep(.el-table .el-table__header .el-table-column--group) {
  484. background-color: #f5f7fa;
  485. font-weight: bold;
  486. > .cell {
  487. font-weight: bold;
  488. color: #303133;
  489. }
  490. background-color: #f0f7ff;
  491. border: 1px solid #409eff !important;
  492. box-shadow: 0 2px 4px rgba(64, 158, 255, 0.2);
  493. > .cell {
  494. font-weight: 700;
  495. color: #1d63dc;
  496. }
  497. }
  498. /* 加深所有单元格边框 */
  499. :deep(.el-table__header th) {
  500. border: 1px solid #c0c4cc !important;
  501. }
  502. /* 分组内部单元格特殊样式 */
  503. :deep(.el-table-column--group .el-table__cell) {
  504. border-top: 1px dashed #a0cfff !important;
  505. }
  506. /* 分组间垂直线 */
  507. :deep(.el-table-column--group) {
  508. position: relative;
  509. &::after {
  510. content: "";
  511. position: absolute;
  512. right: -1px;
  513. top: 0;
  514. bottom: 0;
  515. width: 2px;
  516. background: linear-gradient(to bottom, #409eff, #79bbff);
  517. }
  518. }
  519. /* 新版CSS解决方案 */
  520. .fixed-height-dialog {
  521. :deep(.el-dialog) {
  522. width: 1500px !important; /* 固定宽度 */
  523. height: 85vh !important; /* 使用视口高度 */
  524. display: flex;
  525. flex-direction: column;
  526. margin-top: 5vh !important; /* 垂直居中 */
  527. }
  528. /* 添加媒体查询,确保在小屏幕上也能完整显示 */
  529. @media (max-width: 1500px) {
  530. :deep(.el-dialog) {
  531. width: 95% !important;
  532. max-width: 1500px; /* 仍然限制最大宽度 */
  533. }
  534. }
  535. :deep(.el-dialog__header) {
  536. padding: 20px;
  537. flex-shrink: 0;
  538. }
  539. :deep(.el-dialog__body) {
  540. flex: 1;
  541. padding: 10px 20px;
  542. display: flex;
  543. flex-direction: column;
  544. overflow: hidden; /* 防止内容溢出 */
  545. }
  546. }
  547. /* 表格容器 */
  548. .table-container {
  549. flex: 1;
  550. overflow: auto;
  551. position: relative;
  552. .scrollable-table {
  553. width: 100%; /* 自适应宽度 */
  554. :deep(.el-table__header) {
  555. th {
  556. white-space: nowrap !important; /* 强制表头不换行 */
  557. text-overflow: ellipsis;
  558. overflow: hidden;
  559. padding: 8px 0; /* 增加内边距 */
  560. }
  561. }
  562. :deep(.el-table__body) {
  563. td {
  564. white-space: nowrap !important; /* 强制内容不换行 */
  565. text-overflow: ellipsis;
  566. overflow: hidden;
  567. padding: 8px 0; /* 增加内边距 */
  568. }
  569. }
  570. :deep(.el-table__body-wrapper) {
  571. overflow-x: hidden !important; /* 隐藏X轴滚动条 */
  572. }
  573. }
  574. }
  575. /* 分页样式 */
  576. .pagination-footer {
  577. margin-top: 15px;
  578. flex-shrink: 0;
  579. }
  580. /* 响应式处理 - 确保在小屏幕上布局合理 */
  581. @media (max-width: 1500px) {
  582. .fixed-height-dialog :deep(.el-dialog) {
  583. width: 95% !important;
  584. max-width: 98vw;
  585. }
  586. .table-container {
  587. overflow-x: auto;
  588. }
  589. }
  590. /* 负数值样式 */
  591. .negative-value {
  592. color: #f56c6c;
  593. font-weight: 600;
  594. }
  595. </style>