index.vue 18 KB

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