index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. <template>
  2. <div class="data-query">
  3. <div class="data-query-header">
  4. <div class="manageTitle">{{ title }}</div>
  5. <el-form ref="formRef" :model="formData" class="data-query-form" :rules="rules" @submit.native.prevent>
  6. <div v-if="name === 'freight'" class="form-left">
  7. <el-form-item prop="startDate">
  8. <el-date-picker v-model="formData.startDate" format="YYYY-MM-DD" value-format="YYYY-MM-DD" size="default" type="date" placeholder="开始日期" :prefix-icon="datePreTitle('开始')" :clearable="false" class="pre-text" />
  9. </el-form-item>
  10. <el-form-item prop="endDate">
  11. <el-date-picker v-model="formData.endDate" format="YYYY-MM-DD" value-format="YYYY-MM-DD" :disabled-date="disabledEndDate" size="default" type="date" placeholder="结束日期" :prefix-icon="datePreTitle('结束')" :clearable="false" class="pre-text" />
  12. </el-form-item>
  13. </div>
  14. <div v-if="name === 'waybill'" class="form-left">
  15. <el-form-item>
  16. <el-date-picker v-model="formData.startDate" format="YYYY-MM-DD" value-format="YYYY-MM-DD" size="default" type="date" placeholder="开始日期" :prefix-icon="datePreTitle('开始')" class="pre-text" />
  17. </el-form-item>
  18. <el-form-item>
  19. <el-date-picker v-model="formData.endDate" format="YYYY-MM-DD" value-format="YYYY-MM-DD" :disabled-date="disabledEndDate" size="default" type="date" placeholder="结束日期" :prefix-icon="datePreTitle('结束')" class="pre-text" />
  20. </el-form-item>
  21. </div>
  22. <div v-if="name === 'flight'" class="form-left">
  23. <el-form-item prop="flightDate" style="width: 148px">
  24. <el-date-picker v-model="formData.flightDate" format="YYYY-MM-DD" value-format="YYYY-MM-DD" size="default" type="date" placeholder="请选择航班日期" :clearable="false" />
  25. </el-form-item>
  26. <el-form-item prop="inOrOut" style="width: 108px">
  27. <el-select v-model="formData.inOrOut" size="default">
  28. <el-option value="in" label="进港航班" />
  29. <el-option value="out" label="出港航班" />
  30. </el-select>
  31. </el-form-item>
  32. <el-form-item prop="flightType" style="width: 108px">
  33. <el-select v-model="formData.flightType" size="default" placeholder="航班类型" clearable>
  34. <el-option :value="0" label="货机" />
  35. <el-option :value="1" label="客机" />
  36. <el-option :value="2" label="其他" />
  37. </el-select>
  38. </el-form-item>
  39. <el-form-item prop="sAirport" style="width: 108px">
  40. <el-select v-model="formData.sAirport" :disabled="formData.inOrOut === 'out'" size="default" placeholder="始发站" filterable clearable>
  41. <el-option v-for="{ value, label } in airportOptions" :key="value" :value="value" :label="label" />
  42. </el-select>
  43. </el-form-item>
  44. <el-form-item prop="eAirport" style="width: 108px">
  45. <el-select v-model="formData.eAirport" :disabled="formData.inOrOut === 'in'" size="default" placeholder="目的站" filterable clearable>
  46. <el-option v-for="{ value, label } in airportOptions" :key="value" :value="value" :label="label" />
  47. </el-select>
  48. </el-form-item>
  49. <el-form-item prop="planeType" style="width: 108px">
  50. <el-select v-model="formData.planeType" size="default" placeholder="属性" clearable>
  51. <el-option value="DOM" label="国内" />
  52. <el-option value="INT" label="国际" />
  53. </el-select>
  54. </el-form-item>
  55. <el-form-item prop="sFlightDate" style="width: 148px">
  56. <el-date-picker v-model="formData.sFlightDate" format="YYYY-MM-DD" value-format="YYYY-MM-DD" size="default" type="date" placeholder="请选择实飞时间" />
  57. </el-form-item>
  58. </div>
  59. <div class="form-right">
  60. <el-form-item v-if="name === 'flight'" prop="company" style="width: 180px">
  61. <el-input v-model.trim="formData.company" size="default" placeholder="请输入航司进行搜索" :prefix-icon="Search" clearable @keyup.enter.prevent="dataQuery" />
  62. </el-form-item>
  63. <el-form-item v-if="name === 'waybill'" prop="flightNO" style="width: 180px">
  64. <el-input v-model.trim="formData.flightNO" size="default" placeholder="请输入航班号" clearable />
  65. </el-form-item>
  66. <el-form-item prop="keyWords" :style="name === 'flight' ? { width: '190px' } : {}">
  67. <el-input v-model.trim="formData.keyWords" size="default" :placeholder="keyWordsPlaceHolder" :prefix-icon="Search" clearable @keyup.enter.prevent="dataQuery" />
  68. </el-form-item>
  69. </div>
  70. </el-form>
  71. <el-button size="default" color="#ac014d" @click="dataQuery">搜索</el-button>
  72. <el-button size="default" plain @click="resetForm">重置</el-button>
  73. <ColumnSet :table-columns="tableColumns" @checked-submit="columnChecked" />
  74. </div>
  75. <div v-loading="loading" element-loading-text="拼命加载中" element-loading-background="rgba(0, 0, 0, 0.8)" class="data-query-table">
  76. <SimpleTable :header-cell-style="() => ({ background: '#F9FAFC' })" ref="tableRef" :data="
  77. tableData.slice((currentPage - 1) * pageSize, currentPage * pageSize)
  78. " :columns="tableColumns" :cell-class-name="cellClass" :column-props="{ formatter }" height="calc(100vh - 220px)" custom-sequence @cell-click="cellClickHandler" />
  79. <el-pagination v-if="tableData.length > 0" background layout="total, prev, pager, next, jumper" :total="tableData.length" :page-size="pageSize" style="position: absolute; right: 19px; bottom: 10px" @size-change="handleSizeChange" @current-change="handleCurrentChange">
  80. </el-pagination>
  81. </div>
  82. </div>
  83. </template>
  84. <script setup lang="tsx">
  85. import { Search } from '@element-plus/icons-vue'
  86. import ColumnSet from '@/components/ColumnSet/index.vue'
  87. import SimpleTable from '@/components/SimpleTable/index.vue'
  88. import { ElMessage, FormInstance } from 'element-plus'
  89. import { parseTime } from '@/utils/validate'
  90. import { useTable } from './useTable'
  91. import { useTableColumnSet } from '@/hooks/useTableColumnSet'
  92. import { CommonTableFormatter } from '~/common'
  93. import { Query } from '@/api/webApi'
  94. const props = defineProps({
  95. name: {
  96. type: String,
  97. required: true,
  98. },
  99. title: {
  100. type: String,
  101. required: true,
  102. },
  103. })
  104. const currentPage = ref(1)
  105. const pageSize = ref(50)
  106. const today = parseTime(new Date(), '{y}-{m}-{d}') as string
  107. const formData = reactive({
  108. flightDate: today,
  109. inOrOut: 'out',
  110. flightType: '',
  111. sAirport: '',
  112. eAirport: '',
  113. planeType: '',
  114. sFlightDate: '',
  115. company: '',
  116. startDate: today,
  117. endDate: today,
  118. keyWords: '',
  119. flightNO:''
  120. })
  121. watchEffect(() => {
  122. if (formData.inOrOut === 'in') {
  123. formData.sAirport = ''
  124. formData.eAirport = 'SZX'
  125. } else {
  126. formData.sAirport = 'SZX'
  127. formData.eAirport = ''
  128. }
  129. if (!formData.startDate || !formData.endDate) {
  130. return
  131. }
  132. const start = new Date(formData.startDate).getTime()
  133. const end = new Date(formData.endDate).getTime()
  134. if (start > end) {
  135. ElMessage.warning('开始时间不能晚于结束时间')
  136. formData.endDate = ''
  137. }
  138. if (start <= end - 2 * 24 * 60 * 60 * 1000) {
  139. ElMessage.warning('间隔不能超过2天')
  140. formData.endDate = ''
  141. }
  142. })
  143. const disabledEndDate = (endDate: Date) => {
  144. const start = new Date(formData.startDate + ' 00:00:00').getTime()
  145. const end = endDate.getTime()
  146. return start > end || start <= end - 2 * 24 * 60 * 60 * 1000
  147. }
  148. const datePreTitle = (title: string) => {
  149. return <div class="date-pre-title">{title}:</div>
  150. }
  151. const searchTitleMap = {
  152. flight: '航班号',
  153. waybill: '运单号',
  154. freight: '货物编码',
  155. }
  156. const keyWordsPlaceHolder = computed(
  157. () => `请输入${searchTitleMap[props.name] ?? '内容'}进行搜索`
  158. )
  159. const airportOptions = ref<{ value: string; label: string }[]>([])
  160. const getAirports = async () => {
  161. try {
  162. const {
  163. code,
  164. returnData: { listValues },
  165. message,
  166. } = await Query<{ [x: string]: string }>({
  167. id: DATACONTENT_ID.airportCode,
  168. dataContent: [],
  169. })
  170. if (Number(code) !== 0) {
  171. throw new Error(message || '失败')
  172. }
  173. airportOptions.value = listValues.map(({ code3 }) => ({
  174. value: code3,
  175. label: code3,
  176. }))
  177. } catch (error) {
  178. console.error(error)
  179. }
  180. }
  181. onMounted(() => {
  182. if (props.name === 'flight') {
  183. getAirports()
  184. dataQuery()
  185. }
  186. })
  187. const keyWordsValidator = (rule: any, value: any, callback: any) => {
  188. if (props.name == 'waybill') {
  189. return true
  190. }
  191. const searchTitle = searchTitleMap[props.name] ?? '关键词'
  192. if (!value) {
  193. if (['flight'].includes(props.name)) {
  194. return callback()
  195. } else {
  196. return callback(new Error(`请输入${searchTitle}`))
  197. }
  198. }
  199. const regsMap: { [x: string]: RegExp[] } = {
  200. // flight: [/^[A-Za-z0-9][A-Za-z][0-9]{3,4}$/, /^[0-9]{3,4}$/],
  201. flight: [/^[0-9]{1,4}$/],
  202. waybill: [/^[0-9]{8}$/, /^[0-9]{11}$/, /^[0-9]{3}\-[0-9]{8}$/],
  203. freight: [/^[0-9]{5}$/, /^[0-9]{3}\-[0-9]{8}\-[0-9]{5}$/],
  204. }
  205. const regs = regsMap[props.name] ?? []
  206. const notMatched = regs.length && regs.every(reg => !reg.test(value))
  207. if (notMatched) {
  208. return callback(new Error(`请输入正确的${searchTitle}`))
  209. }
  210. return callback()
  211. }
  212. const rules = {
  213. startDate: [{ required: true, message: '请选择开始日期', trigger: 'blur' }],
  214. endDate: [{ required: true, message: '请选择结束日期', trigger: 'blur' }],
  215. keyWords: [{ validator: keyWordsValidator, trigger: 'blur' }],
  216. flightDate: [{ required: true, message: '请选择航班日期', trigger: 'blur' }],
  217. company: [
  218. {
  219. pattern: /^[A-Za-z0-9][A-Za-z0-9]$/,
  220. message: '请输入正确的航司',
  221. trigger: 'blur',
  222. },
  223. ],
  224. }
  225. const formRef = ref<FormInstance | null>()
  226. const dataQuery = () => {
  227. formRef.value?.validate(valid => {
  228. if (valid) {
  229. tableInit()
  230. getTableData()
  231. // load();
  232. }
  233. })
  234. }
  235. const resetForm = () => {
  236. formRef.value?.resetFields()
  237. }
  238. const loading = ref(false)
  239. const page = ref(1)
  240. const noMore = ref(false)
  241. const { tableColumns, tableData, getTableData } = useTable(
  242. props.name,
  243. formData,
  244. page,
  245. noMore,
  246. loading
  247. )
  248. // const load = () => {
  249. // if (loading.value || noMore.value) {
  250. // return
  251. // }
  252. // page.value++
  253. // getTableData()
  254. // }
  255. const handleSizeChange = val => {
  256. pageSize.value = val
  257. }
  258. const handleCurrentChange = val => {
  259. currentPage.value = val
  260. }
  261. const tableInit = () => {
  262. page.value = 0
  263. noMore.value = false
  264. tableData.value = []
  265. }
  266. const { columnChecked } = useTableColumnSet(tableColumns)
  267. const flightStateMap = {
  268. CAN: '取消',
  269. DLY: '延误',
  270. }
  271. const flightTypeMap = ['货机', '客机', '其他']
  272. const DITypeMap = {
  273. DOM: '国内',
  274. INT: '国际'
  275. }
  276. const formatter: CommonTableFormatter = (row, column, cellValue, index) => {
  277. const value = String(cellValue ?? '').trim()
  278. if (column.property.includes('Time')) {
  279. return value.replace(/[T|\s]+/, '\n')
  280. }
  281. if (column.property === 'DIType' && value) {
  282. return DITypeMap[value] ?? value
  283. }
  284. if (column.property === 'flightState') {
  285. return value ? flightStateMap[value] ?? '正常' : '正常'
  286. }
  287. if (column.property === 'flightType') {
  288. return cellValue ? flightTypeMap[cellValue] ?? '其他' : '其他'
  289. }
  290. return value
  291. }
  292. const cellClass = ({ row, column, rowIndex, columnIndex }) => {
  293. const classes: string[] = []
  294. switch (props.name) {
  295. case 'flight':
  296. if (['flightNO'].includes(column.property) && row[column.property]) {
  297. classes.push('cell-click')
  298. }
  299. break
  300. case 'waybill':
  301. if (['stockCode'].includes(column.property) && row[column.property]) {
  302. classes.push('cell-click')
  303. }
  304. break
  305. case 'freight':
  306. break
  307. default:
  308. break
  309. }
  310. return classes.join(' ')
  311. }
  312. const router = useRouter()
  313. const cellClickHandler = (row, column, cell, event) => {
  314. switch (props.name) {
  315. case 'flight': {
  316. switch (column.property) {
  317. case 'flightNO': {
  318. if (!row.flightAllNO || !row.flightDate) {
  319. ElMessage.error('航班信息缺失!')
  320. return
  321. }
  322. if (!['INT', 'DOM'].includes(row.DIType)) {
  323. ElMessage.error('国内/国际无法识别!')
  324. return
  325. }
  326. const viewName = `${row.DIType === 'DOM' ? '' : 'International'}${
  327. row.departureAirport === 'SZX' ||
  328. (!row.departureAirport && row.arriveAirport !== 'SZX')
  329. ? 'Departure'
  330. : 'Arrival'
  331. }Flight`
  332. router.push({
  333. path: `/dataQuery/flightQuery/${viewName}`,
  334. query: {
  335. flightNO: row.flightAllNO,
  336. flightDate: row.flightDate,
  337. },
  338. })
  339. break
  340. }
  341. default:
  342. break
  343. }
  344. break
  345. }
  346. case 'waybill': {
  347. switch (column.property) {
  348. case 'stockCode': {
  349. if (
  350. !row.stockCode ||
  351. !row.flightDate ||
  352. !['INT', 'DOM'].includes(row.DIType)
  353. ) {
  354. ElMessage.error('运单信息缺失!')
  355. return
  356. }
  357. const viewName = `${row.DIType === 'DOM' ? '' : 'International'}${
  358. row.departureAirport === 'SZX' ||
  359. (!row.departureAirport && row.arriveAirport !== 'SZX')
  360. ? 'Departure'
  361. : 'Arrival'
  362. }Waybill`
  363. router.push({
  364. path: `/dataQuery/waybillQuery/${viewName}`,
  365. query: {
  366. waybillNO: row.stockCode,
  367. flightDate: row.flightDate,
  368. },
  369. })
  370. break
  371. }
  372. }
  373. break
  374. }
  375. case 'freight':
  376. break
  377. default:
  378. break
  379. }
  380. }
  381. </script>
  382. <style lang="scss" scoped>
  383. :deep(.el-pagination.is-background .el-pager li:not(.is-disabled).is-active) {
  384. background-color: #ac014d !important; //修改默认的背景色
  385. }
  386. .data-query {
  387. width: 100%;
  388. height: 100%;
  389. display: flex;
  390. flex-direction: column;
  391. &-header {
  392. width: 100%;
  393. height: 32px;
  394. margin: 12px 0;
  395. display: flex;
  396. }
  397. &-form :deep {
  398. margin-right: 12px;
  399. flex: 1;
  400. display: flex;
  401. justify-content: flex-end;
  402. .form-left {
  403. flex: 1;
  404. display: flex;
  405. .el-form-item {
  406. width: 168px;
  407. margin-right: 8px;
  408. .el-date-editor.pre-text {
  409. .el-input__prefix {
  410. flex-basis: 42px;
  411. padding-left: 15px;
  412. .date-pre-title {
  413. font-style: normal;
  414. font-size: 14px;
  415. font-family: Microsoft YaHei;
  416. color: #303133;
  417. }
  418. }
  419. }
  420. }
  421. }
  422. .form-right {
  423. display: flex;
  424. justify-content: flex-end;
  425. .el-form-item {
  426. width: 280px;
  427. &:not(:last-of-type) {
  428. margin-right: 10px;
  429. }
  430. .el-select,
  431. .el-date-editor {
  432. width: 100%;
  433. }
  434. }
  435. }
  436. .el-form-item {
  437. margin: 0;
  438. .el-input__inner {
  439. font-size: 14px;
  440. font-family: DIN, Microsoft YaHei;
  441. color: #303133;
  442. }
  443. }
  444. }
  445. &-table {
  446. height: 0;
  447. flex: 1;
  448. }
  449. }
  450. </style>