Accident.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. <template>
  2. <div class="safety-platform-container">
  3. <div class="safety-platform-container__header">
  4. <div class="breadcrumb-title">交通事故管理</div>
  5. </div>
  6. <div class="safety-platform-container__main">
  7. <div class="search-table-container">
  8. <header class="disaster-precaution__header">
  9. <el-button
  10. v-if="accidentManagePermission"
  11. class="search-table-container--button"
  12. type="primary"
  13. :icon="Plus"
  14. @click="handleAddVehicleInfo"
  15. >
  16. 添加交通事故记录
  17. </el-button>
  18. <el-button
  19. v-if="accidentManagePermission"
  20. class="search-table-container--button"
  21. @click="batchImportVisible = true"
  22. >
  23. 批量导入
  24. </el-button>
  25. <div class="search-container">
  26. <el-input
  27. v-model="searchKeyword"
  28. :placeholder="`请输入${curSearchTypeLabel}进行搜索`"
  29. clearable
  30. @input="handleSearch"
  31. @clear="handleClear"
  32. @keyup.enter="handleSearch"
  33. style="width: 380px"
  34. >
  35. <template #prefix>
  36. <el-icon color="#1777ff"><Search /></el-icon>
  37. </template>
  38. <template #prepend>
  39. <el-select
  40. v-model="searchSelectedType"
  41. placeholder="选择搜索项"
  42. @change="handleSelectedTypeChange"
  43. style="width: 100px"
  44. >
  45. <el-option
  46. v-for="item in accidentQueryOptions"
  47. :key="item.value"
  48. :label="item.label"
  49. :value="item.value"
  50. />
  51. </el-select>
  52. </template>
  53. </el-input>
  54. <div class="search-time">
  55. <span>事故时间:</span>
  56. <el-date-picker
  57. v-model="queryTimes"
  58. type="datetimerange"
  59. range-separator="-"
  60. start-placeholder="开始时间"
  61. end-placeholder="结束时间"
  62. value-format="YYYY-MM-DD HH:mm:ss"
  63. @change="handleSearch"
  64. />
  65. </div>
  66. <div class="search-container-btn">
  67. <el-button type="primary" @click="handleSearch">查询</el-button>
  68. <el-button @click="handleReset">重置</el-button>
  69. <el-button @click="handleExport">导出</el-button>
  70. </div>
  71. </div>
  72. </header>
  73. <div class="batch-table">
  74. <div class="batch-operation--div" v-show="accidentManagePermission && selectionItems.length > 0">
  75. <span>已选{{ selectionItems.length }}项</span>
  76. <div class="batch-operation--div--close">
  77. <div class="batch-operation--div--button">
  78. <el-button class="custom-el-button" @click="handleBatchDelete">批量删除</el-button>
  79. </div>
  80. <el-icon class="close-icon" @click="handleCloseBatchOperation"><Close /></el-icon>
  81. </div>
  82. </div>
  83. <BasicTable
  84. ref="basicTableRef"
  85. :tableData="tableData"
  86. :tableConfig="tableConfig"
  87. @update:page-number="handleCurrentPageChange"
  88. @update:page-size="handlePageSizeChange"
  89. @update:selection="handleSelectionChange"
  90. >
  91. <template #action="scope">
  92. <div class="action-container--div">
  93. <ActionButton text="查看" @click="handleViewDetail(scope.row.id)" />
  94. <ActionButton
  95. text="编辑"
  96. v-if="accidentManagePermission"
  97. @click="handleEditVehicleInfo(scope.row.id)"
  98. />
  99. <ActionButton
  100. v-if="accidentManagePermission"
  101. text="删除"
  102. :popconfirm="{
  103. title: '是否删除该交通事故记录?',
  104. }"
  105. @confirm="handleDeleteVehicleInfo(scope.row)"
  106. />
  107. </div>
  108. </template>
  109. </BasicTable>
  110. </div>
  111. </div>
  112. </div>
  113. </div>
  114. <BatchImport
  115. :visible="batchImportVisible"
  116. :importApiUrl="importApiUrl"
  117. :templateUrl="templateUrl"
  118. :templateName="'交通事故记录-批量导入模版'"
  119. @close="() => (batchImportVisible = false)"
  120. @update="handleUpdate"
  121. />
  122. </template>
  123. <script setup lang="ts">
  124. import { computed, onMounted, ref } from 'vue';
  125. import urlJoin from 'url-join';
  126. import { useRouter } from 'vue-router';
  127. import { ElMessage } from 'element-plus';
  128. import { Plus, Search, Close } from '@element-plus/icons-vue';
  129. import { openMessageBox } from '@/utils/element-plus/messageBox';
  130. import BasicTable from '@/components/BasicTable.vue';
  131. import ActionButton from '@/components/ActionButton.vue';
  132. import { BatchImport } from '@/components/batch-import';
  133. import { downloadByData } from '@/utils/file/download';
  134. import { msgConfirm } from '@/utils/element-plus/messageBox';
  135. import { getCurrentDateTimeString } from '@/utils/dateUtil';
  136. import type { QueryPageRequest } from '@/types/basic-query';
  137. import { useGlobSetting } from '@/hooks/setting';
  138. import useTableConfig from '@/hooks/useTableConfigHook';
  139. import { useUserInfoHook } from '@/hooks/useUserInfoHook';
  140. import { TRAFFIC_PERMISSIONS } from '@/views/traffic/constant';
  141. import {
  142. ACCIDENT_LIST_TABLE_MAX_HEIGHT_DEFAULT,
  143. ACCIDENT_LIST_TABLE_MAX_HEIGHT_PERMISSION,
  144. ACCIDENT_LIST_TABLE_OPTIONS,
  145. ACCIDENT_LIST_TABLE_COLUMNS,
  146. } from './config';
  147. import { FIELDTYPE, FIELD_CONTENT, accidentQueryOptions } from './constant';
  148. import {
  149. AccidentListQuery,
  150. AccidentInfoStruct,
  151. getAccidentInfoList,
  152. deleteAccidentInfo,
  153. exportAccidentInfo,
  154. } from '@/api/traffic-accident';
  155. const { tableConfig, pagination } = useTableConfig(ACCIDENT_LIST_TABLE_COLUMNS, ACCIDENT_LIST_TABLE_OPTIONS);
  156. const { permissions } = useUserInfoHook();
  157. const accidentManagePermission = ref<boolean>(false);
  158. const router = useRouter();
  159. const searchSelectedType = ref(FIELDTYPE.LOCATION);
  160. const searchKeyword = ref('');
  161. const curSearchTypeLabel = computed(() => {
  162. const option = accidentQueryOptions.find((item) => item.value === searchSelectedType.value);
  163. return option ? option.label : FIELD_CONTENT[searchSelectedType.value];
  164. });
  165. const queryTimes = ref(['', '']);
  166. const accidentTableQuery: QueryPageRequest<AccidentListQuery> = {
  167. pageNumber: pagination.pageNumber,
  168. pageSize: pagination.pageSize,
  169. queryParam: {},
  170. };
  171. const basicTableRef = ref<InstanceType<typeof BasicTable>>();
  172. const tableData = ref<AccidentInfoStruct[]>([]);
  173. const selectionItems = ref<any[]>([]);
  174. const handleSelectedTypeChange = () => {
  175. searchKeyword.value = '';
  176. };
  177. const handleSearch = () => {
  178. if (!queryTimes.value) queryTimes.value = ['', ''];
  179. getTableData();
  180. };
  181. const handleClear = () => {
  182. searchKeyword.value = '';
  183. getTableData();
  184. };
  185. const handleReset = () => {
  186. searchSelectedType.value = FIELDTYPE.LOCATION;
  187. searchKeyword.value = '';
  188. queryTimes.value = ['', ''];
  189. getTableData();
  190. };
  191. // 导出
  192. const handleExport = () => {
  193. msgConfirm('确定导出所查询数据?', '导出', {
  194. confirmButtonText: '确定',
  195. showCancelButton: true,
  196. type: 'warning',
  197. })
  198. .then(() => {
  199. exportAccidentInfo(accidentTableQuery.queryParam).then(async (responnse) => {
  200. if (!responnse) {
  201. throw new Error('下载文件失败');
  202. }
  203. downloadByData(responnse, `交通事故记录_${getCurrentDateTimeString()}.xlsx`);
  204. ElMessage.success('下载文件成功');
  205. });
  206. })
  207. .catch(() => {
  208. ElMessage({
  209. type: 'info',
  210. message: '取消导出',
  211. });
  212. });
  213. };
  214. const handleCurrentPageChange = (pageNumber: number) => {
  215. pagination.pageNumber = pageNumber;
  216. accidentTableQuery.pageNumber = pageNumber;
  217. getTableData();
  218. };
  219. const handlePageSizeChange = (pageSize: number) => {
  220. pagination.pageSize = pageSize;
  221. accidentTableQuery.pageSize = pageSize;
  222. getTableData();
  223. };
  224. const handleAddVehicleInfo = () => {
  225. router.push({
  226. name: 'traffic-accident-item',
  227. query: { operate: 'create' },
  228. });
  229. };
  230. const handleEditVehicleInfo = (id: number) => {
  231. router.push({
  232. name: 'traffic-accident-item',
  233. query: { operate: 'edit', id },
  234. });
  235. };
  236. const handleViewDetail = (id: number) => {
  237. router.push({
  238. name: 'traffic-accident-item',
  239. query: { id },
  240. });
  241. };
  242. const handleDeleteVehicleInfo = (row: AccidentInfoStruct) => {
  243. if (!row.id) return;
  244. deleteAccidentInfo({ accidentRecordIds: [row.id] }).then(() => {
  245. ElMessage.success('删除成功');
  246. getTableData();
  247. });
  248. };
  249. // 批量删除
  250. const handleSelectionChange = (selection: any[]) => {
  251. selectionItems.value = selection;
  252. };
  253. const handleCloseBatchOperation = () => {
  254. if (!basicTableRef.value) return;
  255. basicTableRef.value.clearSelection();
  256. };
  257. const handleBatchDelete = async () => {
  258. const confirmed = await openMessageBox('', '删除后信息不可恢复,确认删除吗?', 'warning');
  259. if (!confirmed) return;
  260. const deleteIds = selectionItems.value.map((item) => item.id);
  261. if (!deleteIds.length) return;
  262. deleteAccidentInfo({ accidentRecordIds: deleteIds }).then(() => {
  263. ElMessage.success('批量删除成功');
  264. getTableData();
  265. });
  266. };
  267. // 批量导入
  268. const batchImportVisible = ref(false);
  269. const { urlPrefix } = useGlobSetting();
  270. const importApiUrl = ref(urlJoin(urlPrefix, '/trafficAccident/importTrafficAccidentRecord'));
  271. const templateUrl = ref('./skyeye-file-upload/sfysecurity/TEMPLATE/import-traffic-template.xlsx');
  272. const handleUpdate = () => {
  273. batchImportVisible.value = false;
  274. getTableData();
  275. };
  276. const getTableData = () => {
  277. tableConfig.loading = true;
  278. accidentTableQuery.queryParam = {
  279. fieldType: searchSelectedType.value,
  280. fieldContent: searchKeyword.value,
  281. startTime: queryTimes.value[0] || '',
  282. endTime: queryTimes.value[1] || '',
  283. };
  284. getAccidentInfoList(accidentTableQuery).then((res) => {
  285. tableData.value = res?.records || [];
  286. pagination.total = res?.totalRow || 0;
  287. });
  288. tableConfig.loading = false;
  289. };
  290. onMounted(() => {
  291. getTableData();
  292. accidentManagePermission.value = Boolean(
  293. permissions.find((item: { code: string }) => item.code === TRAFFIC_PERMISSIONS.ACCIDENT_MANAGE),
  294. );
  295. tableConfig.maxHeight = accidentManagePermission.value
  296. ? ACCIDENT_LIST_TABLE_MAX_HEIGHT_PERMISSION
  297. : ACCIDENT_LIST_TABLE_MAX_HEIGHT_DEFAULT;
  298. });
  299. </script>
  300. <style scoped lang="scss">
  301. @use '@/styles/page-main-layout.scss' as *;
  302. @use '@/styles/page-details-layout.scss' as *;
  303. @use '@/styles/basic-table-action.scss' as *;
  304. .search-container {
  305. display: flex;
  306. .search-time {
  307. color: rgba(0, 0, 0, 0.85);
  308. font-size: 14px;
  309. margin-left: 20px;
  310. display: flex;
  311. align-items: center;
  312. }
  313. .search-container-btn {
  314. margin-left: auto;
  315. }
  316. }
  317. .batch-table {
  318. position: relative;
  319. width: 100%;
  320. height: 100%;
  321. }
  322. .batch-operation--div {
  323. @include flex-center;
  324. justify-content: flex-start;
  325. position: absolute;
  326. top: 0;
  327. left: 0;
  328. gap: 60px;
  329. width: 100%;
  330. height: 48px;
  331. border: 4px;
  332. padding: 16px 25px;
  333. background-color: #ddefff;
  334. z-index: 100;
  335. &--close {
  336. @include flex-center;
  337. justify-content: space-between;
  338. flex: 1;
  339. }
  340. .close-icon {
  341. font-size: 20px;
  342. color: #ff4d4f;
  343. cursor: pointer;
  344. }
  345. }
  346. </style>