index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. <template>
  2. <div class="airport-view">
  3. <div class="airport-header">
  4. <AirportForm :name="name" @form-data-change="formDataChangeHandler" />
  5. <div class="airport-count">
  6. <CountBox
  7. :count-number="airportCount.flightNum"
  8. label="今日计划航班数(班)"
  9. :length="3"
  10. />
  11. <CountBox
  12. :count-number="airportCount.finishFlightNum"
  13. label="已完成航班数(班)"
  14. :length="3"
  15. />
  16. <CountBox
  17. :count-number="airportCount.weight"
  18. :label="`已${isDeparture ? '装载' : '卸载'}重量(吨)`"
  19. :length="4"
  20. />
  21. </div>
  22. <div class="airport-settings">
  23. <div v-permission="getPermission('count')">
  24. <CommonSwitch v-model:flag="countFlag" label="显示件数" />
  25. </div>
  26. <!-- <div v-permission="getPermission('UTC')">
  27. <CommonSwitch v-model:flag="UTCFlag" label="开启UTC" />
  28. </div> -->
  29. <div v-permission="getPermission('columnSet')">
  30. <ColumnSet
  31. :table-columns="tableColumns"
  32. @checked-submit="columnChecked"
  33. />
  34. </div>
  35. </div>
  36. </div>
  37. <div class="airport-table">
  38. <SimpleTable
  39. ref="tableRef"
  40. :columns="tableColumns"
  41. :data="tableData"
  42. row-key="rowKey"
  43. sequence
  44. highlight-current-row
  45. scrollbar-always-on
  46. :stripe="false"
  47. show-summary
  48. :summary-method="summaryMethod"
  49. :cache-keys="cacheKeys"
  50. :filter-sort-options="filterSortOptions"
  51. :label-formatter="tableColumnFormatter"
  52. :row-class-name="rowClassName"
  53. :cell-class-name="cellClass"
  54. :column-props="{ formatter: tableDataFormatter }"
  55. @sort-rule-change="sortRuleChangeHandler"
  56. @cell-click="cellClickHandler"
  57. />
  58. </div>
  59. </div>
  60. </template>
  61. <script lang="tsx" setup>
  62. import AirportForm from './AirportForm.vue'
  63. import ColumnSet from '@/components/ColumnSet/index.vue'
  64. import CountBox from '../../components/CountBox/index.vue'
  65. import CommonSwitch from '../../components/CommonSwitch/index.vue'
  66. import SimpleTable from '@/components/SimpleTable/index.vue'
  67. import { useTableColumnSet } from '@/hooks/useTableColumnSet'
  68. import { useAirportTable } from './useAirportTable'
  69. import { useTableStyle } from '../../hooks/useTableStyle'
  70. import { useTableCellClick } from '../../hooks/useTableCellClick'
  71. import { useFormatter } from './useFormatter'
  72. import { CommonData } from '~/common'
  73. import { useLoop } from '@/hooks/useLoop'
  74. import { useTableSettingsStore } from '@/store/tableSettings'
  75. import { useFlightState } from './useFlightState'
  76. import { Query } from '@/api/webApi'
  77. import { ElMessage, SummaryMethod } from 'element-plus'
  78. const props = defineProps({
  79. name: {
  80. type: String,
  81. required: true,
  82. },
  83. })
  84. // const isInternational = props.name.includes('International')
  85. const isDeparture = props.name.includes('Departure')
  86. const formData: CommonData = reactive({})
  87. const formDataChangeHandler = (data: CommonData) => {
  88. Object.assign(formData, data)
  89. }
  90. const airportCount = reactive({
  91. flightNum: 0,
  92. finishFlightNum: 0,
  93. weight: 0,
  94. })
  95. const getAirportCount = async () => {
  96. try {
  97. const { startDate, endDate } = formData
  98. if (typeof startDate !== 'string' || typeof endDate !== 'string') {
  99. throw new Error('Type Error: date must be string')
  100. }
  101. const dataContent = [startDate.slice(0, 10), endDate.slice(0, 10)]
  102. const {
  103. code,
  104. returnData: { listValues },
  105. message,
  106. } = await Query({
  107. id:
  108. DATACONTENT_ID[
  109. `${props.name.slice(0, 1).toLowerCase() + props.name.slice(1)}Count`
  110. ],
  111. dataContent,
  112. })
  113. if (Number(code) !== 0) {
  114. throw new Error(message || '失败')
  115. }
  116. if (!listValues.length) {
  117. ElMessage.info('未查询到统计信息')
  118. airportCount.flightNum = airportCount.finishFlightNum = airportCount.weight = 0
  119. return
  120. }
  121. const { flightNum, finishFlightNum, weight } = listValues[0]
  122. airportCount.flightNum = flightNum ?? 0
  123. airportCount.finishFlightNum = finishFlightNum ?? 0
  124. if (typeof weight === 'number' && weight > 0) {
  125. if (weight <= 1000) {
  126. airportCount.weight = 1
  127. } else {
  128. airportCount.weight = Math.round(weight / 1000)
  129. }
  130. } else {
  131. airportCount.weight = 0
  132. }
  133. } catch (error) {
  134. console.error(error)
  135. }
  136. }
  137. const {
  138. tableColumns,
  139. tableColumnsCountIndexMap,
  140. tableData,
  141. getTableData,
  142. } = useAirportTable(props.name, formData)
  143. const finishedCount = ref(0)
  144. const { warningNodesMap, getWarningNodes } = useFlightState(
  145. props.name,
  146. tableData,
  147. finishedCount,
  148. formData
  149. )
  150. useLoop([getAirportCount, getTableData, getWarningNodes], 'airport', [formData])
  151. const countFlag = ref(false)
  152. const { tableColumnFormatter, tableDataFormatter } = useFormatter(
  153. countFlag,
  154. tableColumnsCountIndexMap
  155. )
  156. // const UTCFlag = ref(true)
  157. const summaryMethod: SummaryMethod<CommonData> = ({ columns, data }) => {
  158. const sums: string[] = []
  159. columns.forEach((column, index) => {
  160. const countColumn = tableColumns.value.find(
  161. col => column.property === col.columnName && col.needCount
  162. )
  163. if (countColumn) {
  164. if (countColumn.countMode === 'split') {
  165. let sumArr = data.reduce(
  166. (prev: number[], curr: CommonData) => {
  167. const cellData = curr[column.property]
  168. if (typeof cellData === 'string') {
  169. const splitData = cellData.split('/')
  170. splitData.forEach((str, i) => {
  171. const num = Number(str)
  172. if (!Number.isNaN(num)) {
  173. if (prev[i]) {
  174. prev[i] += num
  175. } else {
  176. prev[i] = num
  177. }
  178. }
  179. })
  180. }
  181. return prev
  182. },
  183. [0]
  184. )
  185. // const matched = column.label.match(/(?<=\()\S+(?=\))/)
  186. // if (matched && !countFlag.value) {
  187. // const machedStr = matched[0]
  188. // const countIndex = machedStr.split('/').findIndex(str => str === '件')
  189. // if (countIndex > -1) {
  190. // sumArr.splice(countIndex, 1)
  191. // }
  192. // }
  193. if (!countFlag.value) {
  194. const countIndex = tableColumnsCountIndexMap[column.property]
  195. if (countIndex) {
  196. sumArr.splice(countIndex, 1)
  197. }
  198. }
  199. sums[index] = sumArr.join('/')
  200. } else {
  201. const sumNumber = data.reduce((prev: number, curr: CommonData) => {
  202. const cellData = curr[column.property]
  203. if (countColumn.countMode === 'all') {
  204. return prev + 1
  205. }
  206. if (countColumn.countMode === 'notNull') {
  207. return cellData ? prev + 1 : prev
  208. }
  209. const value = Number(cellData)
  210. if (!Number.isNaN(value)) {
  211. prev += value
  212. }
  213. return prev
  214. }, 0)
  215. sums[index] = sumNumber.toString()
  216. }
  217. }
  218. })
  219. sums[0] = '合计:' + (sums[0] ?? '')
  220. return sums
  221. }
  222. /* 离港视图默认的排序方式:
  223. * 0.国内离港-有收运核单的排在前
  224. * 1.已起飞排在前
  225. * 2.未起飞中已装机在前
  226. * 3.已起飞和未起飞分类中各自按照预计起飞时间排序
  227. */
  228. const defaultDepartureSortFunction = (a: CommonData, b: CommonData) => {
  229. const departureTimeCompare = (a: CommonData, b: CommonData) => {
  230. if (a.planDepartureTime) {
  231. if (b.planDepartureTime) {
  232. if (a.planDepartureTime > b.planDepartureTime) {
  233. return 1
  234. } else if (a.planDepartureTime < b.planDepartureTime) {
  235. return -1
  236. } else {
  237. return 0
  238. }
  239. } else {
  240. return -1
  241. }
  242. } else if (b.planDepartureTime) {
  243. return 1
  244. } else {
  245. return 0
  246. }
  247. }
  248. const loadCompare = (a: CommonData, b: CommonData) => {
  249. if (a.loadPlaneSureTime) {
  250. if (b.loadPlaneSureTime) {
  251. return departureTimeCompare(a, b)
  252. } else {
  253. return -1
  254. }
  255. } else if (b.loadPlaneSureTime) {
  256. return 1
  257. } else {
  258. return departureTimeCompare(a, b)
  259. }
  260. }
  261. const takeOffCompare = (a: CommonData, b: CommonData) => {
  262. if (a.hasTakenOff === 'Y') {
  263. if (b.hasTakenOff === 'Y') {
  264. return departureTimeCompare(a, b)
  265. } else {
  266. return -1
  267. }
  268. } else if (b.hasTakenOff === 'Y') {
  269. return 1
  270. } else {
  271. return loadCompare(a, b)
  272. }
  273. }
  274. const receiveCompare = (a: CommonData, b: CommonData) => {
  275. if (a.receiveSure) {
  276. if (b.receiveSure) {
  277. return takeOffCompare(a, b)
  278. } else {
  279. return -1
  280. }
  281. } else if (b.receiveSure) {
  282. return 1
  283. } else {
  284. return takeOffCompare(a, b)
  285. }
  286. }
  287. const receiveSureCompare = (a: CommonData, b: CommonData) => {
  288. if (a.receiveSure1) {
  289. if (b.receiveSure1) {
  290. return takeOffCompare(a, b)
  291. } else {
  292. return -1
  293. }
  294. } else if (b.receiveSure1) {
  295. return 1
  296. } else {
  297. return takeOffCompare(a, b)
  298. }
  299. }
  300. const enterCompare = (a: CommonData, b: CommonData) => {
  301. if (a.enterPark) {
  302. if (b.enterPark) {
  303. return receiveSureCompare(a, b)
  304. } else {
  305. return -1
  306. }
  307. } else if (b.enterPark) {
  308. return 1
  309. } else {
  310. return receiveSureCompare(a, b)
  311. }
  312. }
  313. // return isInternational ? enterCompare(a, b) : receiveCompare(a, b)
  314. return takeOffCompare(a, b)
  315. }
  316. const defaultArrivalSortFunction = (a: CommonData, b: CommonData) => {
  317. const landingTimeCompare = (a: CommonData, b: CommonData) => {
  318. if (a.planLandingTime) {
  319. if (b.planLandingTime) {
  320. if (a.planLandingTime > b.planLandingTime) {
  321. return 1
  322. } else if (a.planLandingTime < b.planLandingTime) {
  323. return -1
  324. } else {
  325. return 0
  326. }
  327. } else {
  328. return -1
  329. }
  330. } else if (b.planLandingTime) {
  331. return 1
  332. } else {
  333. return 0
  334. }
  335. }
  336. const tallyCompare = (a: CommonData, b: CommonData) => {
  337. if (a.tally) {
  338. if (b.tally) {
  339. return landingTimeCompare(a, b)
  340. } else {
  341. return -1
  342. }
  343. } else if (b.tally) {
  344. return 1
  345. } else {
  346. return landingTimeCompare(a, b)
  347. }
  348. }
  349. const unloadCompare = (a: CommonData, b: CommonData) => {
  350. if (a.unLoad) {
  351. if (b.unLoad) {
  352. return tallyCompare(a, b)
  353. } else {
  354. return -1
  355. }
  356. } else if (b.unLoad) {
  357. return 1
  358. } else {
  359. return tallyCompare(a, b)
  360. }
  361. }
  362. return unloadCompare(a, b)
  363. }
  364. const filterSortOptions = computed(() => ({
  365. defaultFilterValueMap,
  366. extraFilterValueMap: flightStateFilter,
  367. defaultSortFunction: isDeparture ? defaultDepartureSortFunction : undefined,
  368. defaultSortRuleMap: isDeparture
  369. ? undefined
  370. : {
  371. planLandingTime: 'ascending',
  372. },
  373. }))
  374. const sortRuleMap = ref({})
  375. const sortRuleChangeHandler = map => {
  376. sortRuleMap.value = map
  377. }
  378. const { columnChecked } = useTableColumnSet(tableColumns)
  379. const { rowClass, cellClass } = useTableStyle(props.name, warningNodesMap)
  380. const rowClassName = params => {
  381. const { row, rowIndex } = params
  382. const classes: string[] = []
  383. if (
  384. (row.hasTakenOff === 'Y' || row.hasLanded === 'Y') &&
  385. (['planDepartureTime', 'planLandingTime'].some(
  386. key => sortRuleMap.value[key] === 'ascending'
  387. ) ||
  388. Object.keys(sortRuleMap.value).length === 0) &&
  389. finishedCount.value < tableData.value.length &&
  390. rowIndex === finishedCount.value - 1
  391. ) {
  392. classes.push('underline-red')
  393. }
  394. return `${rowClass(params)} ${classes.join(' ')}`
  395. }
  396. const { cellClickHandler } = useTableCellClick(props.name)
  397. const route = useRoute()
  398. const tableSettingsStore = useTableSettingsStore()
  399. const { savedTableFilterValueMap } = storeToRefs(tableSettingsStore)
  400. const defaultFilterValueMap = savedTableFilterValueMap.value[route.path]
  401. const flightStateFilter = computed<{} | { [x: string]: string[] }>(() => {
  402. switch (formData.flightState) {
  403. case 'hasTakenOff':
  404. return { hasTakenOff: ['Y'] }
  405. case 'hasNotTakenOff':
  406. return { hasTakenOff: ['N'] }
  407. case 'hasLanded':
  408. return { hasLanded: ['Y'] }
  409. case 'hasNotLanded':
  410. return { hasLanded: ['N'] }
  411. case 'canceled':
  412. return { flightState: ['CAN'] }
  413. case 'groundService':
  414. return { groundService: ['Y'] }
  415. case 'groundServiceSZ':
  416. return { groundServiceSZ: ['Y'] }
  417. default:
  418. return {}
  419. }
  420. })
  421. const cacheKeys = ['IATACode']
  422. const permissionMap = {
  423. DepartureAirport: {
  424. count:
  425. 'number_of_pieces_displayed_in_domestic_departure_terminal_view_button',
  426. UTC: 'turn_on_utc_in_view_of_domestic_departure_terminal_button',
  427. columnSet: 'domestic_departure_terminal_view_column_setting_button',
  428. },
  429. ArrivalAirport: {
  430. count:
  431. 'number_of_pieces_displayed_in_domestic_inbound_terminal_view_button',
  432. UTC: 'turn_on_utc_in_view_of_domestic_inbound_terminal_button',
  433. columnSet: 'domestic_inbound_terminal_view_column_setting_button',
  434. },
  435. InternationalDepartureAirport: {
  436. count:
  437. 'number_of_pieces_displayed_in_international_departure_terminal_view_button',
  438. UTC: 'international_departure_terminal_view_opens_utc_button',
  439. columnSet: 'international_departure_terminal_view_column_setting_button',
  440. },
  441. InternationalArrivalAirport: {
  442. count:
  443. 'number_of_display_pieces_of_international_inbound_terminal_view_button',
  444. UTC: 'the_view_of_international_inbound_terminal_opens_utc_button',
  445. columnSet: 'view_column_setting_of_international_inbound_terminal_button',
  446. },
  447. InternationalDepartureTransferAirport: {
  448. count:
  449. 'number_of_pieces_displayed_in_international_transfer_terminal_view_button',
  450. UTC: 'international_transfer_terminal_view_opens_utc_button',
  451. columnSet: 'international_transfer_terminal_view_column_setting_button',
  452. },
  453. }
  454. const getPermission = (type?: string) => {
  455. return [permissionMap[props.name][type]]
  456. }
  457. const tableRef = ref<InstanceType<typeof SimpleTable> | null>(null)
  458. const hasSetTableScroll = ref(false)
  459. watch(
  460. [() => formData.startDate, () => formData.endDate],
  461. ([startDate, endDate], [preStartDate, preEndDate]) => {
  462. if (startDate !== preStartDate || endDate !== preEndDate) {
  463. hasSetTableScroll.value = false
  464. }
  465. }
  466. )
  467. watch(tableData, async () => {
  468. await nextTick()
  469. if (hasSetTableScroll.value || !finishedCount.value) {
  470. return
  471. }
  472. if (tableRef.value?.table) {
  473. tableRef.value?.table?.setScrollTop((finishedCount.value - 1) * 50)
  474. }
  475. hasSetTableScroll.value = true
  476. })
  477. </script>
  478. <style lang="scss" scoped>
  479. @import './index.scss';
  480. </style>