本文最后更新于 2025年5月18日 下午
flask+vue前后端分离学习
预期效果:使用flask+vue实现前后端分离
零基础速学
一周完成小组任务
Ok just do it
最终:实际5天多一点完成,感谢大佬发出来的建议CA的web端的参考
由于vue中有大量html语言,可能导致阅读混乱,有想要这个记录的md文件的可以私我
目前参考文章
neverlesslai/SimpleCAWEB: 简易CA系统的WEB端
大型实验周的作品,实现了一个包含X.509证书的申请,审核,撤销等功能的简单CA系统
【2024最新版】3小时学会Vue3,小白零基础视频教程,web前端快速入门实战课程_哔哩哔哩_bilibili (基本刷完)
60分钟真正搞明白Python和Flask(快速入门与进阶,不能错过的高质量教程)#python#flask_哔哩哔哩_bilibili (看到了第27min)
【Flask】快速入门后台写接口【API】【Python3】【无前端】【json格式】_哔哩哔哩_bilibili (看完了)
武沛齐老师教你 最新Flask+Vue的前后端开发快速教程(含源码)_哔哩哔哩_bilibili (没怎么看)
ES6 从入门到精通 - 邓瑞编程 (这两个是跟b站的vue3相关的)
邓瑞编程
【完结】Vue3零基础快速上手(2024最新版)_哔哩哔哩_bilibili
基本全是速通,主打一个速度:dog:
[TOC]
vue篇 npm run d
1. 基础篇与绑定
“.#mount”指向了挂载的标签
{{}}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 来取值(这个符号单独放出来居然会让我的推送报错。。)vue  - 用 const {函数名} = 包名来指定包,代替了去掉了前面每次的包的前缀Vue. - 返回值来作为参数 - 注意标签要用div来包裹,否则无法正常显示。 ```vue <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > <script src ="vue.global.js" > </script > </head > <body > <div id ="app" > {{ msg }} <h2 > {{ web.name }} </h2 > <h2 > {{ web.version }} </h2 > </div > <script > const {createApp,reactive} = Vue createApp ({ setup ( ){ const web =reactive ({ name :'vue3' , version :'3.0.0' , url :"xyyr" }) return { msg :'hello world' , web } } }).mount ('#app' ) </script > </body > </html >
2. vue3模块化开发 类似的代码,只不过包引入方式不太一样(具体有啥区别还不知道)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> {{ msg }} <h2>{{ web.name }}</h2> <h2>{{ web.version }}</h2> </div> <script type = "module"> import {createApp,reactive} from './vue.esm-browser.js' createApp({ setup(){ const web =reactive({ name:'vue3', version:'3.0.0', url:"xyyr" }) return{ msg:'hello world', web } } }).mount('#app') </script> </body> </html>
3. ref和reactive区别
ref用于存储单个基本类型的数据,如字符、字符串、数组。需要用.value来引用更改
reactive用于存储负责数据类型,如对象或数组等。直接可以进行更改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> {{ msg }} <h2>{{ web.name }}</h2> <h2>{{ web.version }}</h2> <h2>{{ number }}</h2> <h2>{{ lists[2] }}</h2> <h2>{{ lists }}</h2> </div> <script type = "module"> import {createApp,reactive,ref} from './vue.esm-browser.js' createApp({ setup(){ const number = ref(0) number.value = 20 const lists = ref([1,2,3,4,5]) lists.value.push(6) const web =reactive({ name:'vue3', version:'3.0.0', url:"xyyr" }) web.url = 'https://www.xyyr.com/' return{ msg:'hello world', web, number, lists } } }).mount('#app') </script> </body> </html>
4. 绑定事件与案件修饰符v-on: == @
()=>{} 是 JavaScript 中的一个简化语法,用来定义一个匿名函数。
如果要引用到函数,也要作为返回值返回
v-on:click=”函数名”,pycharm里会报错,但是正常运行(v-on:可以用@来替代)
br换行符,hr分割符
可以根据自己的需求定义自己的快捷键,up代表按键弹起时,down代表按下时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 <div id="app"> {{ msg }} <h2>{{ web.user }}</h2> <h2>{{ web.name }}</h2> <h2>{{ web.url }}</h2> <h2>{{ number }}</h2> <h2>{{ lists[2] }}</h2> <h2>{{ lists }}</h2> <button v-on:click="edit">修改</button><br> <button @click="edit">修改-简写形式</button><hr> 回车 <input type="text" @keyup.enter="add(40,60)"> 空格 <input type="text" @keyup.space="add(30,50)"></input> TAB <input type="text" @keydown.tab="add(20,30)"></input> w <input type="test" @keyup.w="add(10,20)"></input> <!--组合快捷键--> ctrl+enter <input type="text" @keyup.enter.ctrl="add(40,60)"> </div> <script type = "module"> import {createApp,reactive,ref} from './vue.esm-browser.js' createApp({ setup(){ const number = ref(0) number.value = 20 const lists = ref([1,2,3,4,5]) lists.value.push(6) const web =reactive({ name:'vue3', version:'3.0.0', url:"xyyr", user:0 }) web.url = 'https://www.xyyr.com/' const edit = ()=>{ web.url = 'https://www.xyyr-new.com/' } const add =(a,b)=>{ web.user += a+b } return{ msg:'hello world', web, number, lists, edit, add } } }).mount('#app') </script> </body> </html>
5. 显示和隐藏v-show(频繁用v-show,不频繁可以用v-if) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> {{ web.show }}<hr> <p v-show="web.show">这里是一段显示的文本</p> <button @click="toggle">切换</button> </div> <script type = "module"> import {createApp,reactive,ref} from './vue.esm-browser.js' createApp({ setup(){ const web = reactive({ show:true }) const toggle=()=>{ web.show = !web.show } return{ web, toggle } } }).mount('#app') </script> </body> </html>
6.v-if条件判断 1 2 3 <p v-if="web.user <1000">新网站</p> <p v-else-if = "web.user >=1000 &&web.user < 2000">优秀网站</p> <p v-else >资深网站</p>
7.动态属性绑定v-bind:vulue == :value
v-bind:vulue == :value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .textColor{ color:red; } </style> </head> <body> <div id="app"> 属性绑定<input type="text" :value="web.show"> img <img :src="web.img"> <b :Class="{textColor:web.fontstatus}">xyyr</b> </div> <script type = "module"> import {createApp,reactive,ref} from './vue.esm-browser.js' createApp({ setup(){ const web = reactive({ show:true, img:"xyyr.jpg", fontstatus:true, user:500 }) const toggle=()=>{ web.show = !web.show } const add=()=>{ web.user += 500 } return{ web, toggle, add } } }).mount('#app') </script> </body> </html>
8. 遍历数组或对象v-for
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> <ul> <li v-for="value in data.number" > {{value}}</li> <li v-for="(value,index) in data.number" > index => {{index}} : value => {{value}} </li> <li v-for="(value,key,index) in data.user" > index => {{index}} : key => {{key}} : value => {{value}} </li> <hr> <template v-for="(value,key,index) in data.user"> <li v-if ="index ==1 " > index => {{index}} : key => {{key}} : value => {{value}} </li> </template> <ul> <li v-for="(value,index) in data.teacher" :title="value.name" :key="value.id"> index => {{index}} : value.id => {{value.id}} : value.name => {{value.name}} </li> </ul> </ul> </div> <script type = "module"> import {createApp,reactive,ref} from './vue.esm-browser.js' createApp({ setup(){ const web =reactive({ name:'vue3', version:'3.0.0', url:"xyyr", user:0 }) const data = reactive({ number:['一','二','三'],//数组 user:{ name:'小明', age:18, gender:'男' }, teacher:[ {id:100,name:"小红"}, {id:101,name:"小蓝"}, {id:102,name:"小绿"} ] }) return{ web, data } } }).mount('#app') </script> </body> </html>
9. 双向数据绑定 v-model(这是实时渲染的)
v-on是单项非动态绑定,数据更新但是视图不更新。
v-mode双向数据绑定。
本篇有单选框,复选框,下拉框以及对应的数据绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> <h3>文本框{{ data.text }}</h3> <h3>单选框{{ data.radio }}</h3> <h3>复选框{{ data.checkbox }}</h3> <h3>下拉框{{ data.select }}</h3> 单向数据绑定<input type="text" :value="data.text"> 双向数据绑定<input type="text" v-model="data.text"> <!--单选框--> <input type ="radio" v-model="data.radio" value="1">写作 <input type ="radio" v-model="data.radio" value="2">阅读 <!--复选框--> <input type ="checkbox" v-model="data.checkbox" value="1">Vue <input type ="checkbox" v-model="data.checkbox" value="2">React <input type ="checkbox" v-model="data.checkbox" value="3">Angular <hr> <input tyoe="checkbox" v-model="data.remember">记住密码 <!--下拉框--> <select v-model="data.select"> <option value="">请选择</option> <option value="1">Vue</option> <option value="2">React</option> <option value="3">Angular</option> </select> </div> <script type = "module"> import {createApp,reactive} from './vue.esm-browser.js' createApp({ setup(){ const data =reactive({ text: "hello.com",//文本框 radio:false,//单选框 checkbox:[],//复选框 remember:"",//记住我 select:"",//下拉框 textarea:"",//文本域 file:"",//文件上传 button:"",//按钮 submit:"",//提交按钮 reset:"",//重置按钮 range:"",//范围输入框 }) return{ data } } }).mount('#app') </script> </body> </html>
10. v-model修饰符
感觉这次用不到,先截了个图,没跟着敲
11. v-text 与v-html(text相当于直接的文本,html会解析对应的标签属性,如大小、颜色等)
12. 计算属性computed(计算属性有缓存,只有发生数据改变时才会调用一次)
13. 侦听器watch(手动监听)
conselve.log可以在控制台打印值(可以根据选择来做判断)
这里数组与对象都是引用传递,修改值后会真修改。
还没手敲,复制的作者的。(别问我为什么之前是截图)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 html <div id="app"> 爱好 <select v-model="hobby"> <option value="">请选择</option> <option value="1">写作</option> <option value="2">画画</option> <option value="3">运动</option> </select> <hr> 年 <select v-model="date.year"> <option value="">请选择</option> <option value="2023">2023</option> <option value="2024">2024</option> <option value="2025">2025</option> </select> 月 <select v-model="date.month"> <option value="">请选择</option> <option value="10">10</option> <option value="11">11</option> <option value="12">12</option> </select> </div> js <script type="module"> import { createApp, ref, reactive, watch } from './vue.esm-browser.js' createApp({ setup() { const hobby = ref("") //爱好 const date = reactive({ //日期 year: "2023", month: "10" }) //监听 hobby watch(hobby, (newValue, oldValue) => { console.log("oldValue", oldValue, "newValue", newValue) if (newValue == "2") { console.log("画画") } }) //监听 date watch(date, (newValue, oldValue) => { /* JS中对象和数组是通过引用传递的, 而不是通过值传递 当修改对象或数组的值时, 实际上修改的是对象或数组的引用, 而不是创建一个新的对象或数组 所以,如果修改了对象或数组的值,那么打印出来的结果则是修改后的值 */ console.log("oldValue", oldValue, "newValue", newValue) if (newValue.year == "2025") { console.log("2025") } if (newValue.month == "11") { console.log("11") } }) //监听 date 中的某个属性 year watch(() => date.year, (newValue, oldValue) => { console.log("oldValue", oldValue, "newValue", newValue) if (date.year == "2024") { console.log("2024") } }) return { hobby, date } } }).mount("#app") </script>
14. 自动监听watcheffect 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 html <div id="app"> 爱好 <select v-model="hobby"> <option value="">请选择</option> <option value="1">写作</option> <option value="2">画画</option> <option value="3">运动</option> </select> <hr> 年 <select v-model="date.year"> <option value="">请选择</option> <option value="2023">2023</option> <option value="2024">2024</option> <option value="2025">2025</option> </select> 月 <select v-model="date.month"> <option value="">请选择</option> <option value="10">10</option> <option value="11">11</option> <option value="12">12</option> </select> </div> js <script type="module"> /* watch需要显式指定要监听的属性, 并且只有当监听的属性发生变化时才会执行 若需要更精细地控制或需要获取到原值, 需要使用watch */ import { createApp, ref, reactive, watchEffect } from './vue.esm-browser.js' createApp({ setup() { const hobby = ref("") //爱好 const date = reactive({ //日期 year: "2023", month: "10" }) //自动监听 watchEffect(() => { console.log("------ 监听开始") if (hobby.value == "2") { console.log("画画") } if (date.year == "2025") { console.log("2025") } if (date.month == "11") { console.log("11") } console.log("------ 监听结束") }) return { hobby, date } } }).mount("#app") </script>
15.图片轮播 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h3>{{ number }}</h3> <!-- <img src="/images/1.jpg" style="width: 300px;"> --> <img :src=`/images/${number}.jpg` style="width: 300px;"> <hr> <button @click="prev">上一张</button> <button @click="next">下一张</button> <ul> <li v-for="(value, index) in 4"> <a href="#" @click="jump(value)">{{ value }}</a> </li> </ul> </div> <script type="module"> import { createApp, ref } from './vue.esm-browser.js' createApp({ setup() { const number = ref(1) //上一张 const prev = () => { number.value-- if (number.value == 0) { number.value = 4 } } //下一张 const next = () => { number.value++ if (number.value == 5) { number.value = 1 } } //跳转 const jump = (value) => { number.value = value } return { number, prev, next, jump } } }).mount("#app") </script> </body> </html>
16.记事本案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <input type="text" v-model="data.content"> <button @click="add">添加</button> <hr> <ul> <li v-for="(value, index) in data.list"> {{ value }} <button @click="del(index)">删除</button> </li> </ul> 记录数 {{ data.list.length }} <br> <button @click="clear">清空</button> </div> <script type="module"> import { createApp, reactive } from './vue.esm-browser.js' createApp({ setup() { const data = reactive({ content: "", list: ["邓瑞编程", "dengruicode.com"], }) //添加 const add = () => { if (data.content == "") { alert("请填写内容") return } data.list.push(data.content) //push 向数组末尾添加一个或多个元素 data.content = "" //清空文本框 } //删除 const del = (index) => { data.list.splice(index, 1) //splice(要删除元素的索引位置, 要删除的元素数量) } //清空 const clear = () => { data.list = [] } return { data, add, del, clear } } }).mount("#app") </script> </body> </html>
17.购物车案例及优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <style > table { width : 600px ; color : #8f8e8e ; text-align : center; border-collapse : collapse; } table thead { background : #F5F5F5 ; } table tr { height : 30px ; line-height : 30px ; border : 1px solid #ececec ; } </style > </head > <body > <div id ="app" > <table > <thead > <tr > <td > <input type ="checkbox" v-model ="data.selected" @change ="selectAll" /> </td > <td > 商品</td > <td > 单价</td > <td > 库存</td > <td colspan ="2" > 操作</td > </tr > </thead > <tbody > <tr v-for ="(value, index) in data.list" > <td > <input type ="checkbox" v-model ="data.checkboxList" :value ="value" @change ="checkSelect" /> </td > <td > {{ value.name }}</td > <td > {{ value.price }}</td > <td > {{ value.stock }}</td > <td > <button @click ="sub(value)" > -</button > {{ value.number }} <button @click ="add(value)" > +</button > </td > <td > <button @click ="del(index,value.id)" > 删除</button > </td > </tr > </tbody > <tfoot > <tr > <td > 总价 {{ totalPrice() }}</td > </tr > </tfoot > </table > </div > <script type ="module" > import { createApp, reactive } from './vue.esm-browser.js' createApp ({ setup ( ) { const data = reactive ({ selected : false , checkboxList : [], list : [{ id : 1 , name : "铅笔" , price : 10 , number : 1 , stock : 3 }, { id : 2 , name : "鼠标" , price : 20 , number : 2 , stock : 5 }, { id : 3 , name : "键盘" , price : 30 , number : 1 , stock : 6 }], }) const sub = (value ) => { value.number -- if (value.number <= 1 ) { value.number = 1 } } const add = (value ) => { value.number ++ if (value.number >= value.stock ) { value.number = value.stock } } const del = (index, id ) => { data.list .splice (index, 1 ) let newArr = data.checkboxList .filter ((value, index ) => { return value.id != id }) data.checkboxList = newArr checkSelect () } const totalPrice = ( ) => { let total = 0 for (let i = 0 ; i < data.checkboxList .length ; i++) { total += data.checkboxList [i].price * data.checkboxList [i].number } return total } const selectAll = ( ) => { if (data.selected ) { data.checkboxList = data.list } else { data.checkboxList = [] } } const checkSelect = ( ) => { if (data.checkboxList .length != data.list .length || data.list .length == 0 ) { data.selected = false } else { data.selected = true } } return { data, sub, add, del, totalPrice, selectAll, checkSelect } } }).mount ("#app" ) </script > </body > </html >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <style > table { width : 600px ; color : #8f8e8e ; text-align : center; border-collapse : collapse; } table thead { background : #F5F5F5 ; } table tr { height : 30px ; line-height : 30px ; border : 1px solid #ececec ; } </style > </head > <body > <div id ="app" > <table > <thead > <tr > <td > <input type ="checkbox" v-model ="data.selected" /> </td > <td > 商品</td > <td > 单价</td > <td > 库存</td > <td colspan ="2" > 操作</td > </tr > </thead > <tbody > <tr v-for ="(value, index) in data.list" > <td > <input type ="checkbox" v-model ="data.checkboxList" :value ="value" /> </td > <td > {{ value.name }}</td > <td > {{ value.price }}</td > <td > {{ value.stock }}</td > <td > <button @click ="sub(value)" > -</button > {{ value.number }} <button @click ="add(value)" > +</button > </td > <td > <button @click ="del(index,value.id)" > 删除</button > </td > </tr > </tbody > <tfoot > <tr > <td > 总价 {{ totalPrice }}</td > </tr > </tfoot > </table > </div > <script type ="module" > import { createApp, reactive, watch, computed } from './vue.esm-browser.js' createApp ({ setup ( ) { const data = reactive ({ selected : false , checkboxList : [], list : [{ id : 1 , name : "铅笔" , price : 10 , number : 1 , stock : 3 }, { id : 2 , name : "鼠标" , price : 20 , number : 2 , stock : 5 }, { id : 3 , name : "键盘" , price : 30 , number : 1 , stock : 6 }], }) const sub = (value ) => { value.number -- if (value.number <= 1 ) { value.number = 1 } } const add = (value ) => { value.number ++ if (value.number >= value.stock ) { value.number = value.stock } } const del = (index, id ) => { data.list .splice (index, 1 ) let newArr = data.checkboxList .filter ((value, index ) => { return value.id != id }) data.checkboxList = newArr } const totalPrice = computed (() => { return data.checkboxList .reduce ((total, item ) => total + item.price * item.number , 0 ) }) let flag = true watch (() => data.selected , (newValue, oldValue ) => { if (newValue) { data.checkboxList = data.list } else { if (flag) { data.checkboxList = [] } } }) watch (() => data.checkboxList , (newValue, oldValue ) => { console .log ("newValue:" , newValue, "oldValue:" , oldValue) console .log (newValue.length ) if (newValue.length == data.list .length && data.list .length != 0 ) { data.selected = true flag = true } else { data.selected = false flag = false } }) return { data, sub, add, del, totalPrice, } } }).mount ("#app" ) </script > </body > </html >
18.axios实现文章搜索 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="js/axios.min.js"></script> </head> <body> <div id="app"> <select v-model="data.type"> <option value="0">请选择</option> <option value="1">ID</option> <option value="2">标题</option> </select> <input type="text" v-model="data.content"> <button @click="search">搜索</button> <ul> <li v-for="(value, index) in data.list"> {{ value }} </li> </ul> </div> <script type="module"> import { createApp, reactive } from './js/vue.esm-browser.js' createApp({ setup() { const data = reactive({ type: "0", //搜索类型 content: "", //搜索内容 list: [], }) //搜索 const search = () => { //console.log(data.content) data.list = [] //清空 if (data.type == "1") { let id = data.content //参数 //get请求 axios.get(`http://127.0.0.1/article/get/id/${id}`).then(response => { console.log("get.data:", response.data) if(response.data.status == "success"){ if(response.data.data){ data.list.push(response.data.data) //push 向数组末尾添加一个或多个元素 } } }).catch(error => { console.log("get.error:", error) }) } else if (data.type == "2") { //参数 let param = { title: data.content } //post请求 [axios post的默认请求头是 application/json] axios.post('http://127.0.0.1/article/postJson/search', param).then(response => { console.log("postJson.data:", response.data) if(response.data.status == "success"){ for(let i=0; i<response.data.data.length; i++){ data.list.push(response.data.data[i]) //push 向数组末尾添加一个或多个元素 } } }).catch(error => { console.log("postJson.error:", error) }) } } return { data, search } } }).mount("#app") </script> </body> </html>
flask篇 基础知识
路由、变量及url规则,模板,静态文件
重定向和错误
requirements.txt
flask flask-sqlalchemy flask_wtf wtforms[email] flask-sijax
1 pip3 install -r requirements.txt
第一个例子,app.py(基础启动) 1 2 3 4 5 6 7 8 9 10 from flask import Flask app = Flask(__name__)@app.route('/' ) def index (): return 'Hello, World!' if __name__ == '__main__' : app.run(debug=True )
其他地方调用,名字是app,自己调用,名字是main
关于@以及root的作用(来自gpt的解释):
@的作用
在 Python 中,@ 语法用于装饰器 (decorator),它是一种函数修饰工具。
装饰器的作用是在函数或方法的执行之前或之后,动态地添加功能 。 使用 @decorator_name 的语法等价于手动调用:
1 2 3 4 python 复制代码def index (): return 'Hello, World!' index = decorator_name(index )
在你的代码中,@app.route('/') 是一个装饰器,它的作用是将一个 URL 路径绑定到一个函数上。也就是说,当用户访问特定路径(如/)时,Flask 会调用对应的函数并返回其结果。
route的作用
2.1. app.route('/')
route 是 Flask 提供的一个方法,常与装饰器配合使用。
它的作用是定义路由 ,即指定当访问某个 URL 时,该由哪个函数处理请求。
2.2. 使用说明
/ 是 URL 的路径,表示根路径(主页)。
装饰的函数 index() 是处理请求的视图函数,它返回的内容会作为 HTTP 响应的内容。
例如:
当用户在浏览器中访问 http://127.0.0.1:5000/ 时,Flask 会执行 index() 函数,并将返回的字符串 'Hello, World!' 显示在页面上。
第二个例子,app1.py(变量类型的定义)
通过路由索引,注意,如果在路径中作为参数时,route内变量的形式为要加<>
url标准写法后侧带/,就算访问访问时少加/也能正常访问(少加的话如果访问,会自动补全),反之就不可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from flask import Flask app = Flask(__name__)@app.route('//' ) def index (): return 'Hello, World!' @app.route('/<name>/' ) def name (name ): return 'Hello, %s!' % name@app.route('/ids/<int:id>/' ) def ids (id ): return 'ID: %d' % id if __name__ == '__main__' : app.run(host='127.0.0.1' , port=5000 , debug=True , threaded=True )
第三个例子app2.py、index2.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from flask import Flask, render_template app = Flask(__name__) @app.route('/' ) def index(): t_int = 20 t_str = 'xyyr' t_list = [1,2,3,4,5] t_dict = {'name' :'xyyr' , 'age' :20} return render_template('index2.html' , my_int =t_int, my_str =t_str, my_list =t_list, my_dict =t_dict)if __name__ == '__main__' : app.run (debug =True )
下面时对应的html文件,包括对应的传参。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h2 > 模板</h2 > my_int : {{ my_int }} <br > my_str : {{ my_str }} <br > my_list : {{ my_list }} <br > my_dict : {{ my_dict }} <br > my_bool : {{ my_bool }} </br > <h2 > 列表数据</h2 > {{ my_list [0]}} {{ my_list [1]}} {{ my_list.2 }} <h2 > 字典数据</h2 > {{ my_dict ['name']}} {{ my_dict.ahe }} <h2 > 算数运算</h2 > {{ my_int + my_list.0 }} </body > </html >
第四个例子app3.py flask静态文件
时间紧迫,先截图后补手敲的代码。
第五个例子app4.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from flask import Flask , redirect, url_for app = Flask(__name__)@app.route('/' ) def index (): return 'Hello, World!' @app.route('/admin' ) def hello_admin (): return 'Hello, admin!' @app.route('/guest/<guest>' ) def hello_guest (guest ): return 'Hello, %s as guest!' % guest@app.route('/user/<name>' ) def hello_user (name ): if name == 'admin' : return redirect(url_for('hello_admin' )) else : return redirect(url_for('hello_guest' , guest=name))if __name__ == '__main__' : app.run(debug=True )
第六个例子,app5.py(封装为表格返回) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from flask import Flask , render_template, requestfrom werkzeug.wrappers.response import ResponseStream app = Flask(__name__)@app.route('/' ) def stufent (): return render_template('student.html' )@app.route('/result' ,methods=['POST' ,'GET' ] ) def result (): if request.method == 'POST' : result = request.form return render_template("result.html" , result=result)if __name__ == '__main__' : app.run(debug=True )
7. app6.py(url重定向与错误)
高级知识
cookies及会话
消息闪现
WTF表单
SQLAlchemy及数据库、Sjiax
1.cookie
2.session会话
用flask写json接口
注意写json时,变量里面不要乱加空格,空格也会被当作字典中变量的一部分
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from flask import Flask, request, jsonify app = Flask(__name__)@app.route('/' , methods=["GET" , "POST" ] ) def index (): return 'Hello, World!' @app.route('/test/first' , methods=["POST" ] ) def first_post (): try : my_json = request.get_json() print (my_json) get_name = my_json.get("name" ) get_age = my_json.get("age" ) if not all ([get_name, get_age]): return jsonify(msg="缺少输入的参数" ) return jsonify({"name" : get_name, "age" : get_age}) except Exception as e: print (e) return jsonify(msg="出错了,重新输入" )if __name__ == '__main__' : app.run(debug=True )
实例:
写接口篇 1.初次尝试(就着几个不一样的教程混杂着试) 这里用的是post传参的搜索功能
flask代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @app.route('/article/postJson/search',methods=['POST']) def search(): data = request.get_json() print(data) print(data.get("title")) datas = [{"title":"111","content":"111"},{"title":"222","content":"222"}, {"title":"333","content":"333"}] if data.get("title") == '111': return jsonify({ "data":datas, "msg":"查询成功", "status":"success" }) return jsonify(data)
js代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 <script type="module"> import { createApp, reactive } from './js/vue.esm-browser.js' createApp({ setup() { const data = reactive({ type: "0", //搜索类型 content: "", //搜索内容 list: [], }) //搜索 const search = () => { //console.log(data.content) data.list = [] //清空 if (data.type == "1") { let id = data.content //参数 //get请求 axios.get(`http://127.0.0.1/article/get/id/${id}`).then(response => { console.log("get.data:", response.data) if(response.data.status == "success"){ if(response.data.data){ data.list.push(response.data.data) //push 向数组末尾添加一个或多个元素 } } }).catch(error => { console.log("get.error:", error) }) } else if (data.type == "2") { //参数 let param = { title: data.content } //post请求 [axios post的默认请求头是 application/json] axios.post('http://127.0.0.1:5000/article/postJson/search', param).then(response => { console.log("postJson.data:", response.data) if(response.data.status == "success"){ for(let i=0; i<response.data.data.length; i++){ data.list.push(response.data.data[i]) //push 向数组末尾添加一个或多个元素 } } }).catch(error => { console.log("postJson.error:", error) }) } } return { data, search } } }).mount("#app") </script>
效果:后端用request接收前端传递的数据,前端用axios接收对应的数据。
前端中途遇到过的困难 刷新后定位异常的问题 路由配置的index.ts中,将路由形式改为webhash
前端用户验证问题: 用sessionStorage在登录成功时存储,登出时退出,每个功能调用前先验证会话信息。
涉及用法:
1 2 sessionStorage.setItem("username", username.value) sessionStorage.clear()
后端跨域传输时,cookie传递不过去 在配置项改为https,将secure标志改为false,用自签名代理对接,axois使用时也要设置对应的携带cookie参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 server: { port: 5173, host: "0.0.0.0", https: { key: fs.readFileSync("./https/localhost.key"), cert: fs.readFileSync("./https/localhost.crt"), }, proxy: { '/api': { target: 'https://10.33.25.216:8080', changeOrigin: true, secure:false, }, },
el-card长度调整问题 有时候100%不生效,要用100px。
el组件的格式修饰问题 用::v-deep才能深度修饰
1 2 3 4 ::v-deep .upload-demo .el-upload-dragger { height: 200px; /* 调整高度 */ width: 100%; /* 调整宽度 */ }
el-upload自定义上的问题 用:http-request重定义上传逻辑,注:这里会自行传输一个 FormData类型的数据,ts中需要对它进行定义。(就算实际是https,也要用这个名字)
部分代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 interface UploadFileParam { file: File; // 上传的文件对象 onSuccess: (response: any) => void; // 上传成功的回调 onError: (error: any) => void; // 上传失败的回调 [key: string]: any; // 可以有其他可选属性 } function upload_csr (param: UploadFileParam) { const formData = new FormData(); formData.append('csr', param.file); // 上传文件字段名为 'csr' axios.post('/api/ca/read_csr', formData, { headers: { 'Content-Type': 'multipart/form-data', }, })
el组件输入时,需要失去焦点才能输入一次信息的问题 el-form初始化时,参考教程使用了ref,可能是vue2与vue3对信息适配的不同,这里要改为reactive。
el组件上传完毕后清空上传列表 用ref对象接收,然后在upload组件上挂上,最后调用clearFiles方法清除。(注意,这里vscode会提示报错,实际可以运行并达到效果)
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 <el-upload class="upload-demo" drag action="" :http-request="upload_cer" accept=".cer" name="cer" ref = "file_list" :auto-upload="true" :limit= "1" > const file_list = ref([]) file_list.value.clearFiles()
证书卡片吊销后视图不立刻更新 在调用吊销函数时,直接将存储这些信息的数组中删掉该对象,vue视图会重新渲染。
由于 certs 是响应式的,重新赋值 certs.value 会触发视图更新,导致 DOM 中移除已吊销证书的卡片。
1 certs.value = certs.value.filter(cert => cert.id !== cert_id)
功能展示
登录
注册
csr信息填写
基础信息的填写,可以选择上传csr文件,也可以直接填写,只有域名是必填项,不填时会出现警告;国家要求两位英文字母,电子邮件要求是邮件格式,否则会有提示。
公钥上传,可以点击自动生成,右侧会自动显示生成的公钥,也可以将自己的公钥复制过去,点手动提交
提交完成后,点击下载证书,可以直接下载文件
查询CA证书,查询自己所有生效的CA,可以选择下载,也可以点击吊销使证书失效。
验证CA证书,将文件上传后,会弹出提示消息
退出登录 :点击退出登录后直接退出
前端有本地会话验证用户信息,后端有session验证信息,二者分离验证,保证只有登录的用户才能正常使用,如果直接通过页面跳转进来,会弹出提示消息,点击后返回到登录页面