Browse Source

增加动态加载树 todo:树拖拽功能与交互

liceal 4 years ago
parent
commit
89d9dfea45

+ 4 - 0
.env.development

@@ -0,0 +1,4 @@
+NODE_ENV = 'development'
+
+# 生产环境
+VUE_APP_BASE_API = '/work'

+ 4 - 0
.env.production

@@ -0,0 +1,4 @@
+NODE_ENV = 'production'
+
+# 开发环境
+VUE_APP_BASE_API = '/'

+ 57 - 0
src/Api/tree.js

@@ -0,0 +1,57 @@
+import axios from '@/axios/http';
+
+const URL = {
+    getTree: 'tree',
+    addTree: 'tree/',
+    delTree: 'tree/',
+    putTree: 'tree/',
+}
+
+/**
+ * 得到树
+ * @param {Object} params {parent_id:父级节点id(可空,空则返回顶级树)} 
+ */
+export function getNode(params) {
+    return axios({
+        url: URL.getTree,
+        method: 'get',
+        params
+    })
+}
+
+/**
+ * 新增叶子
+ * 如果父级id空,则创建为顶级树
+ * @param {Object} data {name:名字,parent_id:父级id(可空)} 
+ */
+export function addNode(data) {
+    return axios({
+        url: URL.addTree,
+        method: 'post',
+        data
+    })
+}
+
+/**
+ * 删除节点
+ * @param {Object} data {id:节点id}
+ */
+export function delNode(data) {
+    return axios({
+        url: URL.delTree,
+        method: 'delete',
+        data
+    })
+}
+
+/**
+ * 更新节点
+ * @param {Object} data {id:节点id,name:节点名字,parent_id:父级id(可空)}
+ */
+export function putNode(data) {
+    return axios({
+        url: URL.putTree,
+        method: 'put',
+        data
+    })
+}

+ 22 - 16
src/App.vue

@@ -15,14 +15,14 @@
           :index="item.path.split(':')[0]"
           v-if="!item.hasOwnProperty('children')"
           :key="index"
-        >{{item.name}}</el-menu-item>
+        >{{item.alias}}</el-menu-item>
         <el-submenu :index="$route.path" v-else :key="index">
-          <template slot="title">{{item.name}}</template>
+          <template slot="title">{{item.alias}}</template>
           <el-menu-item
             v-for="(child,index_child) in item.children"
             :key="index_child"
             :index="item.path+'/'+child.path.split(':')[0]"
-          >{{child.name}}</el-menu-item>
+          >{{child.alias}}</el-menu-item>
         </el-submenu>
       </template>
     </el-menu>
@@ -33,7 +33,7 @@
 
     <div class="buttons">
       <!-- :disabled="!isLogin" -->
-      <el-button type="primary" @click="dialogVisible=true" >登陆</el-button>
+      <el-button type="primary" @click="dialogVisible=true" :disabled="!isLogin">登陆</el-button>
       <el-button type="primary" @click="logout">登出</el-button>
     </div>
 
@@ -66,10 +66,15 @@ export default {
       user: {
         username: "",
         password: ""
-      },
-      isLogin: false
+      }
     };
   },
+  computed: {
+    isLogin() {
+      console.log("csrftoken", this.$Cookie.get("csrftoken"));
+      return this.$Cookie.get("csrftoken") === undefined;
+    }
+  },
   methods: {
     login() {
       console.log("login");
@@ -79,9 +84,9 @@ export default {
           console.log(res);
           this.$nextTick(() => {
             if (document.cookie !== "") {
-              console.log('刷新界面');
-              location.reload() 
-              this.dialogVisible=false
+              console.log("刷新界面");
+              location.reload();
+              this.dialogVisible = false;
               this.isLogin = true;
             } else {
               this.isLogin = false;
@@ -90,18 +95,19 @@ export default {
         })
         .catch(e => {
           this.$message({
-            type:'error',
-            message:e
-          })
+            type: "error",
+            message: e
+          });
           console.log(e);
         });
     },
     logout() {
-      logout().then(res=>{
-        this.$Cookie.remove('csrftoken')
-        console.log(this.$Cookie.get());
+      logout().then(res => {
+        this.$Cookie.remove("csrftoken");
+        console.log(document.cookie);
         console.log(res);
-      })
+      });
+      this.$Cookie.remove("csrftoken");
       console.log("登出");
     }
   }

+ 6 - 6
src/axios/http.js

@@ -1,15 +1,15 @@
 import axios from 'axios'
 
-axios.defaults.baseURL = "/work/"
+// ${process.env.VUE_APP_BASEURL}
+axios.defaults.baseURL = `${process.env.VUE_APP_BASE_API}/`
 axios.defaults.timeout = 30000
 
-// 路由请求拦截
 // http request 拦截器
 axios.interceptors.request.use(
     config => {
-        console.log('发送请求拦截');
-        console.log('cookie:', document.cookie.split('=')[1]);
-        // document.cookie = document.cookie.split('=')[1]
+        // console.log('发送请求拦截');
+        // console.log('cookie:', document.cookie.split('=')[1]);
+
         config.headers['X-CSRFToken'] = document.cookie.split('=')[1]
 
         return config;
@@ -21,7 +21,7 @@ axios.interceptors.request.use(
 
 axios.interceptors.response.use(
     response => {
-        console.log('接受请求拦截');
+        // console.log('接受请求拦截');
         return response;
     },
     err => {

+ 31 - 28
src/router/index.js

@@ -5,52 +5,55 @@ Vue.use(VueRouter)
 
 const routes = [{
         path: '/',
-        name: '主页',
+        name: 'home',
+        alias: '主页',
         component: () =>
             import ('@/views/home.vue')
     },
     {
         path: '/hours',
-        name: '我的家',
+        name: 'hours',
+        alias: '我的家',
         component: () =>
             import ('@/views/hours/hours.vue')
     },
     {
         path: '/food',
-        name: '菜场',
+        name: 'food',
+        alias: '菜场',
         component: () =>
             import ('@/views/food/food.vue'),
     },
     {
-        path: '/report/list',
-        name: '日报列表',
+        path: '/report',
+        name: 'report',
+        alias: '日报',
+        redirect: '/report/list',
         component: () =>
-            import ('@/views/report/reportList/reportList.vue')
+            import ('@/views/report/report.vue'),
+        children: [{
+                path: 'list',
+                name: 'list',
+                alias: "列表",
+                component: () =>
+                    import ('@/views/report/reportList/reportList.vue')
+            },
+            {
+                path: 'detail/:id(\\d+)?',
+                name: 'detail',
+                alias: "详情与修改",
+                component: () =>
+                    import ('@/views/report/reportDetail/reportDetail.vue')
+            }
+        ]
     },
     {
-        path: '/report/detail/:id(\\d+)?', //+多位
-        name: '日报的详情与修改',
+        path: '/tree',
+        name: 'tree',
+        alias: '动态加载树',
         component: () =>
-            import ('@/views/report/reportDetail/reportDetail.vue')
-    },
-    // {
-    //     path: '/report',
-    //     name: '日报',
-    //     redirect: '/report/list',
-    //     children: [{
-    //             path: 'list',
-    //             name: '列表',
-    //             component: () =>
-    //                 import ('@/views/report/reportList/reportList.vue')
-    //         },
-    //         {
-    //             path: 'detail/:id(\\d+)?',
-    //             name: '详情与修改',
-    //             component: () =>
-    //                 import ('@/views/report/reportDetail/reportDetail.vue')
-    //         }
-    //     ]
-    // }
+            import ('@/views/tree/tree.vue')
+    }
 ]
 
 const router = new VueRouter({

+ 24 - 8
src/views/check/check.vue

@@ -1,5 +1,8 @@
 <template>
 	<div>
+		在
+		<el-input class="user" v-model="position" @input="changeName"/>
+		位置的: 
 		{{food}}
 		<button @click="checkFood(food)">检查食物</button>
 	</div>
@@ -13,20 +16,24 @@ export default {
 				fruit:['苹果','香蕉'],
 				vegetables:['西红柿'],
 				meat:['鸡蛋']
-			}
-
-
+			},
+			position : this.positionName
 		}
 	},
 	props:{
 		food:{
 			default:''
-		}
+		},
+		positionName:{}
+	},
+	model:{
+		props:'user',
+		event:'modelEvent' //每次user改变了 会执行我
 	},
 	methods: {
 		checkFood(food){
-            let data=[]
-            console.log(food);
+			let data=[]
+			console.log(food);
 			// console.log(this.type);
 			Object.keys(this.type).map(v=>{
 				if(this.type[v].indexOf(food)!=-1){
@@ -35,11 +42,20 @@ export default {
 			})
 			data=data?data:'什么都不是'
 			this.$emit('res',data)
+		},
+		changeName(val){
+			//双向绑定改变父组件model的值
+			this.$emit('modelEvent',val)
 		}
 	},
 }
 </script>
 
-<style>
-
+<style lang="less" scoped>
+.user{
+	// background-color: aquamarine;
+	// border-radius: 5px;
+	// padding: 5px;
+	width:100px;
+}
 </style>

+ 13 - 4
src/views/hours/hours.vue

@@ -1,6 +1,10 @@
 <template>
 	<div>
-		<h1>我的屋子</h1>今天我去
+		<h1>我的屋子</h1>
+		在 
+		{{positionName}}
+		<!-- <el-input v-model="positionName"  size="mini" class="input"/> -->
+		今天我去
 		<router-link to="/food">买菜</router-link>,买了:
 		<span v-for="(food,index) in foods" :key="index" class="grid">
 			<span class="food">{{food.name}}</span>
@@ -12,7 +16,7 @@
 
 		<hr />
 		<div v-for="(food,index) in foods" :key="index+'1'">
-			<check :food="food.name" :ref="'check'+food" @res="res" />
+			<check :food="food.name" :ref="'check'+food" @res="res" v-model="positionName" :positionName="positionName"/>
 		</div>
 
 	</div>
@@ -27,7 +31,8 @@ export default {
 	data() {
 		return {
 			foods: {},
-			money: 0
+			money: 0,
+			positionName:'温州'
 		};
 	},
 	created() {
@@ -43,7 +48,7 @@ export default {
 		...mapGetters(["gettersBill"]),
 		res(data) {
 			alert(data);
-		},
+		}
 	}
 };
 </script>
@@ -64,4 +69,8 @@ export default {
 		color: red;
 	}
 }
+.input{
+	width: 100px;
+	
+}
 </style>

+ 15 - 0
src/views/report/report.vue

@@ -0,0 +1,15 @@
+<template>
+  <div>
+      <router-view></router-view>
+  </div>
+</template>
+
+<script>
+export default {
+    name:'report'
+}
+</script>
+
+<style>
+
+</style>

+ 81 - 81
src/views/report/reportDetail/reportDetail.vue

@@ -1,100 +1,100 @@
 <template>
-	<div >
-		<el-row :gutter="20">
-			<el-col :span="4">标题:</el-col>
-			<el-col :span="20">
-				<el-input v-model="report.title"></el-input>
-			</el-col>
-		</el-row>
-		<el-row :gutter="20">
-			<el-col :span="4">内容:</el-col>
-			<el-col :span="20">
-				<el-input v-model="report.content"></el-input>
-			</el-col>
-		</el-row>
-		<el-row>
-			<el-button v-if="isCreate" @click="create" type="primary">创建</el-button>
-			<el-button v-else @click="change" type="primary">编辑</el-button>
-		</el-row>
-	</div>
+  <div>
+    <el-row :gutter="20">
+      <el-col :span="4">标题:</el-col>
+      <el-col :span="20">
+        <el-input v-model="report.title"></el-input>
+      </el-col>
+    </el-row>
+    <el-row :gutter="20">
+      <el-col :span="4">内容:</el-col>
+      <el-col :span="20">
+        <el-input v-model="report.content"></el-input>
+      </el-col>
+    </el-row>
+    <el-row>
+      <el-button v-if="isCreate" @click="create" type="primary">创建</el-button>
+      <el-button v-else @click="change" type="primary">编辑</el-button>
+    </el-row>
+  </div>
 </template>
 
 <script>
-import { addReport,getReport,editReport } from "@/Api/user";
+import { addReport, getReport, editReport } from "@/Api/user";
 export default {
-	name: "detail",
-	created() {
-		console.log("得到传参", this.$route);
-		if (this.$route.params.id !== undefined) {
-			let id = this.$route.params.id;
-			this.report.id = id
-			getReport(id).then(res=>{
-				console.log(res.dat);
-				let data = res.data
-				this.report.title = data.title
-        this.report.content = data.content
-			})
-		}
-	},
-	data() {
-		return {
-			report: {
-				id:'',
-				title: "",
-				content: ""
-			},
-			tempData: []
-		};
-	},
-	computed: {
-		isCreate() {
-			return this.$route.params.id === undefined;
-		}
-	},
-	methods: {
-		create() {
-			let data = {
-				title: this.report.title,
-				content: this.report.content,
-				creat_by: 1
-			};
-			addReport(data).then(res => {
-				console.log(123123, res);
-				if (res) {
-					this.$message({
-						message: "恭喜你,插入成功",
+  name: "detail",
+  created() {
+    console.log("得到传参", this.$route);
+    if (this.$route.params.id !== undefined) {
+      let id = this.$route.params.id;
+      this.report.id = id;
+      getReport(id).then(res => {
+        console.log(res.dat);
+        let data = res.data;
+        this.report.title = data.title;
+        this.report.content = data.content;
+      });
+    }
+  },
+  data() {
+    return {
+      report: {
+        id: "",
+        title: "",
+        content: ""
+      },
+      tempData: []
+    };
+  },
+  computed: {
+    isCreate() {
+      return this.$route.params.id === undefined;
+    }
+  },
+  methods: {
+    create() {
+      let data = {
+        title: this.report.title,
+        content: this.report.content,
+        creat_by: 1
+      };
+      addReport(data).then(res => {
+        console.log(123123, res);
+        if (res) {
+          this.$message({
+            message: "恭喜你,插入成功",
             type: "success",
-            duration:1000
-					});
-					this.$router.push({
-						path: "/report/list"
-					});
-				}
-			});
-		},
-		change() {
-			editReport(this.report).then(res=>{
+            duration: 1000
+          });
+          this.$router.push({
+            path: "/report/list"
+          });
+        }
+      });
+    },
+    change() {
+      editReport(this.report).then(res => {
         console.log(res);
         this.$message({
-          type:"success",
-          message:"修改成功",
-          duration:1000
-        })
-			})
-		}
-	}
+          type: "success",
+          message: "修改成功",
+          duration: 1000
+        });
+      });
+    }
+  }
 };
 </script>
 
 <style lang="less" scoped>
 .el-row {
-	margin-bottom: 5px;
+  margin-bottom: 5px;
 }
 .el-col {
-	text-align: right;
-	line-height: 40px;
+  text-align: right;
+  line-height: 40px;
 }
 .el-button {
-	float: right;
+  float: right;
 }
 </style>

+ 226 - 0
src/views/tree/tree.vue

@@ -0,0 +1,226 @@
+<template>
+  <div>
+    <!-- 
+      拖拽
+      draggable
+      @node-drop="handleDrop"
+      @node-drag-enter="handleDragEnter"
+      :allow-drop="allowDrop"
+    -->
+    <el-tree
+      :props="props"
+      :load="loadNode"
+      node-key="id"
+      lazy
+      show-checkbox
+      :expand-on-click-node="false"
+      ref="tree"
+      v-loading="loading"
+    >
+      <template slot-scope="{data,node}">
+        <div>
+          {{data.name}}
+          <span class="handle">
+            <el-button type="text" size="mini" @click="addNode(data,node)">新增</el-button>
+            <el-button v-if="data.id!=-1" type="text" size="mini" @click="delNode(data,node)">删除</el-button>
+            <el-button v-if="data.id!=-1" type="text" size="mini" @click="putNode(data,node)">更新</el-button>
+          </span>
+        </div>
+      </template>
+    </el-tree>
+  </div>
+</template>
+
+<script>
+import { getNode, addNode, delNode, putNode } from "@/Api/tree";
+export default {
+  name: "tree",
+  data() {
+    return {
+      props: {
+        label: "name",
+        children: "zones",
+        isLeaf: (data, node) => {
+          // console.log("isLeaf",data,node);
+          //算出是否是叶子 true是叶子,false不是🍃
+          if (data.id != -1) {
+            if (!data.hasOwnProperty("rght")) return true; //如果没有rght属性 则就是叶子节点
+            return parseInt((data.rght - data.lft - 1) / 2) == 0;
+          }
+          return false;
+        }
+      },
+      loading: false
+    };
+  },
+  methods: {
+    //加载节点
+    loadNode(node, resolve) {
+      // console.log(node);
+      if (node.level === 0) {
+        let data = [
+          {
+            name: "树容器",
+            id: -1
+          }
+        ];
+        return resolve(data);
+      } else if (node.level === 1) {
+        getNode().then(res => {
+          // console.log(res.data);
+          // res.data.leaf = false
+          return resolve(res.data);
+        });
+      } else {
+        // console.log(node);
+        let parent_id = node.data.id;
+        let data = { parent_id };
+        getNode(data)
+          .then(res => {
+            // console.log(res);
+            resolve(res.data);
+          })
+          .catch(e => {
+            // console.log(e);
+            resolve({});
+          });
+      }
+    },
+    //添加节点
+    addNode(data, node) {
+      console.log(data, node);
+      this.$prompt("请输入名字", "插入节点", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消"
+      })
+        .then(({ value }) => {
+          console.log(data);
+          let rep =
+            data.id == -1
+              ? { name: value }
+              : { name: value, parent_id: data.id };
+          this.loading = true;
+          addNode(rep).then(res => {
+            let childrenNode = res.data.node;
+            let msg = res.data.message;
+            this.$refs.tree.append(childrenNode, node); //插入节点
+            this.$message({
+              type: "success",
+              message: msg
+            });
+            console.log("插入成功", res);
+            this.loading = false;
+          });
+        })
+        .catch(e => {
+          console.log(e);
+        });
+    },
+    //删除节点
+    delNode(data, node) {
+      console.log(data, node);
+      if (data.id == -1) {
+        this.$message({
+          type: "warring",
+          message: "顶级容器不能删除"
+        });
+        return;
+      }
+      this.loading = true;
+      delNode({ id: data.id })
+        .then(res => {
+          console.log("删除节点", res);
+          this.$refs.tree.remove(node);
+          this.loading = false;
+        })
+        .catch(e => {
+          console.log(e);
+        });
+    },
+    //更新节点 更新名字
+    putNode(data, node) {
+      this.$prompt("请输入名字", "修改节点", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消"
+      })
+        .then(({ value }) => {
+          this.loading = true;
+          let rep = {
+            id: data.id,
+            name: value
+          };
+          putNode(rep).then(res => {
+            console.log("更新成功");
+            console.log(data.id,data);
+            data.name = value
+            this.$refs.tree.updateKeyChildren(data.id,data)
+            this.$forceUpdate()
+            this.loading = false;
+          }).catch(e=>{
+            this.loading = false
+            console.log(e);
+          })
+        })
+        .catch(e => {
+          this.loading = false
+          console.log("取消修改",e);
+        });
+    },
+
+    // 以下未使用 ✳
+    //拖拽 更新节点
+    handleDrop(draggingNode, dropNode, dropType, ev) {
+      console.log(draggingNode.data, dropNode.data);
+      let from = draggingNode.data; //拖拽的节点
+      let to = dropNode.data; //到达某个点
+      //更新 某个节点 与 另一个节点 在 同一个父级下
+      let data = {
+        id: from.id,
+        name: from.name,
+        parent_id: to.parent_id
+      };
+      this.loading = true;
+      putNode(data).then(res => {
+        this.loading = false;
+        console.log(res);
+      });
+    },
+    //拖拽 进入
+    handleDragEnter(draggingNode, dropNode, ev) {
+      console.log("tree drag enter: ", dropNode);
+      let from = draggingNode.data; //拖拽的节点
+      let to = dropNode.data; //到达某个点
+      let data = {
+        id: from.id,
+        name: from.name,
+        parent_id: to.id
+      };
+      this.loading = true;
+      putNode(data).then(res => {
+        //拖拽进入成功
+        console.log(res);
+        this.loading = false;
+      });
+    },
+    //拖拽
+    allowDrop(draggingNode, dropNode, type) {
+      // console.log(dropNode);
+
+      if (dropNode.isLeaf) {
+        return false;
+      } else {
+        // console.log(dropNode);
+        return true;
+      }
+    }
+  }
+};
+</script>
+
+<style lang="less">
+.handle {
+  // background-color: darksalmon;
+  // padding: 0px 3px;
+  margin-left: 30px;
+}
+</style>

+ 4 - 4
vue.config.js

@@ -16,12 +16,12 @@ module.exports = {
     },
     devServer: {
         proxy: {
-            '/work': {
+            [process.env.VUE_APP_BASE_API]: {
                 target: "http://127.0.0.1:8000",
                 changeOrigin: true,
-                pathRewrite: {
-                    '^/work': '/work' //重定向
-                }
+                // pathRewrite: {
+                //     `^${process.env.VUE_APP_BASE_API}`: '/work' //重定向
+                // }
             }
         }
     }