vue2、flask使用详解

发布于:2022-08-10 ⋅ 阅读:(554) ⋅ 点赞:(0)

一、vue的安装

1.安装node.js

下载地址: http://nodejs.cn/download/ 安装的时候一直下一步直到结束

确认是否安装成功:

  • 在cmd中运行node -v命令,查看是否能够输出版本号
  • 在cmd中运行npm -v命令,查看是否能够输出版本号

2.安装node.js淘宝镜像加速器(cnpm)

npm install cnpm -g

如果安装过慢使用 npm install -g cnpm --registry=https://registry.npm.taobao.org

3.安装vue-cli

cnpm instal1 vue-cli-g

4.安装webpack

npm install webpack -g

npm install webpack-cli -g

确认是否安装成功:

  • webpack -v
  • webpack-cli -v

参考链接https://blog.csdn.net/qq_45408390/article/details/118151297

二、使用

1.正常使用

vue ui创建项目,package manager选择npm,其他均为默认配置

参考链接https://learning.dcloud.io/#/?vid=14

2.element-ui使用(包含axios部分内容)

main.js中配置如下:

import ElementUI from 'element-ui' //element-ui
import 'element-ui/lib/theme-chalk/index.css' //element-ui

Vue.use(ElementUI) //element-ui

在vue组建中可以直接使用element ui

使用详解链接为https://element.eleme.io/#/zh-CN/component/installation

①element button使用举例:

<el-button type="primary" @click="submit" round :disabled="dis!=''">提交</el-button>

methods: {
    submit(){
		if(this.percentage < 60) {
			this.$message.error('The data information has not been filled in completely');
		} else {
			this.$router.push('/IndexBuild/InferenceGenerate')
			this.$message({
				message: '数据提交成功',
				type: 'success'
			});
			// this.$emit('getPercentage', 80);
			this.dis='disabled';
			this.$emit('getState', 1);
		}
	},
}

注:

  • this.$message是弹消息,可以在element ui中看到使用方法
  • :disabled=true/false中true/false并非字符串,是直接的true/false,所以可以借助!=或==使用

②element upload使用举例:

vue.config.js中:

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
      // 设置代理
		proxy: {
			"/api": {
				target: "http://127.0.0.1:5000", // 访问数据的计算机域名
				// target: "http://10.101.168.234:5000", // 访问数据的计算机域名
				ws: true, // 是否启用websockets
				changOrigin: true, //开启代理,
				pathRewrite: { // 重写代理规则,/api开头,代理到/
				'^/api': '/'
				// 例:/api/user/login代理到
				// http://localhost:5000/user/login
				}
			}
		}
    }
})

注:

  • devServer中proxy为设置的代理
  • 在build以后这个文件中的api无法在vue中使用,需要替换vue中的api为实际地址

vue组建中:

<el-upload
	class="upload-demo"
	action="/api/file/upload"
	:on-remove="(file, fileList) => {queryEncoderFile.pop({name: file.name});return handleRemove(file)}"
	:on-success="(response, file, fileList) => {queryEncoderFile.push({name: file.name})}"
	:before-upload="(file) => {return beforeUpload(file, 'collection.tsv')}"
	multiple
	:limit="1"
	:on-exceed="handleExceed"
	:file-list="queryEncoderFile"
	:disabled="dis!=''">
	<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
methods: {
	handleRemove(file) {
		this.$axios
			.post(
				"/api/file/remove",
				{
					fileName: file.name,
				},
				{
					headers: {
						token: "666666",
					},
				}
			)
			.then((res) => {
				// if (res.data.code === 0) {
				// 	this.tempResult = res.data.data.id;
				// }
				console.log(res)
			})
			.catch(function (error) {
				console.log(error);
			});
	},
	handleExceed(files, fileList) {
		this.$message.warning(`当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
	},
	beforeUpload(file, fileName){
		if (file.name != fileName) {
			this.$message.error('File name must be '+ fileName);
			return false
		}
	},
	submit(){
		if(this.percentage < 60) {
			this.$message.error('The data information has not been filled in completely');
		} else {
			this.$router.push('/IndexBuild/InferenceGenerate')
			this.$message({
				message: '数据提交成功',
				type: 'success'
			});
			// this.$emit('getPercentage', 80);
			this.dis='disabled';
			this.$emit('getState', 1);
		}
	},
}
.el-upload input{
		display: none !important;;
}

flask中:

from flask import Flask, jsonify, request
import os
# 上传文件
from wtforms import Form, FileField
from flask_wtf.file import FileRequired, FileAllowed
from werkzeug.utils import secure_filename
from werkzeug.datastructures import CombinedMultiDict
# 下载文件
from flask import send_from_directory
# 跨域
from flask_cors import CORS

# 表单提交相关校验
class fileForm(Form):
    file = FileField(validators=[FileRequired(), FileAllowed(['tsv', 'memmap', 'npy', 'png'])])

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False  # 禁止中文转义
CORS(app, supports_credentials=True)

# 删除文件
@app.route("/file/remove",methods=['POST'])
def removeFile():
    print(request.get_json())
    fileName = request.get_json().get('fileName')
    # 文件的路径
    path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'static', fileName)
    print(path)
    os.remove(path)
    return jsonify({
        "code": 200,
        "message": "删除文件成功",
        "fileName": fileName
    })

# 在body的form-data里附带参数
@app.route("/file/upload",methods=['POST'])
def uploadFile():
    # # 保存文件的路径
    # save_path = os.path.join(os.path.abspath(os.path.dirname(__file__)).split('TPMService')[0], 'TPMService/static')
    # # 获取文件
    # attfile = request.files.get('file')
    # attfile.save(os.path.join(save_path, attfile.filename))
    # return jsonify({
    #     "code": 200,
    #     "message": "上传请求成功"
    # })

    # 初始化返回对象
    # resp_success = format.resp_format_success
    # resp_failed = format.resp_format_failed

    file_form = fileForm(CombinedMultiDict([request.form, request.files]))
    if file_form.validate():
        # 获取项目路径+保存文件夹,组成服务保存绝对路径
        save_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'static')
        # 通过表单提交的form-data获取选择上传的文件
        attfile = request.files.get('file')
        # 进行安全名称检查处理
        file_name = secure_filename(attfile.filename)
        # 保存文件文件中
        attfile.save(os.path.join(save_path, file_name))

        # resp_success['data'] = {"fileName": file_name}
        return jsonify({
            "code": 200,
            "message": "上传请求成功",
            "fileName": file_name
        })
    else:
        # resp_failed['message'] = '文件格式不符合预期'
        # return resp_failed
        return jsonify({
            "code": 400,
            "message": "文件格式不符合预期"
        }), 400

注:

  • action为上传地址,文件会自动上传
  • 绑定的fileList不会自动加入和删除文件,需要在on-success和on-remove阶段自己加入和删除,这里的on-success只是加入文件到fileList中,on-remove传给服务器信息,让服务器删除文件
  • before-upload可以进行文件上传前检测
  • 这里可能会带默认的上传文件框,所以需要在type中手动清除默认的input upload框
  • handle-remove中包含了this.$axios的post方法提交信息,第一个框包含body内容,第二个包含header内容,对应flask中用request.headers.get()获取内容、用request.get_json().get()获取内容,res中为返回信息

③element button下载文件使用举例:

vue中:

<el-button type="primary" @click="download('train-queries.tsv')" round>下载文件</el-button>

download(fileName){
	this.$axios({
		method:'get',
		url:'/api/file/download',
		params: {
			name: fileName
		},
		responseType: 'blob'
	}).then(response => {
		const blob = new Blob([response.data]);
		const filename = response.headers['content-disposition'];
		const downloadElement = document.createElement('a');
		const href = window.URL.createObjectURL(blob); //创建下载的链接
		downloadElement.href = href;
		[downloadElement.download] = [filename.split('=')[1]];
		document.body.appendChild(downloadElement);
		downloadElement.click(); //点击下载
		document.body.removeChild(downloadElement); //下载完成移除元素
		window.URL.revokeObjectURL(href); //释放blob对
	}).catch((error) => {
		console.log(error)
	})
}

flask中:

# 在url里附带参数
@app.route("/file/download", methods=['GET'])
def downloadFile():
    fileName = request.args.get('name')
    print(fileName)

    # 保存文件的相对路径
    save_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'static')

    result = send_from_directory(save_path, fileName)
    print(save_path + ' ' + fileName)
    return result

注:

  • 这里使用的是axios的get方法,对应flask中request.args.get()
  • 这里的文件名可能出现乱码现象,所以应用英文名

④element input文本框限制数字使用:

<el-input 
	v-model="subvectorNumValue" 
	placeholder="请输入数目"
	type="number"
	style="width:20%"
	oninput="value=value.replace(/\D|^0/g,'')"
	:disabled="dis!=''">
</el-input>

注:

  • v-model绑定data中的值
  • style的width确认框的宽度

⑤element input文本域使用,此处不能输入,只是获取信息:

vue中:

<el-input
	id = "scroll_text"
	type="textarea"
	:rows="15"
	style="border: 2px solid white;"
	v-model="inferValue"
	readonly=""
>
</el-input>

watch:{
	inferValue() { //文本框自动滑到最底部
		// const textarea = document.getElementById('scroll_text');
		// textarea.scrollTop = textarea.scrollHeight;
		this.$nextTick(() => { //更新dom文档
			const textarea = document.getElementById('scroll_text');
			textarea.scrollTop = textarea.scrollHeight;
		})
	}
}

高亮显示:

data() {
	return {
		inferValue: '',
		dis: '',
		timer: ''
	}
},
methods: {
    // 在某个函数中需要this.timer = setInterval(this.changeColor, 500);启动定时器
    // 在某个函数中clearInterval(this.timer); 清除定时器
	changeColor(){ //这里是定时器的设置下高亮显示文本框
		if(document.getElementById("scroll_text").style.border=='2px solid red'){
			document.getElementById("scroll_text").style.border = '2px solid plum';
		}else{
			document.getElementById("scroll_text").style.border = '2px solid red';
		}
	},
}

注:

  • 高亮显示结束后可以重新设置颜色来显示结束,比如黑色;也可以弹出结束提醒

3.axios使用

①安装axios

npm install axios --save

②嵌入axios

npm install

③main.js配置如下:

import axios from "axios" //axios

Vue.prototype.$axios = axios //axios

④vue中使用方法如下:

//get
this.$axios({
	method:'get',
	url:'/api/file/download',
	params: {
		name: fileName
	},
}).then(response => {
	console.log(response )
}).catch((error) => {
	console.log(error)
})
//post
this.$axios
.post(
	"/api/file/remove",
	{
		fileName: file.name,
	},
	{
	    headers: {
			token: "666666",
		},
	}
)
.then((res) => {
	// if (res.data.code === 0) {
	// 	this.tempResult = res.data.data.id;
	// }
	console.log(res)
})
.catch(function (error) {
	console.log(error);
});

4.路由使用

①安装路由

npm install vue-router --save

②嵌入路由

npm install 

③src/router/index.js中配置

import Vue from'vue'
//导入路由插件
import Router from 'vue-router'
//导入上面定义的组件
import UserInput from '../components/UserInput'
import InferenceGenerate from '../components/InferenceGenerate'
import IndexLearnAndBuild from '../components/IndexLearnAndBuild'
import IndexSever from '../components/IndexSever'
import RecallAnalysis from '../components/ReacllAnalysis'
import DemonstrationDisplay from '../components/DemonstrationDisplay'
import HomePage from '../components/HomePage'
import LoginPage from '../components/LoginPage'
import RegisterPage from '../components/RegisterPage'
import BuildMain from '../components/BuildMain'
import SeverMain from '../components/SeverMain'
//安装路由
Vue.use(Router)
//配置路由
const routes = [
	{
		//路由路径
		path:'/',
		//重定向
		redirect: '/home'
	},
	{
		//路由路径
		path:'/home',
		//路由名称
		name:'home',
		//跳转到组件
		// component:UserInput
		component:HomePage
	},
	{
		//路由路径
		path:'/register',
		//路由名称
		name:'register',
		//跳转到组件
		component:RegisterPage
	},
	{
		//路由路径
		path:'/login',
		//路由名称
		name:'login',
		//跳转到组件
		component:LoginPage
	},
	{
		//路由路径
		path:'/IndexBuild',
		//路由名称
		name:'BuildMain',
		//跳转到组件
		component:BuildMain,
		redirect: '/IndexBuild/UserInput',
		children:[
			{
				//路由路径
				path:'/IndexBuild/UserInput',
				//路由名称
				name:'UserInput',
				//跳转到组件
				component:UserInput
			},
			{
				//路由路径
				path:'/IndexBuild/InferenceGenerate',
				//路由名称
				name:'InferenceGenerate',
				//跳转到组件
				component:InferenceGenerate
			},
			{
				//路由路径
				path:'/IndexBuild/IndexLearnAndBuild',
				//路由名称
				name:'IndexLearnAndBuild',
				//跳转到组件
				component:IndexLearnAndBuild
			}
		]
	},
	{
		//路由路径
		path:'/IndexSever',
		//路由名称
		name:'SeverMain',
		//跳转到组件
		component:SeverMain,
		redirect: '/IndexSever/IndexSever',
		children:[
			{
				//路由路径
				path:'/IndexSever/IndexSever',
				//路由名称
				name:'IndexSever',
				//跳转到组件
				component:IndexSever
			},
			{
				//路由路径
				path:'/IndexSever/RecallAnalysis',
				//路由名称
				name:'RecallAnalysis',
				//跳转到组件
				component:RecallAnalysis
			},
			{
				//路由路径
				path:'/IndexSever/DemonstrationDisplay',
				//路由名称
				name:'DemonstrationDisplay',
				//跳转到组件
				component:DemonstrationDisplay
			},
		]
	},
]
const router = new Router({
	mode:'history',
	redirect:'/UserInput',
	routes
})
export default router

④main.js中配置

import router from './router'//自动扫描里面的路由配置

⑤vue中使用

<keep-alive>
	<router-view @getPercentage="getPercentage" @getState="getState"></router-view>
</keep-alive>

<router-link to="/IndexBuild/UserInput">
	<i class="ti-user"></i>
	<p>User Input</p>
</router-link>

注:

  • keep-alive可以让页面变更后之前的页面数据保存,@用来让子组件向父组件传递信息,父组件中有getPercentage函数获取信息,子组件中用this.$emit('getPercentage', param);传递信息
  • 子路由的作用相当于二级路由,保证一级路由的前提下二级路由改变,可以在router-view中用router-link改变内容

5.socketio

①vue中安装

npm install vue-socket.io --save

npm install socket.io-client --save

npm install

②main.js

import VueSocketIO from 'vue-socket.io' //socketIO
import socketIO from 'socket.io-client'

Vue.use(new VueSocketIO({ //socketIO
	debug: true,
	connection: socketIO('http://127.0.0.1:5000/mysocket', {
		autoConnect: false // 取消自动连接
	}),
	// vuex: {
	// 	actionPrefix: 'SOCKET_',
	// 	mutationPrefix: 'SOCKET_'
	// },
	// options: { path: "/mysocket" }
}))

③vue组建中:

export default {
    name: 'InferenceGenerate',
	props: {
		msg: String
	},
	data() {
		return {
			inferValue: '',
		    dis: '',
			timer: ''
		}
	},
	sockets:{    // socket.io携带,与watch/create/data等同级
		connect:function () {
			console.log('连接成功');   // 判断是否正确连接上后端
		},	
		inferText:function (data) {    // inferText为对应后端发出的信息接口,可自行更换
			if(data.data == 'over') {
				clearInterval(this.timer); //清除定时器,防止继续闪烁
				document.getElementById("scroll_text").style.border = '2px solid'; //清除高亮颜色
				this.$alert('Infer embedding over', '', {
						confirmButtonText: '确定',
				});
			} else {
				this.inferValue = data.data.join('\n');   // 获取后端发出的信息
			}
		}
	},
    mounted () {    // 在组件开始渲染时进行调用
		this.$socket.connect(); // socket连接
		this.$socket.emit('test');  // 发送消息:对应后端test测试函数
		console.log('连接中');
	},
	destroyed() {
		// 断开socket
		if (this.$socket) {
			this.$socket.close();
		}
	}
}

④flask中:

import time
# socketIO
from flask_socketio import SocketIO, emit

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False  # 禁止中文转义
CORS(app, supports_credentials=True)
# socketio = SocketIO(app)
socketio = SocketIO(app, cors_allowed_origins="*")

@socketio.on('test', namespace='/mysocket')   # 监听前端发回的包头 test ,应用命名空间为 mysocket
def test():  # 此处可添加变量,接收从前端发回来的信息
    print('触发test函数')
    emit('myResopnse', {'data': 'test_OK'}) # 此处 myResopnse 对应前端 sockets 的 myResopnse

@socketio.on('getInfer', namespace='/mysocket')   # 监听前端发回的包头 test ,应用命名空间为 mysocket
def test():  # 此处可添加变量,接收从前端发回来的信息
    num = []
    for i in range(5):
        num.append(i)
        time.sleep(2)
        emit('inferText', {'data': num}) # 此处 myResopnse 对应前端 sockets 的 myResopnse
    emit('inferText', {'data': 'over'})  # 此处 myResopnse 对应前端 sockets 的 myResopnse

if __name__ == '__main__':
    # app.run(host='0.0.0.0',debug=True)
    socketio.run(app, host='0.0.0.0', port=5000, debug=True)

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到