综合办公系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

resourceTree.vue 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <template>
  2. <div>
  3. <el-row :gutter="10" class="mb8">
  4. <el-col :span="1.5">
  5. <el-button type="primary" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">展开/折叠</el-button>
  6. </el-col>
  7. </el-row>
  8. <el-input v-model="filterText" placeholder="输入关键字过滤" />
  9. <el-tree v-if="refreshTable" ref="tree" :data="treeData" :props="defaultProps" node-key="fieldId"
  10. :default-expanded-keys="[0,100]" @node-click="handleNodeClick" :highlight-current="true"
  11. :filter-node-method="filterNode" :default-expand-all="isExpandAll" :expand-on-click-node="false">
  12. <!-- 自定义节点样式 -->
  13. <span class="custom-tree-node" slot-scope="{ node, data }">
  14. <span>{{ node.label }}</span>
  15. <span v-if="isModify">
  16. <el-button v-hasPermi="['oa:resource:edit']" type="text" icon="el-icon-plus" size="mini"
  17. @click="() => append(data)">
  18. 新增
  19. </el-button>
  20. <el-button v-hasPermi="['oa:resource:remove']" type="text" icon="el-icon-delete" size="mini"
  21. @click="() => remove(node, data)">
  22. 删除
  23. </el-button>
  24. </span>
  25. </span>
  26. </el-tree>
  27. <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
  28. <el-form ref="formRef" :model="form" :rules="rules">
  29. <el-form-item label="父级专业" prop="parentId">
  30. <treeselect v-model="form.parentId" :options="treeData" :normalizer="normalizer" :show-count="true"
  31. placeholder="选择上级专业" />
  32. </el-form-item>
  33. <el-form-item label="专业名称" prop="field">
  34. <el-input v-model="form.field" placeholder="请输入专业名称" />
  35. </el-form-item>
  36. </el-form>
  37. <div slot="footer" class="dialog-footer">
  38. <el-button @click="open = false">取 消</el-button>
  39. <el-button type="primary" @click="submitForm">确 定</el-button>
  40. </div>
  41. </el-dialog>
  42. </div>
  43. </template>
  44. <script>
  45. import { listResource, getResource, delResource, addResource, updateResource } from "@/api/oa/study/resource";
  46. import Treeselect from "@riophae/vue-treeselect";
  47. import "@riophae/vue-treeselect/dist/vue-treeselect.css";
  48. export default {
  49. components: { Treeselect },
  50. props: {
  51. isModify: {
  52. type: Boolean,
  53. default: false
  54. },
  55. fields: {
  56. type: Array,
  57. default: []
  58. }
  59. },
  60. data() {
  61. return {
  62. treeData: [], // 树形结构数据
  63. defaultProps: { // 树形组件配置
  64. children: 'children',
  65. label: 'field'
  66. },
  67. filterText: '',
  68. isExpandAll: false,
  69. refreshTable: true,
  70. title: '',
  71. open: false,
  72. form: {
  73. fieldId: undefined,
  74. field: "",
  75. parentId: undefined,
  76. },
  77. rules: {
  78. field: [
  79. { required: true, message: "专业名称不能为空", trigger: "blur" }
  80. ],
  81. parentId: [
  82. { required: true, message: "父级专业不能为空", trigger: "change" }
  83. ]
  84. },
  85. // 菜单树选项
  86. menuOptions: [],
  87. }
  88. },
  89. watch: {
  90. filterText(val) {
  91. this.$refs.tree.filter(val);
  92. },
  93. // 监听父组件传入的 fields
  94. fields: {
  95. immediate: true,
  96. handler(newVal) {
  97. this.initTree(newVal || []);
  98. }
  99. }
  100. },
  101. methods: {
  102. async initTree(fields) {
  103. // 空数据保护
  104. if (!fields || fields.length === 0) {
  105. this.treeData = [{
  106. fieldId: 0,
  107. field: undefined,
  108. children: []
  109. }];
  110. this.$emit('getTreeData', []);
  111. return;
  112. }
  113. const built = this.buildTree(fields);
  114. // 根节点按首项规则决定标题和 id
  115. if (fields[0].fieldId === 0) {
  116. this.treeData = [{
  117. fieldId: 0,
  118. field: '全部专业',
  119. children: built
  120. }];
  121. } else {
  122. this.treeData = [{
  123. fieldId: 100,
  124. field: '全部学习',
  125. children: built
  126. }];
  127. }
  128. this.$emit('getTreeData', built);
  129. },
  130. buildTree(items) {
  131. const map = {}; // 哈希表存储所有节点
  132. const roots = []; // 存储根节点
  133. // 先遍历所有项,存入哈希表
  134. items.forEach(item => {
  135. map[item.fieldId] = { ...item, children: [] };
  136. });
  137. // 再次遍历建立父子关系
  138. items.forEach(item => {
  139. const node = map[item.fieldId];
  140. if (item.parentId === 0 || item.parentId === 100) {
  141. roots.push(node);
  142. }
  143. else {
  144. const parent = map[item.parentId];
  145. if (parent) {
  146. parent.children.push(node);
  147. }
  148. }
  149. });
  150. return roots;
  151. },
  152. filterNode(value, data) {
  153. if (!value) return true;
  154. return data.field.indexOf(value) !== -1;
  155. },
  156. async loadNode(node, resolve) {
  157. if (node.level === 0) {
  158. // 加载根节点
  159. return resolve(await this.fetchRootNodes());
  160. }
  161. // 加载子节点
  162. resolve(await this.fetchChildNodes(node.key));
  163. },
  164. handleNodeClick(data) {
  165. this.$emit('clickNode', data)
  166. },
  167. append(data) {
  168. this.resetForm();
  169. this.title = "新增专业";
  170. this.open = true;
  171. this.form.parentId = data.fieldId; // 设置父节点为当前节点
  172. },
  173. async remove(node, data) {
  174. if (!data.children) {
  175. data.children = []
  176. }
  177. let list = await listResource({ fieldId: data.fieldId });
  178. this.$confirm('确定删除该专业吗?', "警告", {
  179. confirmButtonText: "确定",
  180. cancelButtonText: "取消",
  181. type: "warning"
  182. }).then(() => {
  183. if (data.children.length > 0) {
  184. this.$message.error("删除失败,存在子专业,无法删除!");
  185. return
  186. }
  187. if (list.total > 0) {
  188. this.$message.error("删除失败,存在资料数据,无法删除!");
  189. return
  190. }
  191. delField(data.fieldId).then(() => {
  192. this.$message.success("删除成功");
  193. this.initTree();
  194. // 如果删除的是当前选中节点,清空选择
  195. if (this.$refs.tree.getCurrentKey() === data.fieldId) {
  196. this.$emit("clickNode", {});
  197. }
  198. });
  199. });
  200. },
  201. /** 展开/折叠操作 */
  202. toggleExpandAll() {
  203. this.refreshTable = false;
  204. this.isExpandAll = !this.isExpandAll;
  205. this.$nextTick(() => {
  206. this.refreshTable = true;
  207. });
  208. },
  209. // 表单提交
  210. submitForm() {
  211. this.$refs.formRef.validate(valid => {
  212. if (valid) {
  213. if (this.form.fieldId) {
  214. updateField(this.form).then(() => {
  215. this.$message.success("修改成功");
  216. this.open = false;
  217. this.initTree();
  218. });
  219. } else {
  220. addField(this.form).then(() => {
  221. this.$message.success("新增成功");
  222. this.open = false;
  223. this.initTree();
  224. });
  225. }
  226. }
  227. });
  228. },
  229. // 重置表单
  230. resetForm() {
  231. this.form = {
  232. fieldId: undefined,
  233. field: "",
  234. parentId: undefined
  235. };
  236. if (this.$refs.formRef) {
  237. this.$refs.formRef.resetFields();
  238. }
  239. },
  240. /** 转换菜单数据结构 */
  241. normalizer(node) {
  242. if (node.children && !node.children.length) {
  243. delete node.children;
  244. }
  245. return {
  246. id: node.fieldId,
  247. label: node.field,
  248. children: node.children
  249. };
  250. },
  251. },
  252. }
  253. </script>
  254. <style lang="scss" scoped>
  255. .tree-container {
  256. width: 300px;
  257. padding: 20px;
  258. }
  259. .custom-tree-node {
  260. flex: 1;
  261. display: flex;
  262. align-items: center;
  263. justify-content: space-between;
  264. font-size: 14px;
  265. padding: 8px 0;
  266. }
  267. </style>