index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  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 { tableColumns, tableData, getTableData } = useAirportTable(
  138. props.name,
  139. formData
  140. )
  141. const finishedCount = ref(0)
  142. const { getWarningRules } = useFlightState(props.name, tableData, finishedCount)
  143. useLoop(
  144. [
  145. getAirportCount,
  146. getTableData,
  147. // getWarningRules,
  148. ],
  149. 'airport',
  150. [formData]
  151. )
  152. const countFlag = ref(false)
  153. const { tableColumnFormatter, tableDataFormatter } = useFormatter(countFlag)
  154. // const UTCFlag = ref(true)
  155. const summaryMethod: SummaryMethod<CommonData> = ({ columns, data }) => {
  156. const sums: string[] = []
  157. columns.forEach((column, index) => {
  158. const countColumn = tableColumns.value.find(
  159. col => column.property === col.columnName && col.needCount
  160. )
  161. if (countColumn) {
  162. if (countColumn.countMode === 'split') {
  163. let sumArr = data.reduce(
  164. (prev: number[], curr: CommonData) => {
  165. const cellData = curr[column.property]
  166. if (typeof cellData === 'string') {
  167. const splitData = cellData.split('/')
  168. splitData.forEach((str, i) => {
  169. const num = Number(str)
  170. if (!Number.isNaN(num)) {
  171. if (prev[i]) {
  172. prev[i] += num
  173. } else {
  174. prev[i] = num
  175. }
  176. }
  177. })
  178. }
  179. return prev
  180. },
  181. [0]
  182. )
  183. const matched = column.label.match(/(?<=\()\S+(?=\))/)
  184. if (matched && !countFlag.value) {
  185. const machedStr = matched[0]
  186. const countIndex = machedStr.split('/').findIndex(str => str === '件')
  187. if (countIndex > -1 && countIndex < sumArr.length) {
  188. sumArr.splice(countIndex, 1)
  189. }
  190. }
  191. sums[index] = sumArr.join('/')
  192. } else {
  193. const sumNumber = data.reduce((prev: number, curr: CommonData) => {
  194. const cellData = curr[column.property]
  195. if (countColumn.countMode === 'all') {
  196. return prev + 1
  197. }
  198. if (countColumn.countMode === 'notNull') {
  199. return cellData ? prev + 1 : prev
  200. }
  201. const value = Number(cellData)
  202. if (!Number.isNaN(value)) {
  203. prev += value
  204. }
  205. return prev
  206. }, 0)
  207. sums[index] = sumNumber.toString()
  208. }
  209. }
  210. })
  211. sums[0] = '合计:' + (sums[0] ?? '')
  212. return sums
  213. }
  214. /* 离港视图默认的排序方式:
  215. * 0.国内离港-有收运核单的排在前
  216. * 1.已起飞排在前
  217. * 2.未起飞中已装机在前
  218. * 3.已起飞和未起飞分类中各自按照预计起飞时间排序
  219. */
  220. const defaultDepartureSortFunction = (a: CommonData, b: CommonData) => {
  221. const departureTimeCompare = (a: CommonData, b: CommonData) => {
  222. if (a.planDepartureTime) {
  223. if (b.planDepartureTime) {
  224. if (a.planDepartureTime > b.planDepartureTime) {
  225. return 1
  226. } else if (a.planDepartureTime < b.planDepartureTime) {
  227. return -1
  228. } else {
  229. return 0
  230. }
  231. } else {
  232. return -1
  233. }
  234. } else if (b.planDepartureTime) {
  235. return 1
  236. } else {
  237. return 0
  238. }
  239. }
  240. const loadCompare = (a: CommonData, b: CommonData) => {
  241. if (a.loadPlaneSureTime) {
  242. if (b.loadPlaneSureTime) {
  243. return departureTimeCompare(a, b)
  244. } else {
  245. return -1
  246. }
  247. } else if (b.loadPlaneSureTime) {
  248. return 1
  249. } else {
  250. return departureTimeCompare(a, b)
  251. }
  252. }
  253. const takeOffCompare = (a: CommonData, b: CommonData) => {
  254. if (a.hasTakenOff === 'Y') {
  255. if (b.hasTakenOff === 'Y') {
  256. return departureTimeCompare(a, b)
  257. } else {
  258. return -1
  259. }
  260. } else if (b.hasTakenOff === 'Y') {
  261. return 1
  262. } else {
  263. return loadCompare(a, b)
  264. }
  265. }
  266. const receiveCompare = (a: CommonData, b: CommonData) => {
  267. if (a.receiveSure) {
  268. if (b.receiveSure) {
  269. return takeOffCompare(a, b)
  270. } else {
  271. return -1
  272. }
  273. } else if (b.receiveSure) {
  274. return 1
  275. } else {
  276. return takeOffCompare(a, b)
  277. }
  278. }
  279. const receiveSureCompare = (a: CommonData, b: CommonData) => {
  280. if (a.receiveSure1) {
  281. if (b.receiveSure1) {
  282. return takeOffCompare(a, b)
  283. } else {
  284. return -1
  285. }
  286. } else if (b.receiveSure1) {
  287. return 1
  288. } else {
  289. return takeOffCompare(a, b)
  290. }
  291. }
  292. const enterCompare = (a: CommonData, b: CommonData) => {
  293. if (a.enterPark) {
  294. if (b.enterPark) {
  295. return receiveSureCompare(a, b)
  296. } else {
  297. return -1
  298. }
  299. } else if (b.enterPark) {
  300. return 1
  301. } else {
  302. return receiveSureCompare(a, b)
  303. }
  304. }
  305. // return isInternational ? enterCompare(a, b) : receiveCompare(a, b)
  306. return takeOffCompare(a, b)
  307. }
  308. const defaultArrivalSortFunction = (a: CommonData, b: CommonData) => {
  309. const landingTimeCompare = (a: CommonData, b: CommonData) => {
  310. if (a.planLandingTime) {
  311. if (b.planLandingTime) {
  312. if (a.planLandingTime > b.planLandingTime) {
  313. return 1
  314. } else if (a.planLandingTime < b.planLandingTime) {
  315. return -1
  316. } else {
  317. return 0
  318. }
  319. } else {
  320. return -1
  321. }
  322. } else if (b.planLandingTime) {
  323. return 1
  324. } else {
  325. return 0
  326. }
  327. }
  328. const tallyCompare = (a: CommonData, b: CommonData) => {
  329. if (a.tally) {
  330. if (b.tally) {
  331. return landingTimeCompare(a, b)
  332. } else {
  333. return -1
  334. }
  335. } else if (b.tally) {
  336. return 1
  337. } else {
  338. return landingTimeCompare(a, b)
  339. }
  340. }
  341. const unloadCompare = (a: CommonData, b: CommonData) => {
  342. if (a.unLoad) {
  343. if (b.unLoad) {
  344. return tallyCompare(a, b)
  345. } else {
  346. return -1
  347. }
  348. } else if (b.unLoad) {
  349. return 1
  350. } else {
  351. return tallyCompare(a, b)
  352. }
  353. }
  354. return unloadCompare(a, b)
  355. }
  356. const filterSortOptions = computed(() => ({
  357. defaultFilterValueMap,
  358. extraFilterValueMap: flightStateFilter,
  359. defaultSortFunction: isDeparture ? defaultDepartureSortFunction : undefined,
  360. defaultSortRuleMap: isDeparture
  361. ? undefined
  362. : {
  363. planLandingTime: 'ascending',
  364. },
  365. }))
  366. const sortRuleMap = ref({})
  367. const sortRuleChangeHandler = map => {
  368. sortRuleMap.value = map
  369. }
  370. const { columnChecked } = useTableColumnSet(tableColumns)
  371. const { rowClass, cellClass } = useTableStyle(props.name)
  372. const rowClassName = params => {
  373. const { row, rowIndex } = params
  374. const classes: string[] = []
  375. if (
  376. (row.hasTakenOff === 'Y' || row.hasLanded === 'Y') &&
  377. (['planDepartureTime', 'planLandingTime'].some(
  378. key => sortRuleMap.value[key] === 'ascending'
  379. ) ||
  380. Object.keys(sortRuleMap.value).length === 0) &&
  381. finishedCount.value < tableData.value.length &&
  382. rowIndex === finishedCount.value - 1
  383. ) {
  384. classes.push('underline-red')
  385. }
  386. return `${rowClass(params)} ${classes.join(' ')}`
  387. }
  388. const { cellClickHandler } = useTableCellClick(props.name)
  389. const route = useRoute()
  390. const { savedTableFilterValueMap } = useTableSettingsStore()
  391. const defaultFilterValueMap = savedTableFilterValueMap[route.path]
  392. const flightStateFilter = computed<{} | { [x: string]: string[] }>(() => {
  393. switch (formData.flightState) {
  394. case 'hasTakenOff':
  395. return { hasTakenOff: ['Y'] }
  396. case 'hasNotTakenOff':
  397. return { hasTakenOff: ['N'] }
  398. case 'hasLanded':
  399. return { hasLanded: ['Y'] }
  400. case 'hasNotLanded':
  401. return { hasLanded: ['N'] }
  402. case 'canceled':
  403. return { flightState: ['CAN'] }
  404. case 'groundService':
  405. return { groundService: ['Y'] }
  406. case 'groundServiceSZ':
  407. return { groundServiceSZ: ['Y'] }
  408. default:
  409. return {}
  410. }
  411. })
  412. const cacheKeys = ['IATACode']
  413. const permissionMap = {
  414. DepartureAirport: {
  415. count:
  416. 'number_of_pieces_displayed_in_domestic_departure_terminal_view_button',
  417. UTC: 'turn_on_utc_in_view_of_domestic_departure_terminal_button',
  418. columnSet: 'domestic_departure_terminal_view_column_setting_button',
  419. },
  420. ArrivalAirport: {
  421. count:
  422. 'number_of_pieces_displayed_in_domestic_inbound_terminal_view_button',
  423. UTC: 'turn_on_utc_in_view_of_domestic_inbound_terminal_button',
  424. columnSet: 'domestic_inbound_terminal_view_column_setting_button',
  425. },
  426. InternationalDepartureAirport: {
  427. count:
  428. 'number_of_pieces_displayed_in_international_departure_terminal_view_button',
  429. UTC: 'international_departure_terminal_view_opens_utc_button',
  430. columnSet: 'international_departure_terminal_view_column_setting_button',
  431. },
  432. InternationalArrivalAirport: {
  433. count:
  434. 'number_of_display_pieces_of_international_inbound_terminal_view_button',
  435. UTC: 'the_view_of_international_inbound_terminal_opens_utc_button',
  436. columnSet: 'view_column_setting_of_international_inbound_terminal_button',
  437. },
  438. InternationalTransferDepartureAirport: {
  439. count:
  440. 'number_of_pieces_displayed_in_international_transfer_terminal_view_button',
  441. UTC: 'international_transfer_terminal_view_opens_utc_button',
  442. columnSet: 'international_transfer_terminal_view_column_setting_button',
  443. },
  444. }
  445. const getPermission = (type?: string) => {
  446. return [permissionMap[props.name][type]]
  447. }
  448. const tableRef = ref<InstanceType<typeof SimpleTable> | null>(null)
  449. const hasSetTableScroll = ref(false)
  450. watch(
  451. [() => formData.startDate, () => formData.endDate],
  452. ([startDate, endDate], [preStartDate, preEndDate]) => {
  453. if (startDate !== preStartDate || endDate !== preEndDate) {
  454. hasSetTableScroll.value = false
  455. }
  456. }
  457. )
  458. watch(tableData, async () => {
  459. await nextTick()
  460. if (hasSetTableScroll.value || !finishedCount.value) {
  461. return
  462. }
  463. if (tableRef.value?.table) {
  464. tableRef.value?.table?.setScrollTop((finishedCount.value - 1) * 50)
  465. }
  466. hasSetTableScroll.value = true
  467. })
  468. </script>
  469. <style lang="scss" scoped>
  470. @import './index.scss';
  471. </style>