index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. <template>
  2. <div class="zhiwei p-4">
  3. <div class="left-tree-box">
  4. <div class="tree-title">机构列表</div>
  5. <div class="tree-box">
  6. <div class="search-input">
  7. <a-input-search allowClear v-model:value="treeSearchValue" placeholder="搜索关键字"
  8. style="width: 100%;height:100%;" />
  9. </div>
  10. <div class="tree-container">
  11. <a-directory-tree :tree-data="treeData" :expandedKeys="expandedKeys" class="tree-list"
  12. :showIcon="false" :auto-expand-parent="autoExpandParent" @expand="onExpand"
  13. :replaceFields="replaceFields" @select="nodeSelect">
  14. <template #switcherIcon>
  15. <div class="self-switcher-icon"></div>
  16. </template>
  17. <template #title="{ name, selected, dataRef }">
  18. <span v-if="name.indexOf(treeSearchValue) > -1">
  19. {{ name.substr(0, name.indexOf(treeSearchValue)) }}
  20. <span style="color: #f50">{{ treeSearchValue }}</span>
  21. {{ name.substr(name.indexOf(treeSearchValue) + treeSearchValue.length) }}
  22. </span>
  23. <span v-else>{{ name }}</span>
  24. </template>
  25. </a-directory-tree>
  26. </div>
  27. </div>
  28. </div>
  29. <div class="right-table-box">
  30. <BasicTable @register="registerTable" class="basic-table">
  31. <template #toolbar>
  32. <Button type="primary" @click="handleAdd">
  33. 新增
  34. </Button>
  35. </template>
  36. <template #action="{ record }">
  37. <TableAction :actions="[
  38. {
  39. label: '编辑',
  40. tooltip: '编辑',
  41. onClick: handleEdit.bind(null, record),
  42. },
  43. {
  44. label: '删除',
  45. tooltip: '删除',
  46. //color: 'error',
  47. onClick: handleDelete.bind(null, record.departid),
  48. },
  49. ]" />
  50. </template>
  51. </BasicTable>
  52. </div>
  53. <ZhiweiManageModal @register="registerModal" @success="onSubmit"></ZhiweiManageModal>
  54. </div>
  55. </template>
  56. <script>
  57. import { defineComponent, reactive, ref, toRefs, computed, onMounted, watch, createVNode } from 'vue';
  58. // 导入接口
  59. import { structureList, getPostsList, delPosts } from '/@/api/sys/zhiwei';
  60. // 导入表格组件,表格事件
  61. import { BasicTable, useTable, TableAction } from '/@/components/Table';
  62. import { useModal } from '/@/components/Modal';
  63. import { Button } from 'ant-design-vue';
  64. import { message, Modal } from 'ant-design-vue';
  65. import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
  66. import ZhiweiManageModal from './ZhiweiManageModal.vue';
  67. export default defineComponent({
  68. name: 'zhiwei',
  69. components: { BasicTable, TableAction, Button, ZhiweiManageModal, ExclamationCircleOutlined },
  70. setup(props, { emit }) {
  71. const [registerModal, { openModal }] = useModal();
  72. // 机构树所用的数据
  73. const data = reactive({
  74. replaceFields: {
  75. children: 'children',
  76. title: 'name',
  77. key: 'id'
  78. },
  79. treeData: [],//单位列表树
  80. treeSearchValue: '',
  81. //用于树状结构搜索的dataList
  82. dataList: [],
  83. expandedKeys: [],
  84. autoExpandParent: true,
  85. });
  86. watch(
  87. () => data.treeSearchValue,
  88. (value) => {
  89. if (value) {
  90. let expanded = data.dataList
  91. .map(item => {
  92. if (item.title.indexOf(value) > -1) {
  93. return getParentKey(item.key, data.treeData);
  94. }
  95. return null;
  96. })
  97. .filter((item, i, self) => item && self.indexOf(item) === i);
  98. data.expandedKeys = expanded;
  99. } else {
  100. data.expandedKeys = [];
  101. }
  102. data.treeSearchValue = value;
  103. data.autoExpandParent = true;
  104. }
  105. )
  106. //生成用于树搜索的dataList
  107. const generateList = (treeData) => {
  108. for (let i = 0; i < treeData.length; i++) {
  109. const node = treeData[i];
  110. const key = node.id;
  111. data.dataList.push({
  112. key,
  113. title: node.name,
  114. });
  115. if (node.children) {
  116. generateList(node.children);
  117. }
  118. }
  119. }
  120. //获取所有父节点key
  121. const getParentKey = (key, tree) => {
  122. let parentKey;
  123. for (let i = 0; i < tree.length; i++) {
  124. const node = tree[i];
  125. if (node.children) {
  126. if (node.children.some(item => item.id === key)) {
  127. parentKey = node.id;
  128. } else if (getParentKey(key, node.children)) {
  129. parentKey = getParentKey(key, node.children);
  130. }
  131. }
  132. }
  133. return parentKey;
  134. }
  135. //树结构展开时触发
  136. const onExpand = (keys) => {
  137. data.expandedKeys = keys;
  138. data.autoExpandParent = false;
  139. }
  140. const departmentId = ref(null)
  141. const nodeSelect = (selectedKeys, { selectedNode, node }) => {
  142. if (departmentId.value === node.dataRef.id) {
  143. return;
  144. } else {
  145. departmentId.value = node.dataRef.id
  146. reload();
  147. }
  148. }
  149. //根据部门ID获取职位列表
  150. const getAllData = (id) => {
  151. return new Promise((resolve) => {
  152. if (departmentId.value) {
  153. let params = {
  154. departmentId: departmentId.value
  155. }
  156. getPostsList(params).then(res => {
  157. if (res.datas && res.datas?.length) {
  158. resolve(res.datas)
  159. } else {
  160. message.info('该部门下无职位数据')
  161. resolve([])
  162. }
  163. })
  164. } else {
  165. resolve([])
  166. }
  167. })
  168. }
  169. const columns = [
  170. {
  171. title: '职位名称',
  172. dataIndex: 'departName',
  173. key: 'departName',
  174. align: 'center'
  175. },
  176. {
  177. title: '排序',
  178. dataIndex: 'orderId',
  179. key: 'orderId',
  180. align: 'center'
  181. },
  182. ]
  183. //注册职位表格
  184. const [
  185. registerTable,
  186. { reload, getDataSource },
  187. ] = useTable({
  188. title: '职位列表', //'菜单列表'
  189. api: getAllData, //加载数据
  190. columns: columns,
  191. useSearchForm: false, //开启搜索区域
  192. bordered: true,
  193. showTableSetting: true, // 显示表格设置
  194. tableSetting: {
  195. redo: true,
  196. size: true,
  197. setting: false,
  198. fullScreen: false
  199. },
  200. canResize: false,
  201. showIndexColumn: true,
  202. pagination: {
  203. // pageSize: 10,
  204. hideOnSinglePage: false
  205. },
  206. actionColumn: {
  207. width: 100,
  208. title: '操作',
  209. dataIndex: 'action',
  210. slots: { customRender: 'action' },
  211. }
  212. });
  213. const formData = ref(null)
  214. //新增职位
  215. const handleAdd = () => {
  216. if (!departmentId.value) {
  217. message.error('没有选择部门');
  218. return;
  219. }
  220. let tempData = getDataSource();
  221. formData.value = {
  222. departid: "",
  223. parentId: "",
  224. departName: "",
  225. orderId: tempData.length + 1,
  226. orgId: departmentId.value
  227. }
  228. openModal(true, {
  229. status: 'add',
  230. form: {
  231. orderId: tempData.length + 1,
  232. orgId: departmentId.value
  233. }
  234. });
  235. }
  236. //修改职位
  237. const handleEdit = (item) => {
  238. formData.value = {
  239. departid: item.departid,
  240. parentId: item.parentId,
  241. departName: item.departName,
  242. orderId: item.orderId
  243. }
  244. openModal(true, {
  245. status: 'edit',
  246. form: {
  247. departid: item.departid,
  248. parentId: item.parentId,
  249. departName: item.departName,
  250. orderId: item.orderId
  251. }
  252. });
  253. }
  254. //删除职位
  255. const handleDelete = (id) => {
  256. Modal.confirm({
  257. title: '提示',
  258. icon: createVNode(ExclamationCircleOutlined),
  259. content: '确定删除该职位数据?',
  260. centered: true,
  261. okText: '确定',
  262. okType: 'danger',
  263. cancelText: '取消',
  264. onOk: (() => {
  265. let params = {
  266. orgId: departmentId.value,
  267. departid: id
  268. }
  269. delPosts(params).then(res => {
  270. if (res.resp_code === 0) {
  271. message.success('操作成功')
  272. onSubmit()
  273. } else if (res.resp_code === 1) {
  274. message.info(res.resp_msg)
  275. } else {
  276. message.error('操作失败')
  277. }
  278. })
  279. })
  280. });
  281. }
  282. const onSubmit = () => {
  283. reload();
  284. }
  285. const getKeys = (treeData) => {
  286. let keys = [];
  287. treeData.forEach(element => {
  288. keys.push(element.id)
  289. if (element.children?.length) {
  290. element.children.forEach(item => {
  291. keys.push(item.id)
  292. })
  293. }
  294. });
  295. return keys
  296. }
  297. onMounted(() => {
  298. //初始化时获取所有机构
  299. structureList().then(res => {
  300. if (res.length) {
  301. data.treeData = res
  302. data.dataList = []
  303. generateList(data.treeData)
  304. const keys = getKeys(data.treeData)
  305. data.expandedKeys = keys;
  306. }
  307. })
  308. });
  309. return {
  310. formData,
  311. ...toRefs(data),
  312. //func
  313. onExpand,
  314. nodeSelect,
  315. registerTable,
  316. registerModal,
  317. reload,
  318. handleAdd,
  319. handleEdit,
  320. handleDelete,
  321. onSubmit
  322. };
  323. },
  324. });
  325. </script>
  326. <style lang="less" scoped>
  327. .zhiwei {
  328. height: 100%;
  329. display: flex;
  330. .left-tree-box {
  331. width: 300px;
  332. // height: 960px;
  333. height: 100%;
  334. background-color: #fff;
  335. border-radius: 6px;
  336. .tree-title {
  337. height: 50px;
  338. line-height: 50px;
  339. text-align: center;
  340. font-family: Source Han Sans CN;
  341. font-size: 16px;
  342. font-weight: bold;
  343. color: #333333;
  344. border-bottom: 1px solid #DEDEDE;
  345. }
  346. .tree-box {
  347. margin-top: 14px;
  348. padding: 0 10px;
  349. .search-input {
  350. height: 34px;
  351. }
  352. .tree-container {
  353. max-height: 680px;
  354. overflow: auto;
  355. &::-webkit-scrollbar {
  356. width: 3px;
  357. }
  358. &::-webkit-scrollbar-thumb {
  359. border-radius: 2px;
  360. background: #ccd5df;
  361. }
  362. &::-webkit-scrollbar-track {
  363. display: none;
  364. }
  365. .tree-list {
  366. font-family: Alibaba PuHuiTi 2.0;
  367. ::v-deep .ant-tree-switcher_close {
  368. .ant-tree-switcher-icon {
  369. margin-top: 5px;
  370. border-left: 12px solid #888888;
  371. border-top: 7px solid transparent;
  372. border-bottom: 7px solid transparent;
  373. transform: rotate(0);
  374. border-radius: 3px;
  375. }
  376. }
  377. ::v-deep .ant-tree-switcher_open {
  378. .ant-tree-switcher-icon {
  379. margin-top: 5px;
  380. border-left: 12px solid #888888;
  381. border-top: 7px solid transparent;
  382. border-bottom: 7px solid transparent;
  383. transform: rotate(90deg);
  384. border-radius: 3px;
  385. }
  386. }
  387. ::v-deep .ant-tree-treenode-selected {
  388. .ant-tree-node-content-wrapper.ant-tree-node-content-wrapper-normal.ant-tree-node-selected::before {
  389. background: #e6f0fb !important;
  390. }
  391. .ant-tree-node-content-wrapper.ant-tree-node-content-wrapper-open.ant-tree-node-selected::before {
  392. background: #e6f0fb !important;
  393. }
  394. .ant-tree-node-content-wrapper.ant-tree-node-content-wrapper-close.ant-tree-node-selected::before {
  395. background: #e6f0fb !important;
  396. }
  397. .ant-tree-node-content-wrapper.ant-tree-node-content-wrapper-normal.ant-tree-node-selected {
  398. color: #0671DD;
  399. }
  400. .ant-tree-node-content-wrapper.ant-tree-node-content-wrapper-open.ant-tree-node-selected {
  401. color: #0671DD;
  402. }
  403. .ant-tree-node-content-wrapper.ant-tree-node-content-wrapper-close.ant-tree-node-selected {
  404. color: #0671DD;
  405. }
  406. }
  407. }
  408. }
  409. }
  410. }
  411. .right-table-box {
  412. padding: 0 20px;
  413. margin-left: 8px;
  414. width: calc(100% - 308px);
  415. height: 100%;
  416. background-color: #fff;
  417. border-radius: 6px;
  418. .basic-table {
  419. height: 100%;
  420. ::v-deep .ant-table-title {
  421. padding: 0 !important;
  422. .vben-basic-title {
  423. font-family: '阿里巴巴普惠体 2.0';
  424. font-size: 16px;
  425. font-weight: bold;
  426. color: #333333;
  427. }
  428. }
  429. }
  430. }
  431. }
  432. </style>