Act.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. <template>
  2. <div class="safety-platform-container">
  3. <header class="safety-platform-container__header">
  4. <div class="breadcrumb-title"> 违规行为记录 </div>
  5. </header>
  6. <main class="safety-platform-container__main">
  7. <div class="search-table-container">
  8. <header>
  9. <div>
  10. <el-button type="primary" class="search-table-container--button" :icon="Plus" @click="handleCreateAct">
  11. 新建记录
  12. </el-button>
  13. <el-button plain class="search-table-container--button" @click="batchImportVisible = true">
  14. 批量导入
  15. </el-button>
  16. <RealtimeNotice />
  17. </div>
  18. <div class="act-search">
  19. <section class="select-box">
  20. <SelectableInput ref="selectableInputRef" :options="ACT_TABLE_SEARCH_OPTIONS" />
  21. <div class="select-box--item">
  22. <span>违规类型:</span>
  23. <el-select
  24. v-model="searchData.violationType"
  25. placeholder="请选择违规类型"
  26. class="select-box--select"
  27. clearable
  28. >
  29. <el-option
  30. v-for="item in ACT_VIOLATION_TYPE_OPTIONS"
  31. :key="item.value"
  32. :value="item.value"
  33. :label="item.label"
  34. :disabled="item.disabled"
  35. />
  36. </el-select>
  37. </div>
  38. <div class="select-box--item">
  39. <span>通知状态:</span>
  40. <el-select
  41. v-model="searchData.isNotice"
  42. placeholder="请选择通知状态"
  43. class="select-box--select"
  44. clearable
  45. >
  46. <el-option
  47. v-for="item in ACT_NOTICE_STATE_OPTIONS"
  48. :key="item.value"
  49. :value="item.value"
  50. :label="item.label"
  51. :disabled="item.disabled"
  52. />
  53. </el-select>
  54. </div>
  55. <div>
  56. <span>时间:</span>
  57. <el-date-picker
  58. v-model="searchData.searchTime"
  59. type="datetimerange"
  60. range-separator="至"
  61. start-placeholder="开始时间"
  62. end-placeholder="结束时间"
  63. />
  64. </div>
  65. </section>
  66. <section class="search-btn">
  67. <el-button type="primary" @click="handleSearch">查询</el-button>
  68. <el-button @click="handleReset">重置</el-button>
  69. <el-button @click="handleDownload">导出</el-button>
  70. </section>
  71. </div>
  72. </header>
  73. <!-- 表格 -->
  74. <BasicTable
  75. :tableData="tableData"
  76. :tableConfig="tableConfig"
  77. @update:pageSize="handleSizeChange"
  78. @update:pageNumber="handleCurrentChange"
  79. @update:selection="handleSelectionChange"
  80. >
  81. <template #violateType="scope">
  82. <span>{{ ACT_VIOLATION_TYPE_LABEL[scope.row.violateType] }}</span>
  83. </template>
  84. <template #capturePhotos="scope">
  85. <ImageViewer :file-list="scope.row.capturePhotos" />
  86. </template>
  87. <template #createSource="scope">
  88. <span>{{ ACT_NOTICE_DATA_SOURCE_LABEL[scope.row.createSource] }}</span>
  89. </template>
  90. <template #isNotice="scope">
  91. <div class="notice-state">
  92. <div
  93. :style="{
  94. backgroundColor: ACT_NOTICE_STATE_COLOR[scope.row.isNotice],
  95. width: '6px',
  96. height: '6px',
  97. borderRadius: '50%',
  98. marginRight: '5px',
  99. }"
  100. ></div>
  101. <span>{{ ACT_NOTICE_STATE_LABEL[scope.row.isNotice] }}</span>
  102. </div>
  103. </template>
  104. <template #action="scope">
  105. <ActionButton
  106. v-if="scope.row.isNotice === ACT_NOTICE_STATE.INACTIVE"
  107. text="编辑"
  108. @click="handleEditAct(scope.row.id)"
  109. />
  110. <ActionButton
  111. v-if="scope.row.isNotice === ACT_NOTICE_STATE.INACTIVE"
  112. text="通知"
  113. @click="handleNoticeAct(scope.row.id)"
  114. />
  115. <ActionButton
  116. text="删除"
  117. :popconfirm="{
  118. title: '确定要删除?',
  119. }"
  120. @confirm="handleDeleteAct(scope.row.id)"
  121. />
  122. </template>
  123. </BasicTable>
  124. </div>
  125. </main>
  126. <BatchImport
  127. :visible="batchImportVisible"
  128. :importApiUrl="importApiUrl"
  129. :templateUrl="templateUrl"
  130. :templateName="'违规行为记录-批量导入模版'"
  131. @close="() => (batchImportVisible = false)"
  132. @update="handleUpdate"
  133. />
  134. </div>
  135. </template>
  136. <script setup lang="ts">
  137. import BasicTable from '@/components/BasicTable.vue';
  138. import useTableConfig from '@/hooks/useTableConfigHook';
  139. import SelectableInput from '@/components/formItems/selectableInput/SelectableInput.vue';
  140. import ActionButton from '@/components/ActionButton.vue';
  141. import RealtimeNotice from './components/RealtimeNotice.vue';
  142. import dayjs from 'dayjs';
  143. import { ElMessage } from 'element-plus';
  144. import { TABLE_OPTIONS, VIOLATION_ACT_TABLE_COLUMNS } from './configs/tables';
  145. import {
  146. ACT_NOTICE_DATA_SOURCE_LABEL,
  147. ACT_VIOLATION_TYPE,
  148. ACT_VIOLATION_TYPE_LABEL,
  149. ACT_TABLE_SEARCH_OPTIONS,
  150. ACT_VIOLATION_TYPE_OPTIONS,
  151. ACT_NOTICE_STATE_OPTIONS,
  152. ACT_NOTICE_STATE,
  153. ACT_NOTICE_STATE_LABEL,
  154. ACT_NOTICE_STATE_COLOR,
  155. } from './constants';
  156. import { ref, reactive, onMounted } from 'vue';
  157. import { Search, Plus } from '@element-plus/icons-vue';
  158. import { useRouter } from 'vue-router';
  159. import { openMessageBox } from '@/utils/element-plus/messageBox';
  160. import type { QueryPageRequest } from '@/types/basic-query';
  161. import type { ActTableSearch, ActTableQuery, ActTableData, UpdateActQuery } from './types';
  162. import {
  163. getActTableList,
  164. noticeActData,
  165. deleteActData,
  166. exportActViolation,
  167. } from '@/api/traffic-violation/traffic-act';
  168. import { downloadFile } from '@/views/disaster/utils/download';
  169. import ImageViewer from './components/ImageViewer.vue';
  170. import { BatchImport } from '@/components/batch-import';
  171. import { useGlobSetting } from '@/hooks/setting';
  172. import urlJoin from 'url-join';
  173. const router = useRouter();
  174. // 搜索栏
  175. const selectableInputRef = ref<InstanceType<typeof SelectableInput>>();
  176. const searchData = reactive<ActTableSearch>({});
  177. function getQuery() {
  178. if (!selectableInputRef.value) return;
  179. tabelQuery.queryParam = {
  180. pageType: 1,
  181. };
  182. const selectableSearch = selectableInputRef.value.getValue();
  183. if (selectableSearch) {
  184. tabelQuery.queryParam[selectableSearch.key as string] = selectableSearch.value;
  185. }
  186. if (searchData.isNotice) {
  187. tabelQuery.queryParam.isNotice = searchData.isNotice;
  188. }
  189. if (searchData.violationType) {
  190. tabelQuery.queryParam.violationType = searchData.violationType;
  191. }
  192. if (searchData.searchTime) {
  193. tabelQuery.queryParam.startTime = dayjs(searchData.searchTime[0]).format('YYYY-MM-DD HH:MM:ss');
  194. tabelQuery.queryParam.endTime = dayjs(searchData.searchTime[1]).format('YYYY-MM-DD HH:MM:ss');
  195. }
  196. }
  197. function handleSearch() {
  198. getQuery();
  199. getTableData();
  200. }
  201. function handleReset() {
  202. selectableInputRef.value?.clearValue();
  203. searchData.carNumber = undefined;
  204. searchData.violateName = undefined;
  205. searchData.deptName = undefined;
  206. searchData.violationType = undefined;
  207. searchData.searchTime = undefined;
  208. }
  209. async function handleDownload() {
  210. getQuery();
  211. try {
  212. const res = await exportActViolation(tabelQuery.queryParam);
  213. if (res.size === 0) return;
  214. const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
  215. const url = window.URL.createObjectURL(blob);
  216. downloadFile(url, '违规行为记录.xlsx');
  217. } catch (e) {
  218. ElMessage.error('下载失败');
  219. console.log(e);
  220. }
  221. }
  222. // 表格
  223. const basicTableRef = ref<InstanceType<typeof BasicTable>>();
  224. const { tableConfig, pagination } = useTableConfig(VIOLATION_ACT_TABLE_COLUMNS, TABLE_OPTIONS);
  225. const tableData = ref<ActTableData[]>([]);
  226. const tabelQuery = reactive<QueryPageRequest<ActTableQuery>>({
  227. pageNumber: pagination.pageNumber,
  228. pageSize: pagination.pageSize,
  229. queryParam: {
  230. pageType: 1,
  231. },
  232. });
  233. const handleSizeChange = (value: number) => {
  234. pagination.pageNumber = value;
  235. tabelQuery.pageSize = value;
  236. getTableData();
  237. };
  238. const handleCurrentChange = (value: number) => {
  239. pagination.pageNumber = value;
  240. tabelQuery.pageSize = value;
  241. getTableData();
  242. };
  243. const handleSelectionChange = (value: any[]) => {};
  244. // const handleCloseBatchOperation = () => {
  245. // if (!basicTableRef.value) return;
  246. // basicTableRef.value.clearSelection();
  247. // };
  248. // const handleBatchNotice = async () => {
  249. // const confirmed = await openMessageBox('', '确认通知任务吗?', 'warning');
  250. // if (!confirmed) return;
  251. // const noticeIds = getSelectionIds(ACTIVE_STATUS.NOT_EFFECTIVE);
  252. // await noticeActData(noticeIds);
  253. // ElMessage.success('批量通知成功');
  254. // getTableData();
  255. // };
  256. // const handleBatchDelete = async () => {
  257. // const confirmed = await openMessageBox('', '删除后任务不可恢复,确认删除吗?', 'warning');
  258. // if (!confirmed) return;
  259. // const deleteIds = getSelectionIds(ACTIVE_STATUS.NOT_EFFECTIVE);
  260. // await deleteActData(deleteIds);
  261. // ElMessage.success('批量删除成功');
  262. // getTableData();
  263. // };
  264. async function getTableData() {
  265. tableConfig.loading = true;
  266. const res = await getActTableList(tabelQuery);
  267. tableData.value = res.records;
  268. pagination.total = res.totalRow;
  269. tableConfig.loading = false;
  270. }
  271. // 批量导入
  272. const batchImportVisible = ref(false);
  273. const { urlPrefix } = useGlobSetting();
  274. const importApiUrl = ref(urlJoin(urlPrefix, '/trafficViolation/importTrafficViolationList'));
  275. const templateUrl = ref('./skyeye-file-upload/sfysecurity/TEMPLATE/import-vehicle-template.xlsx');
  276. const handleUpdate = () => {
  277. batchImportVisible.value = false;
  278. getTableData();
  279. };
  280. onMounted(async () => {
  281. await getTableData();
  282. });
  283. function handleCreateAct() {
  284. router.push({
  285. name: 'traffic-violation-act-item',
  286. query: {
  287. operate: 'act-create',
  288. },
  289. });
  290. }
  291. function handleEditAct(id: number) {
  292. router.push({
  293. name: 'traffic-violation-act-item',
  294. query: {
  295. id,
  296. operate: 'act-edit',
  297. },
  298. });
  299. }
  300. async function handleNoticeAct(id: number) {
  301. tableConfig.loading = true;
  302. try {
  303. await noticeActData(id);
  304. } catch (e) {
  305. ElMessage.error('通知失败');
  306. return;
  307. } finally {
  308. tableConfig.loading = false;
  309. }
  310. getTableData();
  311. }
  312. async function handleDeleteAct(id: number) {
  313. tableConfig.loading = true;
  314. try {
  315. await deleteActData(id);
  316. } catch (e) {
  317. ElMessage.error('删除失败');
  318. return;
  319. } finally {
  320. tableConfig.loading = false;
  321. }
  322. getTableData();
  323. }
  324. </script>
  325. <style scoped lang="scss">
  326. @use '@/styles/page-details-layout.scss' as *;
  327. @use '@/styles/page-main-layout.scss' as *;
  328. @use '@/styles/basic-table-action.scss' as *;
  329. .act-search-input {
  330. max-width: 500px;
  331. }
  332. .act-search {
  333. display: flex;
  334. align-items: center;
  335. justify-content: space-between;
  336. width: 100%;
  337. }
  338. .select-box {
  339. display: flex;
  340. align-items: center;
  341. flex-wrap: wrap;
  342. gap: 32px;
  343. &--item {
  344. @include flex-center;
  345. white-space: nowrap;
  346. }
  347. span {
  348. color: rgba(0, 0, 0, 0.85);
  349. font-size: 14px;
  350. }
  351. .el-select {
  352. width: 200px;
  353. }
  354. }
  355. .search-btn {
  356. display: flex;
  357. }
  358. .notice-state {
  359. display: flex;
  360. align-items: center;
  361. justify-self: center;
  362. }
  363. </style>