|
@@ -0,0 +1,455 @@
|
|
|
+<template>
|
|
|
+ <Dialog
|
|
|
+ :flag="flag"
|
|
|
+ width="90%"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ ref="dialog"
|
|
|
+ class="dialog-wrapper"
|
|
|
+ :tabindex="0"
|
|
|
+ @keyup.esc="dialogHide"
|
|
|
+ >
|
|
|
+ <div class="title">
|
|
|
+ <span class="title-text">{{ title }}</span>
|
|
|
+ <i
|
|
|
+ class="icon el-icon-close"
|
|
|
+ @click="dialogHide"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="content">
|
|
|
+ <div
|
|
|
+ v-loading="loading"
|
|
|
+ element-loading-text="拼命加载中"
|
|
|
+ element-loading-spinner="el-icon-loading"
|
|
|
+ element-loading-background="rgba(0, 0, 0, 0.8)"
|
|
|
+ class="table-wrapper"
|
|
|
+ >
|
|
|
+ <el-table
|
|
|
+ ref="table"
|
|
|
+ height="calc(100vh - 100px - 60px - 10px)"
|
|
|
+ class="table"
|
|
|
+ :data="dealedTableData"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ fit
|
|
|
+ show-summary
|
|
|
+ :summary-method="summaryRow"
|
|
|
+ :header-cell-class-name="headerCellClass"
|
|
|
+ :row-class-name="rowClass"
|
|
|
+ :cell-class-name="cellClass"
|
|
|
+ :span-method="spanMethod"
|
|
|
+ @cell-click="cellClickHandler"
|
|
|
+ >
|
|
|
+ <el-table-column
|
|
|
+ v-for="column in tableColumns"
|
|
|
+ :key="column.prop"
|
|
|
+ :prop="column.prop"
|
|
|
+ :label="column.label"
|
|
|
+ :width="column.width"
|
|
|
+ :fixed="column.fixed"
|
|
|
+ :formatter="formatter"
|
|
|
+ >
|
|
|
+ <template #header>
|
|
|
+ <el-tooltip
|
|
|
+ :content="column.desc || column.label"
|
|
|
+ placement="top"
|
|
|
+ >
|
|
|
+ <TableHeaderCell
|
|
|
+ :label="column.label"
|
|
|
+ :filter-options="tableDataFilters[column.prop]"
|
|
|
+ :filter-values.sync="filterValues[column.prop]"
|
|
|
+ :sortable="column.sortable"
|
|
|
+ :sort-rule.sync="tableDataSortRules[column.prop]"
|
|
|
+ />
|
|
|
+ </el-tooltip>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import Dialog from '@/layout/components/Dialog/index.vue'
|
|
|
+import TableHeaderCell from '@/components/TableHeaderCell/index.vue'
|
|
|
+import { setTableFilters } from '@/utils/table'
|
|
|
+import { TempQuery } from '@/api/temp'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'TableDialog',
|
|
|
+ components: { Dialog, TableHeaderCell },
|
|
|
+ props: {
|
|
|
+ flag: {
|
|
|
+ type: Boolean,
|
|
|
+ require: true,
|
|
|
+ },
|
|
|
+ queryParams: {
|
|
|
+ type: Array,
|
|
|
+ default: () => [],
|
|
|
+ },
|
|
|
+ title: {
|
|
|
+ type: String,
|
|
|
+ default: '标题',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ tableColumns: [
|
|
|
+ {
|
|
|
+ prop: 'flightNO',
|
|
|
+ label: '航班号',
|
|
|
+ desc: '指航班编号',
|
|
|
+ fixed: 'left',
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'flightDate',
|
|
|
+ label: '航班日期',
|
|
|
+ desc: '指航班计划起飞日期(机票上的静态数据)',
|
|
|
+ fixed: 'left',
|
|
|
+ width: 110,
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'departureTime',
|
|
|
+ label: '起飞时间',
|
|
|
+ desc: '指航班预计起飞时间,动态数据,仅显示最新结果',
|
|
|
+ width: 150,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'sourceAirport',
|
|
|
+ label: '起飞站',
|
|
|
+ desc: '指航班执飞航段的起飞航站,以航站英文三字码显示',
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'targetAirport',
|
|
|
+ label: '目的地',
|
|
|
+ desc: '指航班执飞航段的目的航站,以航站英文三字码显示',
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'passengerNameUpcase',
|
|
|
+ label: '旅客姓名',
|
|
|
+ desc: '指旅客姓名的拼音大写',
|
|
|
+ width: 150,
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'bagSN',
|
|
|
+ label: '行李牌号',
|
|
|
+ desc: '指行李的10位数字行李牌号码',
|
|
|
+ width: 110,
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'specialType',
|
|
|
+ label: '特殊行李类型',
|
|
|
+ desc: '指有别于普通托运行李的特殊行李分类,包括(装笼动物、机组行李、易碎行李、VIP行李等),参考BSM报文.E项说明',
|
|
|
+ width: 115,
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'deleted',
|
|
|
+ label: '删除',
|
|
|
+ desc: '指旅客是否取消值机托运,根据BSM报文状态是否有DEL判断,已删除的行李记录为斜体灰色字体',
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'activated',
|
|
|
+ label: '激活',
|
|
|
+ desc: '指托运行李是否被激活,参照BSM报文.S项说明',
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'bagWeight',
|
|
|
+ label: '重量',
|
|
|
+ desc: '指托运行李的重量,参照BSM报文.W项说明',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'latestStatus',
|
|
|
+ label: '最新状态',
|
|
|
+ desc: '指托运行李的当前查询时间所在的节点状态',
|
|
|
+ width: 110,
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'bagLocation',
|
|
|
+ label: '最新位置',
|
|
|
+ desc: '指托运行李的当前查询时间所在的节点状态的识读位置代号',
|
|
|
+ width: 110,
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'U_Device_ID',
|
|
|
+ label: '容器编号',
|
|
|
+ desc: '指集装器ID信息',
|
|
|
+ width: 110,
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'preFlightNO',
|
|
|
+ label: '中转进航班',
|
|
|
+ desc: '指有中转行李转出的进港航班号',
|
|
|
+ width: 110,
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'transferFlightNO',
|
|
|
+ label: '中转出航班',
|
|
|
+ desc: '指有中转行李转入的离港航班号',
|
|
|
+ width: 110,
|
|
|
+ filterable: true,
|
|
|
+ sortable: true,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ tableData: [],
|
|
|
+ tableDataFilters: {},
|
|
|
+ filterValues: {},
|
|
|
+ tableDataSortRules: {},
|
|
|
+ spanArr: [],
|
|
|
+ loading: false,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ dealedTableData() {
|
|
|
+ const filtered = this.tableData.filter(item => {
|
|
|
+ let flag = true
|
|
|
+ Object.entries(this.filterValues).forEach(([key, arr]) => {
|
|
|
+ if (arr.length && !arr.includes(String(item[key]))) {
|
|
|
+ flag = false
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return flag
|
|
|
+ })
|
|
|
+ const sortRules = Object.entries(this.tableDataSortRules).reduce(
|
|
|
+ (pre, [key, value]) => {
|
|
|
+ if (value) {
|
|
|
+ pre[0].push(key)
|
|
|
+ value = value === 'ascending' ? 'asc' : 'desc'
|
|
|
+ pre[1].push(value)
|
|
|
+ }
|
|
|
+ return pre
|
|
|
+ },
|
|
|
+ [[], []]
|
|
|
+ )
|
|
|
+ return this._.orderBy(filtered, sortRules[0], sortRules[1])
|
|
|
+ },
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ flag(val) {
|
|
|
+ if (val) {
|
|
|
+ this.tableData = []
|
|
|
+ this.getTableData()
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs['dialog']?.focus()
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ dealedTableData: {
|
|
|
+ handler(val) {
|
|
|
+ this.spanArr = []
|
|
|
+ let contactDot = 0
|
|
|
+ val.forEach((item, index, arr) => {
|
|
|
+ if (index === 0) {
|
|
|
+ this.spanArr.push(1)
|
|
|
+ } else {
|
|
|
+ if (
|
|
|
+ item['flightNO'] === arr[index - 1]['flightNO'] &&
|
|
|
+ item['flightDate'] === arr[index - 1]['flightDate'] &&
|
|
|
+ item['passengerNameUpcase'] ===
|
|
|
+ arr[index - 1]['passengerNameUpcase'] &&
|
|
|
+ item['checkInNO'] === arr[index - 1]['checkInNO'] &&
|
|
|
+ item['bagWeight'] === arr[index - 1]['bagWeight']
|
|
|
+ ) {
|
|
|
+ this.spanArr[contactDot] += 1
|
|
|
+ this.spanArr.push(0)
|
|
|
+ } else {
|
|
|
+ this.spanArr.push(1)
|
|
|
+ contactDot = index
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ deep: true,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ Object.values(this.tableColumns).forEach(
|
|
|
+ ({ prop, filterable, sortable }) => {
|
|
|
+ if (filterable) {
|
|
|
+ this.$set(this.tableDataFilters, prop, [])
|
|
|
+ this.$set(this.filterValues, prop, [])
|
|
|
+ }
|
|
|
+ if (sortable) {
|
|
|
+ this.$set(this.tableDataSortRules, prop, '')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ updated() {
|
|
|
+ // table数据更新
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.table?.doLayout()
|
|
|
+ })
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ dialogHide() {
|
|
|
+ this.$emit('update:flag', false)
|
|
|
+ },
|
|
|
+ spanMethod({ row, column, rowIndex, columnIndex }) {
|
|
|
+ if (['passengerNameUpcase', 'bagWeight'].includes(column.property)) {
|
|
|
+ const _row = this.spanArr[rowIndex]
|
|
|
+ const _col = _row > 0 ? 1 : 0
|
|
|
+ return {
|
|
|
+ rowspan: _row,
|
|
|
+ colspan: _col,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 给表头单元格加上 ascending 或 descending 使用 element 自带的排序箭头变色
|
|
|
+ headerCellClass({ row, column, rowIndex, columnIndex }) {
|
|
|
+ const classes = []
|
|
|
+ const rule = this.tableDataSortRules[column.property]
|
|
|
+ if (rule) {
|
|
|
+ classes.push(rule)
|
|
|
+ }
|
|
|
+ return classes.join(' ')
|
|
|
+ },
|
|
|
+ rowClass({ row, rowIndex }) {
|
|
|
+ const classes = []
|
|
|
+ if (row.deleted === 'DEL') {
|
|
|
+ classes.push('bgl-deleted')
|
|
|
+ }
|
|
|
+ return classes.join(' ')
|
|
|
+ },
|
|
|
+ cellClass({ row, column, rowIndex, columnIndex }) {
|
|
|
+ const classes = []
|
|
|
+ return classes.join(' ')
|
|
|
+ },
|
|
|
+ cellClickHandler(row, column, cell, event) {},
|
|
|
+ formatter(row, column, cellValue) {
|
|
|
+ switch (column.property) {
|
|
|
+ case 'departureTime':
|
|
|
+ return (cellValue ?? '').replace('T', ' ')
|
|
|
+ case 'deleted':
|
|
|
+ return cellValue === 'DEL' ? cellValue : ''
|
|
|
+ case 'activated':
|
|
|
+ return cellValue === 1 ? '激活' : '未激活'
|
|
|
+ default:
|
|
|
+ return cellValue ?? ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 统计行数
|
|
|
+ summaryRow() {
|
|
|
+ const rowNum = this.dealedTableData.length
|
|
|
+ return ['合计', `共${rowNum}件`]
|
|
|
+ },
|
|
|
+ async getTableData() {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ const {
|
|
|
+ code,
|
|
|
+ returnData: { listValues },
|
|
|
+ message,
|
|
|
+ } = await TempQuery({
|
|
|
+ id: 1844,
|
|
|
+ dataContent: this.queryParams,
|
|
|
+ })
|
|
|
+ if (String(code) !== '0') {
|
|
|
+ throw new Error(message || '获取数据失败')
|
|
|
+ }
|
|
|
+ this.tableData = this._.cloneDeep(listValues)
|
|
|
+ setTableFilters(this.tableData, this.tableDataFilters)
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(error.message || '失败')
|
|
|
+ }
|
|
|
+ this.loading = false
|
|
|
+ },
|
|
|
+ },
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.dialog-wrapper {
|
|
|
+ width: 100%;
|
|
|
+ height: calc(100vh - 100px);
|
|
|
+ .title {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ .icon {
|
|
|
+ margin-right: 10px;
|
|
|
+ font-size: 20px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .content {
|
|
|
+ margin: 0 20px;
|
|
|
+ }
|
|
|
+ ::v-deep .table {
|
|
|
+ width: 100%;
|
|
|
+ .cell {
|
|
|
+ padding: 0;
|
|
|
+ text-align: center;
|
|
|
+ font-size: 14px;
|
|
|
+ font-family: Helvetica, 'Microsoft YaHei';
|
|
|
+ letter-spacing: 0;
|
|
|
+ }
|
|
|
+ .cell-click {
|
|
|
+ cursor: pointer;
|
|
|
+ color: #2d7cff;
|
|
|
+ &.cell-clicked {
|
|
|
+ color: purple;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .el-table__header-wrapper,
|
|
|
+ .el-table__fixed-header-wrapper {
|
|
|
+ .cell {
|
|
|
+ font-weight: bold;
|
|
|
+ color: #101116;
|
|
|
+ }
|
|
|
+
|
|
|
+ .has-gutter {
|
|
|
+ tr {
|
|
|
+ .bgl-huang {
|
|
|
+ background: #fcf0b1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-table__body-wrapper,
|
|
|
+ .el-table__fixed-body-wrapper {
|
|
|
+ tr.bgl-deleted {
|
|
|
+ background: #d2d6df;
|
|
|
+ td {
|
|
|
+ background: #d2d6df;
|
|
|
+ font-style: italic;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-table__body tr.hover-row.current-row > td,
|
|
|
+ .el-table__body tr.hover-row.el-table__row--striped.current-row > td,
|
|
|
+ .el-table__body tr.hover-row.el-table__row--striped > td,
|
|
|
+ .el-table__body tr.hover-row > td {
|
|
|
+ background-color: red;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|