一、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)