safetyOrganizationSystemManagement.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  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 flex">
  7. <div class="nav">
  8. <el-button type="primary" :icon="Plus" @click="addTeam('parent')"> 添加组织 </el-button>
  9. <div class="collapse-wrapper">
  10. <!-- expand-icon-position="left" 版本小了,不支持 -->
  11. <el-collapse accordion v-model="activeName" v-if="fetchSafetyOrganizationList.length > 0">
  12. <el-collapse-item :name="item.orgId" v-for="item in fetchSafetyOrganizationList">
  13. <template #title="{ isActive }">
  14. <div :class="['title-wrapper', { 'is-active': isActive }]">
  15. <span @click.stop="querySafetyTeamData('parent', item)">
  16. {{ item.orgName }}
  17. </span>
  18. <div class="handler">
  19. <el-button :icon="Plus" size="small" type="default" dashed @click.stop="handleCreateSafetySystem('children', item)"
  20. ></el-button
  21. >
  22. <el-button size="small" type="primary" link @click.stop="handleEditSafetySystem('parent', item, '')"
  23. >编辑</el-button
  24. >
  25. <el-button size="small" type="danger" link @click.stop="handleDelSafetySystem('parent', item)"
  26. >删除</el-button
  27. >
  28. </div>
  29. </div>
  30. </template>
  31. <div class="collapse-item-content">
  32. <div v-if="item.children.length > 0">
  33. <ul>
  34. <li class="flex" v-for="children in item.children" :key="children.orgId">
  35. <span @click="querySafetyTeamData('children', children)">
  36. {{ children.orgName }}
  37. </span>
  38. <div class="handler">
  39. <el-button
  40. link
  41. size="small"
  42. type="primary"
  43. @click.stop="handleEditSafetySystem('children', children, item.orgId)"
  44. >编辑</el-button
  45. >
  46. <el-button
  47. link
  48. size="small"
  49. type="danger"
  50. @click.stop="handleDelSafetySystem('children', children)"
  51. >删除</el-button
  52. >
  53. </div>
  54. </li>
  55. </ul>
  56. </div>
  57. <div v-else>
  58. <el-empty description="未添加子组织" :image-size="40" />
  59. </div>
  60. </div>
  61. </el-collapse-item>
  62. </el-collapse>
  63. <div v-else>
  64. <el-empty description="未添加组织" />
  65. </div>
  66. </div>
  67. <AddSafetySystem
  68. v-model:visible="addSafetySystemVisible"
  69. :data="addSafetyOrganizationSystemFormData"
  70. @confirmAddSafetySystem="confirmAddSafetySystemCallback"
  71. />
  72. </div>
  73. <div class="search-table-container table-content">
  74. <div style="margin-bottom:20px">
  75. <el-button type="primary" :icon="Plus" @click="handleCreate"> 添加 </el-button>
  76. <el-button plain @click="handleImport">导入</el-button>
  77. </div>
  78. <header>
  79. <div class="act-search">
  80. <section class="select-box">
  81. <div class="select-box--item">
  82. <span>搜索工号/姓名:</span>
  83. <el-input
  84. v-model="tableQuery.queryParam.keyword"
  85. placeholder="搜索工号/姓名"
  86. class="act-search-input"
  87. />
  88. </div>
  89. <div class="select-box--item">
  90. <span>状态:</span>
  91. <el-select v-model="tableQuery.queryParam.status" placeholder="请选择状态" clearable>
  92. <el-option label="启用" :value="1" />
  93. <el-option label="禁用" :value="2" />
  94. </el-select>
  95. </div>
  96. <div class="select-box--item">
  97. <span>日期范围:</span>
  98. <el-date-picker
  99. v-model="dateRange"
  100. @change="onchangeDateRange"
  101. type="daterange"
  102. range-separator="至"
  103. start-placeholder="开始日期"
  104. end-placeholder="结束日期"
  105. value-format="YYYY-MM-DD"
  106. format="YYYY-MM-DD"
  107. />
  108. </div>
  109. </section>
  110. <section class="search-btn">
  111. <el-button type="primary" @click="handleSearch">查询</el-button>
  112. <el-button @click="handleReset">重置</el-button>
  113. <el-button plain @click="handleDownload">导出</el-button>
  114. </section>
  115. </div>
  116. </header>
  117. <div class="batch-table">
  118. <BasicTable
  119. ref="basicTableRef"
  120. :tableData="tableData"
  121. :tableConfig="tableConfig"
  122. @update:pageSize="handleSizeChange"
  123. @update:pageNumber="handleCurrentChange"
  124. >
  125. <template #status="scope">
  126. <span>
  127. {{ scope.row.status === 1 ? '启用' : scope.row.status === 2 ? '禁用' : '-' }}
  128. </span>
  129. </template>
  130. <template #action="scope">
  131. <div class="action-container--div" style="justify-content: left">
  132. <ActionButton text="编辑" @click="handleEdit(scope.row.id)" />
  133. <ActionButton
  134. text="删除"
  135. :popconfirm="{
  136. title: '确定要删除?',
  137. }"
  138. @confirm="handleDelete(scope.row.id)"
  139. />
  140. <ActionButton text="查看" @click="handleView(scope.row.id)" />
  141. </div>
  142. </template>
  143. </BasicTable>
  144. </div>
  145. </div>
  146. </main>
  147. <BatchImport
  148. v-if="batchImportVisible"
  149. :visible="batchImportVisible"
  150. :import-api-url="importApiUrl"
  151. :template-url="templateUrl"
  152. template-name="安全组织体系管理导入模版"
  153. :show-template="true"
  154. @close="batchImportVisible = false"
  155. @update="handleUpdate"
  156. />
  157. </div>
  158. </template>
  159. <script setup lang="ts">
  160. import { onMounted, reactive, ref } from 'vue';
  161. import { ElMessage, ElMessageBox } from 'element-plus';
  162. import BasicTable from '@/components/BasicTable.vue';
  163. import useTableConfig from '@/hooks/useTableConfigHook';
  164. import ActionButton from '@/components/ActionButton.vue';
  165. import { TABLE_OPTIONS, TABLE_COLUMNS } from './configs/tables';
  166. import { useRouter } from 'vue-router';
  167. import type { QueryPageRequest } from '@/types/basic-query';
  168. import {
  169. getSafetySystemList,
  170. addSafetySystem,
  171. updateSafetySystem,
  172. deleteSafetySystem,
  173. fetchTableList,
  174. delEmployee,
  175. exportSafetyOrganizationSystemManagement
  176. } from '@/api/safety-organization-management';
  177. import { downloadByData } from '@/utils/file/download';
  178. import BatchImport from '@/components/batch-import/BatchImport.vue';
  179. import { useGlobSetting } from '@/hooks/setting';
  180. import urlJoin from 'url-join';
  181. import AddSafetySystem from './components/addSafetySystem.vue';
  182. import {
  183. Delete,
  184. Edit,
  185. Plus,
  186. } from '@element-plus/icons-vue'
  187. const position = ref('left')
  188. const router = useRouter();
  189. // 表格
  190. const basicTableRef = ref<InstanceType<typeof BasicTable>>();
  191. const { tableConfig, pagination } = useTableConfig(TABLE_COLUMNS, TABLE_OPTIONS);
  192. const tableData = ref<any[]>([]);
  193. const fetchSafetyOrganizationList = ref<any[]>([]);
  194. const activeName = ref('');
  195. // 日期范围(用于日期选择器)
  196. const dateRange = ref<[string, string] | string>('');
  197. const tableQuery = reactive<QueryPageRequest<any>>({
  198. pageNumber: pagination.pageNumber,
  199. pageSize: pagination.pageSize,
  200. queryParam: {
  201. classifyName: '',
  202. keyword: '',
  203. status: '',
  204. startTime: '',
  205. endTime: '',
  206. },
  207. });
  208. const handleSizeChange = (value: number) => {
  209. pagination.pageSize = value;
  210. tableQuery.pageSize = value;
  211. getTableData();
  212. };
  213. const handleCurrentChange = (value: number) => {
  214. pagination.pageNumber = value;
  215. tableQuery.pageNumber = value;
  216. getTableData();
  217. };
  218. async function getTableData() {
  219. tableConfig.loading = true;
  220. try {
  221. const res = await fetchTableList(tableQuery);
  222. if (res) {
  223. tableData.value = res.records
  224. pagination.total = res.totalRow;
  225. }
  226. } catch (e) {
  227. console.error('获取列表失败:', e);
  228. tableData.value = [];
  229. pagination.total = 0;
  230. } finally {
  231. tableConfig.loading = false;
  232. }
  233. }
  234. interface addSafetyOrganizationSystemFormDataType {
  235. type: String;
  236. orgName?: String;
  237. orgId?: String | number;
  238. action?: String;
  239. parentid?: String | number;
  240. }
  241. const addSafetySystemVisible = ref(false);
  242. const addSafetyOrganizationSystemFormData = ref<addSafetyOrganizationSystemFormDataType>({
  243. type: '',
  244. orgName: '',
  245. orgId: '',
  246. action: '',
  247. parentid: '',
  248. });
  249. // 一级新增
  250. const addTeam = (type) => {
  251. addSafetyOrganizationSystemFormData.value = {
  252. type,
  253. action: 'add',
  254. };
  255. addSafetySystemVisible.value = true;
  256. };
  257. // 获取组织列表
  258. const fetchSafetyOrganizationTeamList = async () => {
  259. try {
  260. const res = await getSafetySystemList();
  261. fetchSafetyOrganizationList.value = res;
  262. } catch (error) {
  263. ElMessage.error('获取组织列表失败');
  264. }
  265. };
  266. // 定义组织数据类型
  267. interface SafetySystemFormData {
  268. value: string; // 输入的组织名称
  269. action: 'add' | 'edit'; // 操作类型:新增或编辑
  270. orgId?: string | number; // 组织ID(编辑时必传)
  271. parentid?: string | number; // 父组织ID(新增子组织时必传)
  272. type?: 'children' | 'parent'; // 组织类型(子组织或根组织)
  273. }
  274. // 保存弹窗回调
  275. const confirmAddSafetySystemCallback = async (formData: SafetySystemFormData) => {
  276. try {
  277. if (!formData.value?.trim()) {
  278. ElMessage.warning('请输入有效的组织名称!');
  279. return;
  280. }
  281. const requestData = {
  282. orgName: formData.value.trim(),
  283. id: formData.action === 'edit' ? formData.orgId : undefined,
  284. parentid: formData.action === 'add' ? formData.parentid : undefined,
  285. };
  286. // console.log(formData, 'formData')
  287. if (formData.action === 'add') {
  288. if (formData.type === 'children' && formData.orgId) {
  289. requestData.parentid = formData.orgId;
  290. }
  291. await addSafetySystem(requestData);
  292. ElMessage.success('新增组织成功!');
  293. } else {
  294. // 如果是子类,补充父级ID
  295. if (formData.type === 'children' && formData.parentid) {
  296. requestData.parentid = formData.parentid;
  297. }
  298. await updateSafetySystem(requestData);
  299. ElMessage.success('编辑组织成功!');
  300. }
  301. // 刷新列表
  302. fetchSafetyOrganizationTeamList();
  303. } catch (error) {
  304. console.error('操作失败:', error);
  305. ElMessage.error(formData.action === 'add' ? '新增组织失败!' : '编辑组织失败!');
  306. }
  307. };
  308. // 二级新增
  309. const handleCreateSafetySystem = async (type, value) => {
  310. addSafetyOrganizationSystemFormData.value = {
  311. type,
  312. action: 'add',
  313. orgName: value.orgName,
  314. orgId: value.orgId,
  315. };
  316. addSafetySystemVisible.value = true;
  317. // 打开某一个
  318. activeName.value = value.orgId;
  319. };
  320. // 编辑
  321. const handleEditSafetySystem = (type, value, parentid) => {
  322. // console.log(value)
  323. addSafetySystemVisible.value = true;
  324. addSafetyOrganizationSystemFormData.value = {
  325. type,
  326. action: 'edit',
  327. orgName: value.orgName,
  328. orgId: value.orgId,
  329. parentid,
  330. };
  331. };
  332. // 删除
  333. const handleDelSafetySystem = async (type, value) => {
  334. // console.log('删除', type, value)
  335. ElMessageBox.confirm('确认删除该组织吗?', '警告', { type: 'warning' }).then(async () => {
  336. try {
  337. await deleteSafetySystem(value.orgId);
  338. ElMessage.success('删除成功');
  339. // 刷新列表
  340. fetchSafetyOrganizationTeamList();
  341. handleReset();
  342. } catch (error) {
  343. ElMessage.error(error || '删除失败');
  344. }
  345. });
  346. };
  347. // 查询
  348. const querySafetyTeamData = (type, value) => {
  349. console.log(type, '查询', value);
  350. tableQuery.queryParam.classifyName = value.orgId;
  351. getTableData();
  352. };
  353. // 时间查询
  354. const onchangeDateRange = () => {
  355. if (dateRange.value && Array.isArray(dateRange.value) && dateRange.value.length === 2) {
  356. tableQuery.queryParam.startTime = dateRange.value[0] || '';
  357. tableQuery.queryParam.endTime = dateRange.value[1] || '';
  358. } else {
  359. tableQuery.queryParam.startTime = '';
  360. tableQuery.queryParam.endTime = '';
  361. }
  362. getTableData();
  363. };
  364. const handleSearch = () => {
  365. pagination.pageNumber = 1;
  366. tableQuery.pageNumber = 1;
  367. getTableData();
  368. };
  369. const handleReset = () => {
  370. pagination.pageNumber = 1;
  371. tableQuery.queryParam = {
  372. classifyName: '',
  373. keyword: '',
  374. status: '', // 重置为默认启用状态
  375. startTime: '',
  376. endTime: '',
  377. };
  378. dateRange.value = '';
  379. handleSearch();
  380. };
  381. // 批量导入
  382. const batchImportVisible = ref(false);
  383. const { urlPrefix } = useGlobSetting();
  384. const importApiUrl = ref(urlJoin(urlPrefix, '/safetyorguser/importSafetyOrgUser'));
  385. const templateUrl = ref('./skyeye-file-upload/sfysecurity/TEMPLATE/安全组织体系管理导入模版.xlsx');
  386. const handleImport = () => {
  387. batchImportVisible.value = true;
  388. };
  389. const handleUpdate = () => {
  390. batchImportVisible.value = false;
  391. getTableData();
  392. };
  393. const handleDownload = async () => {
  394. try {
  395. const response = await exportSafetyOrganizationSystemManagement(tableQuery.queryParam);
  396. if (response) {
  397. const fileName = `安全组织体系管理_${new Date().toISOString().split('T')[0]}.xlsx`;
  398. downloadByData(response, fileName);
  399. ElMessage.success('导出成功');
  400. }
  401. } catch (e) {
  402. console.error('导出安全组织体系管理失败:', e);
  403. ElMessage.error('导出失败,请重试');
  404. }
  405. };
  406. const handleCreate = () => {
  407. router.push({
  408. name: 'SafetyOrganizationSystemManagementItem',
  409. query: {
  410. operate: 'employee-create',
  411. },
  412. });
  413. };
  414. const handleEdit = (id: number) => {
  415. router.push({
  416. name: 'SafetyOrganizationSystemManagementItem',
  417. query: {
  418. id,
  419. operate: 'employee-edit',
  420. },
  421. });
  422. };
  423. const handleDelete = async (id: number) => {
  424. try {
  425. await delEmployee(id);
  426. ElMessage.success('删除成功');
  427. getTableData();
  428. } catch (e) {
  429. console.error('删除员工失败:', e);
  430. ElMessage.error('删除失败,请重试');
  431. }
  432. };
  433. const handleView = (id: number) => {
  434. router.push({
  435. name: 'SafetyOrganizationSystemManagementItem',
  436. query: {
  437. id,
  438. operate: 'employee-view',
  439. },
  440. });
  441. };
  442. onMounted(() => {
  443. fetchSafetyOrganizationTeamList();
  444. getTableData();
  445. });
  446. </script>
  447. <style scoped lang="scss">
  448. @use '@/styles/page-details-layout.scss' as *;
  449. @use '@/styles/page-main-layout.scss' as *;
  450. @use '@/styles/basic-table-action.scss' as *;
  451. @use '@/views/traffic/violation/style/act-search-table.scss' as *;
  452. .table-content {
  453. }
  454. .nav {
  455. flex: 0 0 300px;
  456. margin-right: 15px;
  457. padding-right: 15px;
  458. border-right: 1px solid #eee;
  459. :deep(.collapse-title) {
  460. flex: 1 0 90%;
  461. order: 1;
  462. .el-collapse-item__header {
  463. flex: 1 0 auto;
  464. order: -1;
  465. }
  466. }
  467. .collapse-wrapper {
  468. margin-top: 10px;
  469. .title-wrapper {
  470. display: flex;
  471. justify-content: space-between;
  472. align-items: center;
  473. width: 100%;
  474. .handler {
  475. flex: 1;
  476. display: flex;
  477. justify-content: flex-end;
  478. align-items: center;
  479. padding-right: 15px;
  480. }
  481. }
  482. .collapse-item-content {
  483. ul {
  484. padding-left: 40px;
  485. li {
  486. display: flex;
  487. justify-content: space-between;
  488. align-items: center;
  489. width: 100%;
  490. padding: 6px 0;
  491. border-bottom: 1px solid #eeeeeed1;
  492. span {
  493. cursor: pointer;
  494. }
  495. }
  496. }
  497. }
  498. }
  499. }
  500. </style>