初始
This commit is contained in:
commit
a71741e0a8
17
public/index.html
Normal file
17
public/index.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
16
src/App.vue
Normal file
16
src/App.vue
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'App'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
#app {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
75
src/components/Breadcrumb.vue
Normal file
75
src/components/Breadcrumb.vue
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<el-breadcrumb separator-class="el-icon-caret-right" class="app-breadcrumb">
|
||||||
|
<el-breadcrumb-item>
|
||||||
|
<router-link to="/">主控</router-link>
|
||||||
|
</el-breadcrumb-item>
|
||||||
|
<el-breadcrumb-item v-for="(item, index) in breadcrumb" :key="index">
|
||||||
|
<router-link :to="item.path">{{ item.title }}</router-link>
|
||||||
|
</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Breadcrumb',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
breadcrumb: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.refreshBreadcrumb()
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* @description: 拿路由列表 并过滤不显示项 和没有权限项
|
||||||
|
*/
|
||||||
|
routes () {
|
||||||
|
return this.$router.options.routes.filter(
|
||||||
|
item => item.hidden !== true && item.roles.indexOf(this.$store.state.user.power.trim()) >= 0
|
||||||
|
)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 当前路径
|
||||||
|
*/
|
||||||
|
presentPath () {
|
||||||
|
return this.$route.path
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
presentPath () {
|
||||||
|
this.refreshBreadcrumb()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* @description: 刷新面包条数据
|
||||||
|
*/
|
||||||
|
refreshBreadcrumb () {
|
||||||
|
const temp = []
|
||||||
|
this.routes.forEach(element => {
|
||||||
|
element.children.forEach(child => {
|
||||||
|
if (this.presentPath.search(child.path) !== -1) {
|
||||||
|
temp[0] = { title: element.meta.title, path: element.path }
|
||||||
|
temp[1] = { title: child.meta.title, path: child.path }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.breadcrumb = temp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@/styles/theme.scss";
|
||||||
|
|
||||||
|
.el-breadcrumb * {
|
||||||
|
font-weight: normal !important;
|
||||||
|
color: $maintext-color !important;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 50px;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
</style>
|
530
src/components/ControllerTabs.vue
Normal file
530
src/components/ControllerTabs.vue
Normal file
@ -0,0 +1,530 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tab-container">
|
||||||
|
<el-button size="medium" type="primary" :class="activeIndex === index ? 'butIconGroupBG' : ''" class="butIconGroup"
|
||||||
|
v-for="(item, index) in controlItems" :key="index" @click="toggleContent(index)">
|
||||||
|
<i :class="item.icon" class="iconfont f-s-35"></i>
|
||||||
|
<div class="m-t-5 fb">{{ item.title }}</div>
|
||||||
|
</el-button>
|
||||||
|
<!-- 子内容菜单 -->
|
||||||
|
<div class="content" :class="{ 'active': activeIndex !== null }">
|
||||||
|
<!-- 订单任务 -->
|
||||||
|
<div v-if="activeIndex === 0" class="contentBox">
|
||||||
|
<!-- 标题 -->
|
||||||
|
<div class="clearB m-b-10 fb f-s-16 contentTit">
|
||||||
|
<i class="iconfont icon-dingdanguanli f-s-22 m-r-5"></i>
|
||||||
|
<span>送餐任务</span>
|
||||||
|
</div>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<el-form label-position="left" ref="questForm" :model="questForm" label-width="80px">
|
||||||
|
<el-form-item label="订单任务">
|
||||||
|
<el-select v-model="questForm.id" :filterable="isMobile" placeholder="请选择,也可输入搜索" :disabled="airLock">
|
||||||
|
<el-option v-for="item in questList" :key="item.id" :label="item.id" :value="item.id">
|
||||||
|
<span class="l">{{ item.id }}</span>
|
||||||
|
<span class="l m-l-5">{{ item.name }}</span>
|
||||||
|
<span class="l m-l-5">{{ item.receive_site_name }}</span>
|
||||||
|
<span v-for="product in item.products" :key="product.id" class="l m-l-5">{{
|
||||||
|
product.name
|
||||||
|
}}-{{ product.pro_buff }}-{{ product.num }}份</span>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="飞机操作">
|
||||||
|
<el-button-group>
|
||||||
|
<el-button size="mini" class="f-s-14" v-if="planeState & 1 && airLock" key="pubBut" type="info"
|
||||||
|
icon="iconfont el-icon-loading" disabled>
|
||||||
|
<font class="m-l-5">航线锁定</font>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="mini" class="f-s-14" v-if="planeState & 1 && !airLock" type="primary"
|
||||||
|
icon="f-s-14 iconfont icon-chakanzhihangrizhi" @click="runQuest">
|
||||||
|
<font class="m-l-5">提交任务</font>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="mini" class="f-s-14" v-if="planeState & 2" key="wirteBut" type="info"
|
||||||
|
icon="f-s-14 iconfont el-icon-loading" disabled>
|
||||||
|
<font class="m-l-5">航点写入中···</font>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="mini" class="f-s-14" v-if="planeState & 4 && !(planeState & 16)" type="warning"
|
||||||
|
icon="f-s-14 iconfont icon-jiesuo"
|
||||||
|
@click="publishFun('{setPlaneState:{bit:3,state:1,count:2,param:[1,0]}}')">
|
||||||
|
<font class="m-l-5">解锁飞机</font>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="mini" class="f-s-14" v-if="planeState & 16 && !(planeState & 1) && !(planeState & 2)"
|
||||||
|
type="success" icon="f-s-14 iconfont icon-yangshi_icon_tongyong_departure"
|
||||||
|
@click="publishFun('{setPlaneState:{bit:5,state:1}')">
|
||||||
|
<font class="m-l-5">执行任务</font>
|
||||||
|
</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="订单操作">
|
||||||
|
<el-button-group>
|
||||||
|
<el-button size="mini" class="f-s-14" type="danger" icon="iconfont icon-meiyoudingdan-01" key="celBUt"
|
||||||
|
@click="reQuest">
|
||||||
|
<font class="m-l-5">取消</font>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="mini" class="f-s-14" type="success" icon="iconfont icon-qiandai" key="bingBut"
|
||||||
|
@click="overQuest">
|
||||||
|
<font class="m-l-5">已送达</font>
|
||||||
|
</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<!-- 飞机操作 -->
|
||||||
|
<template v-if="activeIndex === 1">
|
||||||
|
<!-- 标题 -->
|
||||||
|
<div class="clearB m-b-10 fb f-s-16 contentTit">
|
||||||
|
<i class="iconfont icon-youxishoubing f-s-22 m-r-5"></i>
|
||||||
|
<span>飞机控制</span>
|
||||||
|
</div>
|
||||||
|
<div class="butIconBox">
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border"
|
||||||
|
@click="publishFun('{setPlaneState:{bit:3,state:1,count:2,param:[1,0]}}')">
|
||||||
|
<i class="iconfont icon-jiesuo f-s-24"></i>
|
||||||
|
<div class="m-t-5">解锁</div>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border"
|
||||||
|
@click="publishFun('{setPlaneState:{bit:3,state:0,count:2,param:[0,21196]}}')">
|
||||||
|
<i class="iconfont icon-suoding f-s-24"></i>
|
||||||
|
<div class="m-t-5">加锁</div>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border"
|
||||||
|
@click="publishFun('{setPlaneState:{bit:6,state:1,count:1,param:[2]}')">
|
||||||
|
<i class="iconfont icon-yangshi_icon_tongyong_departure f-s-24"></i>
|
||||||
|
<div class="m-t-5">起飞</div>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border"
|
||||||
|
@click="publishFun('{setPlaneState:{bit:7,state:1}')">
|
||||||
|
<i class="iconfont icon-fengzheng1 f-s-24"></i>
|
||||||
|
<div class="m-t-5">悬停</div>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border">
|
||||||
|
<i class="iconfont icon-duandianxufei f-s-24"></i>
|
||||||
|
<div class="m-t-5">恢复航点</div>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border"
|
||||||
|
@click="publishFun('{setPlaneState:{bit:9,state:1}')">
|
||||||
|
<i class="iconfont icon-yijianfanhang f-s-24"></i>
|
||||||
|
<div class="m-t-5">返航</div>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border"
|
||||||
|
@click="publishFun('{setPlaneState:{bit:5,state:1}')">
|
||||||
|
<i class="iconfont icon-yangshi_icon_tongyong_arriving f-s-24"></i>
|
||||||
|
<div class="m-t-5">降落</div>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<!-- 附加模组操作 -->
|
||||||
|
<template v-if="activeIndex === 2">
|
||||||
|
<!-- 标题 -->
|
||||||
|
<div class="clearB m-b-10 fb f-s-16 contentTit">
|
||||||
|
<i class="iconfont icon-mianxingdiaogou f-s-22 m-r-5"></i>
|
||||||
|
<span>挂载仓控制</span>
|
||||||
|
</div>
|
||||||
|
<div class="butIconBox">
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border" @click="publishFun('{hookConteroller:4')">
|
||||||
|
<i class="iconfont icon-zhongliang f-s-24"></i>
|
||||||
|
<div class="m-t-5">重量归零</div>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border" @click="publishFun('{hookConteroller:0')">
|
||||||
|
<i class="iconfont icon-xiangshang f-s-24"></i>
|
||||||
|
<div class="m-t-5">收钩</div>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border" @click="publishFun('{hookConteroller:3')">
|
||||||
|
<i class="iconfont icon-qiyong f-s-24"></i>
|
||||||
|
<div class="m-t-5">继续</div>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border" @click="publishFun('{hookConteroller:2')">
|
||||||
|
<i class="iconfont icon-xuanting-zanting f-s-24"></i>
|
||||||
|
<div class="m-t-5">暂停</div>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<!-- 标题 -->
|
||||||
|
<div class="clearB m-b-10 fb f-s-16 contentTit">
|
||||||
|
<i class="iconfont icon-shipinjiankong f-s-22 m-r-5"></i>
|
||||||
|
<span>摄像头控制</span>
|
||||||
|
</div>
|
||||||
|
<div class="butIconBox">
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border"
|
||||||
|
@click="publishFun('{cameraController:{item:0,val:0}')">
|
||||||
|
<i class="iconfont icon-icon-rotation f-s-24"></i>
|
||||||
|
<div class="m-t-5">一键回中</div>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border"
|
||||||
|
@click="publishFun('{cameraController:{item:2,val:0,yaw:0,pitch:-1}}')">
|
||||||
|
<i class="iconfont icon-rotateX f-s-24"></i>
|
||||||
|
<div class="m-t-5">一键俯视</div>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border">
|
||||||
|
<i class="iconfont icon-rotateY f-s-24"></i>
|
||||||
|
<div class="m-t-5">旋转</div>
|
||||||
|
</el-button>
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border">
|
||||||
|
<i class="iconfont icon-fangda f-s-24"></i>
|
||||||
|
<div class="m-t-5">焦距</div>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<!-- 标题 -->
|
||||||
|
<div class="clearB m-b-10 fb f-s-16 contentTit">
|
||||||
|
<i class="iconfont icon-tongzhi f-s-22 m-r-5"></i>
|
||||||
|
<span>喇叭控制</span>
|
||||||
|
</div>
|
||||||
|
<div class="butIconBox">
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border">
|
||||||
|
<i class="iconfont icon-icon-test f-s-24"></i>
|
||||||
|
<div class="m-t-5">喊话</div>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<!-- 飞机调试 -->
|
||||||
|
<template v-if="activeIndex === 3">
|
||||||
|
<!-- 标题 -->
|
||||||
|
<div class="clearB m-b-10 fb f-s-16 contentTit">
|
||||||
|
<i class="iconfont icon-tongzhi f-s-22 m-r-5"></i>
|
||||||
|
<span>飞机调试</span>
|
||||||
|
</div>
|
||||||
|
<div class="butIconBox">
|
||||||
|
<el-button size="medium" type="primary" class="butIcon border">
|
||||||
|
<i class="iconfont icon-icon-test f-s-24"></i>
|
||||||
|
<div class="m-t-5">喊话</div>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<!-- end 子内容菜单 -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { questAss } from '@/utils/api/table'
|
||||||
|
import mqtt from '@/utils/mqtt'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TabController',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
controlItems: [// 菜单
|
||||||
|
{ title: '任务', icon: 'icon-songcanfuwu' },
|
||||||
|
{ title: '控制', icon: 'icon-youxishoubing' },
|
||||||
|
{ title: '扩展', icon: 'icon-linghuokuozhan' },
|
||||||
|
{ title: '调试', icon: 'icon-banshou_Line' }
|
||||||
|
],
|
||||||
|
activeIndex: null, // 当前选中的菜单
|
||||||
|
tabIsOpen: false, // 判断tab 是否弹出
|
||||||
|
questForm: { // 送餐任务表单
|
||||||
|
id: ''
|
||||||
|
},
|
||||||
|
planesId: this.$route.params.id, // 飞机id
|
||||||
|
airLock: false // 当前飞机是否被锁定
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
plane: {
|
||||||
|
typeof: 'Object',
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* @description: 终端平台是否是pc
|
||||||
|
*/
|
||||||
|
isMobile () {
|
||||||
|
return this.$store.state.app.isMobile
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 飞机状态
|
||||||
|
*/
|
||||||
|
planeState () {
|
||||||
|
const plane = this.plane
|
||||||
|
return plane ? plane.planeState.state : null
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 已付款 已承接 未发货 未发起退款 订单列表
|
||||||
|
*/
|
||||||
|
questList () {
|
||||||
|
const plane = this.plane
|
||||||
|
return plane ? this.$store.state.questList.filter((item) => item.shop_id === plane.shop_id && item.quest === '1' && item.status === '20' && item.back === '0' && item.runing === null) : []
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 已付款 已承接 已发货 订单列表
|
||||||
|
*/
|
||||||
|
overQuestList () {
|
||||||
|
return this.$store.state.questList.filter((item) => item.quest === '1' && item.status === '30' && item.runing != null)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 航线列表
|
||||||
|
*/
|
||||||
|
routeList () {
|
||||||
|
return this.$store.state.routeList
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 获取站点列表
|
||||||
|
*/
|
||||||
|
siteList () {
|
||||||
|
return this.$store.state.siteList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
questAss,
|
||||||
|
/**
|
||||||
|
* @description: 菜单切换 PS:UI
|
||||||
|
* @param {*} index 序号
|
||||||
|
*/
|
||||||
|
toggleContent (index) {
|
||||||
|
this.activeIndex = this.activeIndex === index ? null : index
|
||||||
|
if (this.tabIsOpen) {
|
||||||
|
if (index !== this.activeIndex) {
|
||||||
|
this.tabIsOpen = false
|
||||||
|
this.$emit('mapXOffset', -200)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.tabIsOpen = true
|
||||||
|
this.$emit('mapXOffset', 200)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 发布 mqtt 信息
|
||||||
|
* @param {*} jsonData {'item':val} // item: questAss飞行航点任务 setPlaneState 设置飞机状态 getPlaneState获取飞机状态 resetState设置飞机初始状态 chan1油门通道1 chan2油门通道2 chan3油门通道3 chan4油门通道4 hookConteroller钩子控制 cameraController云台相机控制
|
||||||
|
*/
|
||||||
|
publishFun (jsonData) {
|
||||||
|
if (this.plane) {
|
||||||
|
const val = jsonData
|
||||||
|
mqtt.publishFun(`cmd/${this.plane.macadd}`, val)
|
||||||
|
} else {
|
||||||
|
this.$message.warning('与飞机通信未接通,请稍后')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 执行订单任务
|
||||||
|
*/
|
||||||
|
runQuest () {
|
||||||
|
if (this.questForm.id === '') {
|
||||||
|
this.$message.error('未选择订单任务!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let i
|
||||||
|
this.questList.map((item) => {
|
||||||
|
if (this.questForm.id === item.id) {
|
||||||
|
i = true
|
||||||
|
/* 插入日志 */
|
||||||
|
this.$store.dispatch('fetchLog', { content: `${this.plane.name} 开始执行 订单ID:${item.id}、叫餐号:${item.food_sn}号。` })
|
||||||
|
/* 执行写在这里 */
|
||||||
|
if (item.bind_route === null) { // 判断站点是否已经绑定站点 中断操作
|
||||||
|
this.$message.error('此站点,未绑定任务航点')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let routeData
|
||||||
|
try {
|
||||||
|
const bindRoute = item.bind_route.split(',')
|
||||||
|
routeData = this.routeList.find(element => element.id === bindRoute[0]).route_data
|
||||||
|
routeData = JSON.parse(routeData)// 反序列化
|
||||||
|
/* 处理声音航点 航点里面的表达式 如$food_sn$ 正则替换成订单对应的字段 */
|
||||||
|
item.telTail = item.tel.substr(-4)// 手动加一个手机尾号telTail字段 从 tel字段截取后四位
|
||||||
|
routeData.tasks.forEach((x, index) => {
|
||||||
|
if (x.sound) {
|
||||||
|
const str = this.voiceRouteParse(item, x.sound)
|
||||||
|
routeData.tasks[index].sound = str// 重新写入声音航点
|
||||||
|
}
|
||||||
|
})
|
||||||
|
routeData = JSON.stringify(routeData)// 重新序列化
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('操作失败,请重新尝试')
|
||||||
|
}
|
||||||
|
this.publishFun(`{"questAss":${routeData}}`)// 发送航点信息主题
|
||||||
|
this.questAss(item.id, 'status', '30')// 订单改为发货状态
|
||||||
|
this.$store.dispatch('fetchLockSite', { id: item.receive_site_id, runing: this.plane.id })// 航线注册飞机 锁定送餐点
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (i) { return }
|
||||||
|
this.$message.error('此订单已被申请退款或者订单已经被取消!')
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 匹配声音航点字符串 比如$food_sn$ food_sn匹配成 送餐订单里面的对应字段
|
||||||
|
* @param {*} questItem 送餐的订单 ps:从数据库里拿的 订单信息对象
|
||||||
|
* @param {*} voiceRouteStr 声音航点的字符串
|
||||||
|
*/
|
||||||
|
voiceRouteParse (questItem, voiceRouteStr) {
|
||||||
|
// 定义正则表达式
|
||||||
|
const regex = /\$(.*?)\$/g
|
||||||
|
// 使用replace()方法进行匹配和替换
|
||||||
|
const replacedStr = voiceRouteStr.replace(regex, (match, p1) => {
|
||||||
|
// match表示匹配到的字符串,p1表示捕获组中的内容
|
||||||
|
return questItem[p1] // 用捕获组的内容替换匹配到的字符串
|
||||||
|
})
|
||||||
|
// 输出替换后的字符串
|
||||||
|
return replacedStr // 输出 "请45号电话为13301115846的先生取餐"
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 取消任务
|
||||||
|
*/
|
||||||
|
reQuest () {
|
||||||
|
if (!this.airLock) { // 只有飞机锁定状态 才向量下执行 "取消"操作
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$confirm('确认复位飞机状态,并清除航线的锁定?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
this.overQuestList.forEach((item) => {
|
||||||
|
if (item.runing === this.planesId) {
|
||||||
|
/* 插入日志 */
|
||||||
|
this.$store.dispatch('fetchLog', { content: `订单ID:${item.id},送餐任务取消。` })
|
||||||
|
/* 执行写在这里 */
|
||||||
|
this.publishFun('{"resetState":1}')// 发送设置飞机状态主题 状态设为闲置
|
||||||
|
this.questAss(item.id, 'status', '20')// 订单改回到发货状态之前 既“已付款状态”
|
||||||
|
this.$store.dispatch('fetchLockSite', { id: item.receive_site_id, runing: 'null' })// 解锁航线
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
this.$message.info('取消操作!')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 已送达任务
|
||||||
|
*/
|
||||||
|
overQuest () {
|
||||||
|
if (!this.airLock) { // 只有飞机锁定状态 才向量下执行 "已送达"操作
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$confirm('确认将订单状态改为已送达吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
// 确认执行
|
||||||
|
this.overQuestList.map((item) => {
|
||||||
|
if (item.runing === this.planesId) { // 找出当前飞机正在执行的任务
|
||||||
|
/* 插入日志 */
|
||||||
|
this.$store.dispatch('fetchLog', { content: `订单ID:${item.id} 送餐任务已完成。` })
|
||||||
|
/* 执行写在这里 */
|
||||||
|
this.publishFun('{"resetState":1}')// 发送设置飞机状态主题 状态设为闲置
|
||||||
|
this.questAss(item.id, 'status', '40')// 订单改为已完成状态
|
||||||
|
this.$store.dispatch('fetchLockSite', { id: item.receive_site_id, runing: 'null' })// 解锁航线
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
this.$message.info('取消操作!')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 在地图上绘制航线
|
||||||
|
*/
|
||||||
|
makeRouteForMap () {
|
||||||
|
let bindRoute
|
||||||
|
let routeData
|
||||||
|
this.siteList.forEach(item => {
|
||||||
|
if (item.runing === this.planesId) {
|
||||||
|
// 获取航点信息
|
||||||
|
if (item.bind_route !== null) { // 判断站点是否已经绑定站点
|
||||||
|
bindRoute = item.bind_route.split(',')
|
||||||
|
this.$store.dispatch('fetchRouteList').then(res => { // 只能异步拿 虽然效率低一些
|
||||||
|
routeData = res.find(element => element.id === bindRoute[0]).route_data
|
||||||
|
this.$emit('makeRoute', JSON.parse(routeData))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.$message.error('此站点,未绑定任务航点')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
if (this.siteList && this.routeList) {
|
||||||
|
this.airLock = this.siteList.some(item => item.runing === this.planesId)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
airLock (val) {
|
||||||
|
if (val) { // 如果当前飞机正在执行任务 把航线绘制到地图上
|
||||||
|
this.makeRouteForMap()
|
||||||
|
} else { // 如果没有执行任务 把地图上航线清除
|
||||||
|
this.$emit('clearRoute')
|
||||||
|
}
|
||||||
|
this.$store.dispatch('fetchQuestList')// 刷新订单列表
|
||||||
|
this.$store.dispatch('fetchSiteList')// 刷新站点列表
|
||||||
|
},
|
||||||
|
siteList (val) {
|
||||||
|
this.airLock = val.some(item => item.runing === this.planesId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@/styles/theme.scss";
|
||||||
|
|
||||||
|
.tab-container {
|
||||||
|
height: 365px;
|
||||||
|
width: 80px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
position: fixed;
|
||||||
|
right: 15px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.butIconGroup {
|
||||||
|
color: $maintext-color;
|
||||||
|
background-color: rgba(255, 255, 255, 0.5);
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 5px;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.butIconGroupBG {
|
||||||
|
color: $graylight-color;
|
||||||
|
background-color: $brand-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.butIconGroup:last-of-type {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
color: $maintext-color;
|
||||||
|
border-radius: 15px;
|
||||||
|
height: 100%;
|
||||||
|
position: fixed;
|
||||||
|
right: -400px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 350px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
transition: right 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content.active {
|
||||||
|
right: 95px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentTit i {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.butIcon {
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
width: 66px;
|
||||||
|
height: 66px;
|
||||||
|
text-align: center;
|
||||||
|
border: none;
|
||||||
|
float: left;
|
||||||
|
margin: 0px !important;
|
||||||
|
margin-bottom: 15px !important;
|
||||||
|
margin-right: 15px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.butIconBox .butIcon:nth-child(4n) {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
</style>
|
467
src/components/MapBox.vue
Normal file
467
src/components/MapBox.vue
Normal file
@ -0,0 +1,467 @@
|
|||||||
|
<template>
|
||||||
|
<div id="map">
|
||||||
|
<slot name="content"></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import mapboxgl from 'mapbox-gl'
|
||||||
|
import planeIcon from '@/assets/svg/plane.svg'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MapBox',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
map: null,
|
||||||
|
planes: [], // 飞机对象
|
||||||
|
lngLats: [], // 航线 所有航点
|
||||||
|
wayLngLats: [], // 航线 不包括起点航点
|
||||||
|
takeoffLngLats: [], // 航线 第一个航点 起点 最后一个航点
|
||||||
|
GoogleRasterStyle: {
|
||||||
|
name: 'Mapbox Streets',
|
||||||
|
sprite: this.$store.state.settings.host + '/map/sprite',
|
||||||
|
glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
|
||||||
|
version: 8,
|
||||||
|
sources: {
|
||||||
|
google: {
|
||||||
|
type: 'raster',
|
||||||
|
tileSize: 256,
|
||||||
|
tiles: ['https://sb.im/google-maps/vt?lyrs=s&x={x}&y={y}&z={z}']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layers: [{ id: 'GoogleRasterLayer', type: 'raster', source: 'google' }]
|
||||||
|
},
|
||||||
|
MapBoxglRasterStyle: 'mapbox://styles/mapbox/outdoors-v12',
|
||||||
|
MapBoxglSatellite: 'mapbox://styles/mapbox/satellite-streets-v12'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
defaultLnglat () {
|
||||||
|
return this.$store.getters['app/getDefaultLngLat']
|
||||||
|
},
|
||||||
|
defaultZoom () {
|
||||||
|
return this.$store.getters['app/getDefaultZoom']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.init().then(() => { // 地图初始化之后
|
||||||
|
this.map.on('load', () => {
|
||||||
|
// 更新样式,添加自定义 sprite
|
||||||
|
|
||||||
|
// 星空背景 大气层
|
||||||
|
this.map.setFog({
|
||||||
|
range: [1.0, 8.0],
|
||||||
|
color: 'white',
|
||||||
|
'horizon-blend': 0.01
|
||||||
|
})
|
||||||
|
// 添加等高线图层
|
||||||
|
// this.map.addSource('contours', {
|
||||||
|
// type: 'vector',
|
||||||
|
// url: 'mapbox://mapbox.mapbox-terrain-v2'
|
||||||
|
// })
|
||||||
|
// this.map.addLayer({
|
||||||
|
// id: 'contours',
|
||||||
|
// type: 'line',
|
||||||
|
// source: 'contours',
|
||||||
|
// 'source-layer': 'contour',
|
||||||
|
// layout: {
|
||||||
|
// 'line-join': 'round',
|
||||||
|
// 'line-cap': 'round'
|
||||||
|
// },
|
||||||
|
// paint: {
|
||||||
|
// 'line-color': '#ff69b4', // 设置等高线颜色
|
||||||
|
// 'line-width': 1 // 设置等高线宽度
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// 3纬度
|
||||||
|
// this.map.addSource('mapbox-dem', {
|
||||||
|
// type: 'raster-dem',
|
||||||
|
// url: 'mapbox://mapbox.mapbox-terrain-dem-v1',
|
||||||
|
// tileSize: 512,
|
||||||
|
// maxzoom: 14
|
||||||
|
// })
|
||||||
|
// this.map.setTerrain({ source: 'mapbox-dem', exaggeration: 1.5 })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* @description: 地图初始化
|
||||||
|
*/
|
||||||
|
async init () {
|
||||||
|
// token
|
||||||
|
mapboxgl.accessToken = 'pk.eyJ1Ijoic3pkb3QiLCJhIjoiY2xhNXpjd3IxMG9leTNubjg3dnFhZm84ZyJ9.ubUTyh6ZR87qyndE5adgvw'
|
||||||
|
// 实例化map
|
||||||
|
this.map = new mapboxgl.Map({
|
||||||
|
container: 'map',
|
||||||
|
style: this.GoogleRasterStyle,
|
||||||
|
center: this.defaultLnglat,
|
||||||
|
zoom: this.defaultZoom,
|
||||||
|
pitch: 45,
|
||||||
|
bearing: 0,
|
||||||
|
projection: 'globe'
|
||||||
|
})
|
||||||
|
// 地图控件
|
||||||
|
this.map.addControl(new mapboxgl.ScaleControl(), 'bottom-right')
|
||||||
|
this.map.addControl(new mapboxgl.NavigationControl(), 'bottom-right')
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 清除地图上的航线
|
||||||
|
*/
|
||||||
|
clearRoute () {
|
||||||
|
// 判断地图上是否已经存在 航线信息 存在先将其清除
|
||||||
|
if (this.map.getSource('way_route')) {
|
||||||
|
this.map.removeLayer('takeoff_layer')
|
||||||
|
this.map.removeSource('takeoff_route')
|
||||||
|
this.map.removeLayer('way_route_layer')
|
||||||
|
this.map.removeLayer('way_route_layer1')
|
||||||
|
this.map.removeSource('way_route')
|
||||||
|
this.map.removeLayer('home_point_layer')
|
||||||
|
this.map.removeSource('home_point')
|
||||||
|
this.map.removeLayer('takeoff_point_layer')
|
||||||
|
this.map.removeSource('takeoff_point')
|
||||||
|
for (let i = 2; i < this.lngLats.length - 1; i++) {
|
||||||
|
this.map.removeLayer('way_point_layer' + i)
|
||||||
|
this.map.removeSource('way_point' + i)
|
||||||
|
}
|
||||||
|
this.lngLats = []
|
||||||
|
this.wayLngLats = []
|
||||||
|
this.takeoffLngLats = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 绘画航线
|
||||||
|
* @param {*} routeObj 航点文件对象
|
||||||
|
*/
|
||||||
|
makeRoute (routeObj) {
|
||||||
|
// 初始化清空航线
|
||||||
|
this.clearRoute()
|
||||||
|
// 获取所有航点 及 非起点所有航点
|
||||||
|
routeObj.tasks.forEach((element, index) => {
|
||||||
|
const lngLat = [element.y, element.x, 100]
|
||||||
|
this.lngLats.push(lngLat)
|
||||||
|
if (index > 1 && index < routeObj.taskcount - 1) {
|
||||||
|
this.wayLngLats.push(lngLat)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 跳转home点位置
|
||||||
|
this.goto(this.lngLats[0])
|
||||||
|
// 起飞 降落虚线
|
||||||
|
if (routeObj.tasks[this.lngLats.length - 1].command === 20) {
|
||||||
|
this.takeoffLngLats.push(this.lngLats[2])
|
||||||
|
this.takeoffLngLats.push(this.lngLats[1])
|
||||||
|
this.takeoffLngLats.push(this.lngLats[this.lngLats.length - 2])
|
||||||
|
} else if (routeObj.tasks[this.lngLats.length - 1].command === 21) {
|
||||||
|
this.takeoffLngLats.push(this.lngLats[2])
|
||||||
|
this.takeoffLngLats.push(this.lngLats[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 起飞降落虚线
|
||||||
|
this.map.addSource('takeoff_route', {
|
||||||
|
type: 'geojson',
|
||||||
|
data: {
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {},
|
||||||
|
geometry: {
|
||||||
|
type: 'LineString',
|
||||||
|
coordinates: this.takeoffLngLats
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.map.addLayer({
|
||||||
|
id: 'takeoff_layer',
|
||||||
|
type: 'line',
|
||||||
|
source: 'takeoff_route',
|
||||||
|
layout: {
|
||||||
|
'line-join': 'round', // 线连接处的样式
|
||||||
|
'line-cap': 'round'// 线结束处的样式
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
'line-color': '#fff', // 线的颜色
|
||||||
|
'line-width': 2, // 线的宽度
|
||||||
|
'line-opacity': 1.0, // 线的透明度
|
||||||
|
'line-dasharray': [3, 2]// 虚线
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 航线 叠加描边线
|
||||||
|
this.map.addSource('way_route', {
|
||||||
|
type: 'geojson',
|
||||||
|
data: {
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {},
|
||||||
|
geometry: {
|
||||||
|
type: 'LineString',
|
||||||
|
coordinates: this.wayLngLats
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.map.addLayer({
|
||||||
|
id: 'way_route_layer1',
|
||||||
|
type: 'line',
|
||||||
|
source: 'way_route',
|
||||||
|
layout: {
|
||||||
|
'line-join': 'round', // 线连接处的样式
|
||||||
|
'line-cap': 'round'// 线结束处的样式
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
'line-color': '#fff', // 线的颜色
|
||||||
|
'line-width': 4, // 线的宽度
|
||||||
|
'line-opacity': 1.0// 线的透明度
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.map.addLayer({
|
||||||
|
id: 'way_route_layer',
|
||||||
|
type: 'line',
|
||||||
|
source: 'way_route',
|
||||||
|
layout: {
|
||||||
|
'line-join': 'round', // 线连接处的样式
|
||||||
|
'line-cap': 'round'// 线结束处的样式
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
'line-color': '#f00', // 线的颜色
|
||||||
|
'line-width': 2, // 线的宽度
|
||||||
|
'line-opacity': 1.0// 线的透明度
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 航点 图标 PS:home点 起飞点 航点 送餐点等
|
||||||
|
this.lngLats.forEach((item, index) => {
|
||||||
|
// home点
|
||||||
|
if (index === 0) { // 第一点home点
|
||||||
|
this.map.addSource('home_point', {
|
||||||
|
type: 'geojson',
|
||||||
|
data: {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: [{
|
||||||
|
type: 'Feature',
|
||||||
|
geometry: {
|
||||||
|
type: 'Point',
|
||||||
|
coordinates: item
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
'marker-symbol': 'homePoint'// home点图标
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.map.addLayer({
|
||||||
|
id: 'home_point_layer',
|
||||||
|
type: 'symbol',
|
||||||
|
source: 'home_point',
|
||||||
|
layout: {
|
||||||
|
'icon-image': '{marker-symbol}',
|
||||||
|
'icon-size': 1.5, // 图标的大小
|
||||||
|
'icon-allow-overlap': true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (index === 1) { // 起飞点
|
||||||
|
let takeoffPoint
|
||||||
|
// 起点图标设定 根据最后一个航点为RETURN_TO_LAUNCH 20 返回起点降落 或者 最后一个航点为LAND 21 指定点降落
|
||||||
|
if (routeObj.tasks[this.lngLats.length - 1].command === 20) {
|
||||||
|
takeoffPoint = 'takeoffLandPoint'
|
||||||
|
} else if (routeObj.tasks[this.lngLats.length - 1].command === 21) {
|
||||||
|
takeoffPoint = 'takeoffPoint'
|
||||||
|
}
|
||||||
|
this.map.addSource('takeoff_point', {
|
||||||
|
type: 'geojson',
|
||||||
|
data: {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: [{
|
||||||
|
type: 'Feature',
|
||||||
|
geometry: {
|
||||||
|
type: 'Point',
|
||||||
|
coordinates: item
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
'marker-symbol': takeoffPoint// 起飞点图标
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.map.addLayer({
|
||||||
|
id: 'takeoff_point_layer',
|
||||||
|
type: 'symbol',
|
||||||
|
source: 'takeoff_point',
|
||||||
|
layout: {
|
||||||
|
'icon-image': '{marker-symbol}',
|
||||||
|
'icon-size': 1.5, // 图标的大小
|
||||||
|
'icon-allow-overlap': true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else { // waypoint点
|
||||||
|
if (index !== this.lngLats.length - 1) { // 最后一个点不显示 要不和最后一个点结合 要不和起点结合
|
||||||
|
let wayPoint = 'wayPoint'
|
||||||
|
if (index === this.lngLats.length - 2) { // 降落点 如果是LAND 21 和最后一个waypoint点结合
|
||||||
|
if (routeObj.tasks[this.lngLats.length - 1].command === 21) {
|
||||||
|
wayPoint = 'wayLandPoint'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (routeObj.tasks[index].command === 94) { // command字段为94既然 投放放勾航点 放勾图标
|
||||||
|
wayPoint = 'hookPoint'
|
||||||
|
}
|
||||||
|
this.map.addSource('way_point' + index, {
|
||||||
|
type: 'geojson',
|
||||||
|
data: {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: [{
|
||||||
|
type: 'Feature',
|
||||||
|
geometry: {
|
||||||
|
type: 'Point',
|
||||||
|
coordinates: item
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
'marker-symbol': wayPoint
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.map.addLayer({
|
||||||
|
id: 'way_point_layer' + index,
|
||||||
|
type: 'symbol',
|
||||||
|
source: 'way_point' + index,
|
||||||
|
layout: {
|
||||||
|
'icon-image': '{marker-symbol}',
|
||||||
|
'icon-size': 1.5, // 图标的大小
|
||||||
|
'icon-allow-overlap': true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 创建飞机轨迹 ps:原理删除之前的轨迹 重新绘制
|
||||||
|
* @param {arr} coordinatesArray 飞机经纬高度数组
|
||||||
|
*/
|
||||||
|
createPathWithArray (coordinatesArray) {
|
||||||
|
// 创建一个包含纬度、经度和高度信息的 GeoJSON LineString
|
||||||
|
const geojson = {
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {},
|
||||||
|
geometry: {
|
||||||
|
type: 'LineString',
|
||||||
|
coordinates: coordinatesArray.map(coord => [coord[0], coord[1], coord[2]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 不是第一次 则改变路径图层里面得数据
|
||||||
|
if (this.map.getLayer('path')) {
|
||||||
|
this.map.getSource('path').setData(geojson)
|
||||||
|
} else { // 第一次在地图里添加路径图层
|
||||||
|
// 如果坐标数组不为空,创建新路径
|
||||||
|
if (coordinatesArray.length > 0) {
|
||||||
|
// 添加3D图层
|
||||||
|
this.map.addLayer({
|
||||||
|
id: 'path',
|
||||||
|
type: 'line',
|
||||||
|
source: {
|
||||||
|
type: 'geojson',
|
||||||
|
data: geojson
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
'line-cap': 'round',
|
||||||
|
'line-join': 'round'
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
'line-color': 'purple',
|
||||||
|
'line-width': 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 创建一个飞机
|
||||||
|
* @param {*} plane 飞机对象
|
||||||
|
*/
|
||||||
|
makePlane (plane, index = 0) {
|
||||||
|
const customIcon = document.createElement('div')
|
||||||
|
customIcon.className = 'custom-marker' // 添加自定义样式类名
|
||||||
|
customIcon.style.backgroundImage = `url(${planeIcon})` // 使用引入的 SVG 图标
|
||||||
|
customIcon.style.width = '64px' // 图标宽度
|
||||||
|
customIcon.style.height = '64px' // 图标高度
|
||||||
|
// 创建一个marker对象
|
||||||
|
this.planes[index] = new mapboxgl.Marker(customIcon)
|
||||||
|
.setLngLat([plane.lng, plane.lat])
|
||||||
|
.setPopup(new mapboxgl.Popup({ offset: 25 }).setHTML('<h3>' + plane.name + '</h3><hr><p>macID:' + plane.macadd + '</p>')) // 添加弹出窗口
|
||||||
|
.addTo(this.map)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 移除页面上的所有飞机
|
||||||
|
*/
|
||||||
|
removePlanes () {
|
||||||
|
this.planes.forEach(plane => {
|
||||||
|
plane.remove()
|
||||||
|
})
|
||||||
|
this.planes = []
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 实时更新经纬度
|
||||||
|
* @param {obj} lnglat lng经度 lat纬度
|
||||||
|
* @param {*} index 飞机序号
|
||||||
|
* @param {*} pathArr 是否创建轨迹
|
||||||
|
*/
|
||||||
|
setPlaneLngLat (lnglat, index, pathArr, isflow) {
|
||||||
|
// 设置新的经纬度
|
||||||
|
const plane = this.planes[index]
|
||||||
|
if (plane != null) {
|
||||||
|
plane.setLngLat([lnglat.lng, lnglat.lat])
|
||||||
|
}
|
||||||
|
// 创建轨迹
|
||||||
|
this.createPathWithArray(pathArr)// 创建轨迹
|
||||||
|
// 镜头跟随飞机
|
||||||
|
if (isflow) {
|
||||||
|
this.map.flyTo({
|
||||||
|
center: lnglat,
|
||||||
|
speed: 2,
|
||||||
|
curve: 1,
|
||||||
|
easing (t) {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 镜头跳转
|
||||||
|
* @param {obj} lnglat {lng:lng,lat:lat} 经纬度
|
||||||
|
*/
|
||||||
|
goto (lnglat) {
|
||||||
|
this.map.flyTo({
|
||||||
|
center: lnglat,
|
||||||
|
speed: 2,
|
||||||
|
curve: 1,
|
||||||
|
easing (t) {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 屏幕横移
|
||||||
|
* @param {*} val 正数向左移动 负数向右移动
|
||||||
|
*/
|
||||||
|
mapXOffset (val) {
|
||||||
|
this.map.panBy([val, 0], {
|
||||||
|
duration: 333 // 过渡持续时间,以毫秒为单位
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
if (this.map) {
|
||||||
|
this.$store.commit('app/setDefaultLngLat', this.map.getCenter())// 记录当前经纬度 缺省
|
||||||
|
this.$store.commit('app/setDefaultZoom', this.map.getZoom())// 记录当前经纬度 缺省
|
||||||
|
this.map.remove()// 清除地图资源
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
#map {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-marker {
|
||||||
|
background-size: cover;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
78
src/components/PlaneStatus.vue
Normal file
78
src/components/PlaneStatus.vue
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<template>
|
||||||
|
<el-row class="m-15" type="flex" justify="space-between">
|
||||||
|
<el-col :span="2">
|
||||||
|
<PublicTag icon="icon-weixing" val="16.88" unit="颗" state="normal" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="2">
|
||||||
|
<PublicTag icon="icon-weixing" val="16.88" unit="颗" state="normal" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="2">
|
||||||
|
<PublicTag icon="icon-weixing" val="16.88" unit="颗" state="normal" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="2">
|
||||||
|
<PublicTag icon="icon-gaodu" val="50" unit="米" state="normal" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="3">
|
||||||
|
<HeartTag :heartBeat="planeState.heartBeat" :heartRandom="planeState.heartRandom"
|
||||||
|
:getPlaneMode="planeState.getPlaneMode" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="2">
|
||||||
|
<PublicTag icon="icon-mianxingdiaogou" :val="planeState.state" unit="" state="normal" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="2">
|
||||||
|
<PublicTag icon="icon-gaodu" :val="planeState.groundSpeed" unit="米" state="normal" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="2">
|
||||||
|
<PublicTag icon="icon-gaodu" val="50" unit="米" state="danger" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="2">
|
||||||
|
<PublicTag icon="icon-gaodu" val="50" unit="米" state="danger" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import HeartTag from '@/components/Tag/HeartTag'
|
||||||
|
import PublicTag from '@/components/Tag/PublicTag'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PlaneStatus',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
planesId: this.$route.params.id,
|
||||||
|
planeState: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
HeartTag,
|
||||||
|
PublicTag
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
plane () {
|
||||||
|
return this.$store.state.airList.find(plane => plane.id === this.planesId)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
plane: {
|
||||||
|
handler (val) {
|
||||||
|
this.planeState = val.planeState
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {},
|
||||||
|
created () {
|
||||||
|
if (this.plane) {
|
||||||
|
this.planeState = this.plane.planeState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@/styles/theme.scss";
|
||||||
|
|
||||||
|
.el-row {
|
||||||
|
z-index: 90;
|
||||||
|
}
|
||||||
|
</style>
|
205
src/components/QuestTabs.vue
Normal file
205
src/components/QuestTabs.vue
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-15">
|
||||||
|
<el-collapse v-model="activeNames" accordion>
|
||||||
|
<template v-if="list.length != 0">
|
||||||
|
<el-collapse-item class="mainFontColor" v-for="(item, index) in list" :key="item.id"
|
||||||
|
:title="'订单ID:' + item.id + ' | 时间:' + parseTime(item.addtime)" :name="item.id">
|
||||||
|
<el-descriptions direction="vertical" :column="3" border>
|
||||||
|
<el-descriptions-item label="状态" span="1">{{ orderState(item.status, item.back, item.quest) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="订单号" span="2">{{ item.order_sn }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="昵称">{{ item.name }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="手机号">{{ item.tel }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="价格">{{ item.price }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="货品总数">{{ item.product_num }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="收货站点">{{ item.receive_site_name }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="取餐号">{{ item.food_sn }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="订单详情" span="3">
|
||||||
|
<el-table :data="item.products" style="width: 100%">
|
||||||
|
<el-table-column prop="pid" label="ID" width="60">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="name" label="名称">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="pro_buff" label="属性">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="num" label="数量" width="60">
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="item.back == 0 ? '客户备注' : '退货缘由'" span="3">
|
||||||
|
{{ item.back == 0 ? item.remark != '' ? item.remark : '无' : item.back_remark }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="操作" span="3">
|
||||||
|
<el-button-group v-if="item.back == 1">
|
||||||
|
<el-button type="danger" icon="iconfont icon-zhengque">
|
||||||
|
<font class="m-l-5">同意退款</font>
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="questAss(item.id, 'back', 'zero')" type="warning" icon="iconfont icon-cuowu">
|
||||||
|
<font class="m-l-5">拒绝退款</font>
|
||||||
|
</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
<el-button-group v-else-if="item.back == 0 && item.quest == 0">
|
||||||
|
<el-button @click="questAss(item.id, 'quest', '1')" type="primary" icon="iconfont icon-chengjie">
|
||||||
|
<font class="m-l-5">承接订单</font>
|
||||||
|
</el-button>
|
||||||
|
<el-button type="danger" icon="iconfont icon-cuowu">
|
||||||
|
<font class="m-l-5">取消订单</font>
|
||||||
|
</el-button>
|
||||||
|
<el-button type="info" icon="iconfont icon-dayin">
|
||||||
|
<font class="m-l-5" @click="orderPrint(index)">打印小票</font>
|
||||||
|
</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
<el-button-group v-else-if="item.back == 0 && item.quest == 1 && item.status == 20">
|
||||||
|
<el-button type="danger" icon="iconfont icon-cuowu">
|
||||||
|
<font class="m-l-5">取消订单</font>
|
||||||
|
</el-button>
|
||||||
|
<el-button type="info" icon="iconfont icon-dayin">
|
||||||
|
<font class="m-l-5" @click="orderPrint(index)">打印小票</font>
|
||||||
|
</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-collapse-item>
|
||||||
|
</template>
|
||||||
|
<div v-else class="p-t-60 p-b-60 seatFontColor fc">
|
||||||
|
<i class="iconfont icon-meiyoushuju f-s-100"></i>
|
||||||
|
<div>空空如也</div>
|
||||||
|
</div>
|
||||||
|
</el-collapse>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { parseTime } from '@/utils/index'
|
||||||
|
import { questAss } from '@/utils/api/table'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
list: [],
|
||||||
|
activeNames: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: ['fil'],
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* @description: 获取订单列表 ps:待发货及待收货 并没有发起退款和正在退款状态
|
||||||
|
*/
|
||||||
|
questList () {
|
||||||
|
return this.$store.state.questList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
parseTime, // 时间戳格式化
|
||||||
|
questAss,
|
||||||
|
orderState (status, back, quest) {
|
||||||
|
if (back === 1) {
|
||||||
|
return '申请退款'
|
||||||
|
} else {
|
||||||
|
if (quest === 1) {
|
||||||
|
return '已确认接单'
|
||||||
|
} else {
|
||||||
|
return '未确认订单'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 通过蓝牙打印机并打印小票
|
||||||
|
* @param {number} index 订单列表下标索引
|
||||||
|
*/
|
||||||
|
orderPrint (index) {
|
||||||
|
// 订单文本编辑
|
||||||
|
// console.log(this.list[index])
|
||||||
|
// let con
|
||||||
|
// con = '无人机外卖送餐#' + this.list[index].food_sn + '\n\n'
|
||||||
|
// this.btPrint(con, '10', '1')
|
||||||
|
// con = ''
|
||||||
|
// con += '------------客户信息------------\n'
|
||||||
|
// con += '昵称:' + this.list[index].name + '\n'
|
||||||
|
// const tel = this.list[index].tel.substr(0, 3) + '****' + this.list[index].tel.substr(7)
|
||||||
|
// con += '电话:' + tel + '\n\n'
|
||||||
|
// con += '------------订单信息------------\n'
|
||||||
|
// this.btPrint(con, '0', '0')
|
||||||
|
// con = ''
|
||||||
|
// con += '取餐号:' + this.list[index].food_sn + '\n'
|
||||||
|
// this.btPrint(con, '31', '0')
|
||||||
|
// con = ''
|
||||||
|
// con += '单号:' + this.list[index].order_sn + '\n'
|
||||||
|
// con += '收货站点:' + this.list[index].receive_site_name + '\n'
|
||||||
|
// con += '下单时间:' + this.parseTime(this.list[index].addtime) + '\n\n'
|
||||||
|
// if (this.list[index].remark !== '') {
|
||||||
|
// con += '------------客户备注------------\n'
|
||||||
|
// con += this.list[index].remark + '\n\n'
|
||||||
|
// }
|
||||||
|
// con += '--------------商品--------------\n'
|
||||||
|
// this.list[index].products.forEach(element => {
|
||||||
|
// con += 'ID:' + element.pid + '\n'
|
||||||
|
// con += '品名:' + element.name + '\n'
|
||||||
|
// if (element.pro_buff !== '') {
|
||||||
|
// con += '规格: ' + element.pro_buff + '\n'
|
||||||
|
// }
|
||||||
|
// con += '数量:' + element.num + '\n'
|
||||||
|
// con += '单价:' + element.price + '\n'
|
||||||
|
// con += '················\n'
|
||||||
|
// })
|
||||||
|
// con += '货品总数:' + this.list[index].product_num + '\n'
|
||||||
|
// con += '货品总额:' + this.list[index].price + '\n\n'
|
||||||
|
// con += '---------加盟无人机送餐---------\n'
|
||||||
|
// this.btPrint(con, '0', '0')
|
||||||
|
// con = ''
|
||||||
|
// var data = 'https://flicube.com'
|
||||||
|
// var align = 1
|
||||||
|
// var model = 49
|
||||||
|
// var size = 32
|
||||||
|
// var eclevel = 50
|
||||||
|
// BTPrinter.printQRCode(function (data) {
|
||||||
|
// console.log('Success')
|
||||||
|
// console.log(data)
|
||||||
|
// }, function (err) {
|
||||||
|
// console.log('Error')
|
||||||
|
// console.log(err)
|
||||||
|
// }, data, align, model, size, eclevel)
|
||||||
|
// con += '\n请扫上列二维码关注我们\n\n\n'
|
||||||
|
// this.btPrint(con, '0', '1')
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: cordova 打印api接口
|
||||||
|
* @param {string} str 内容
|
||||||
|
* @param {string} size 字体大小预设
|
||||||
|
* @param {string} algin 对齐
|
||||||
|
*/
|
||||||
|
btPrint (str, size = '0', align = '0') {
|
||||||
|
// BTPrinter.printTitle(function (data) {
|
||||||
|
// console.log('Success')
|
||||||
|
// console.log(data)
|
||||||
|
// }, function (err) {
|
||||||
|
// console.log('Error')
|
||||||
|
// console.log(err)
|
||||||
|
// }, str, size, align)// string, size, align
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 按父组件的fil参数 条件 显示列表
|
||||||
|
* @param {*} list 要处理的列表
|
||||||
|
*/
|
||||||
|
computeQuestList () {
|
||||||
|
if (this.fil === 2) {
|
||||||
|
this.list = this.questList.filter(item => item.back === '1')
|
||||||
|
} else if (this.fil === 0) {
|
||||||
|
this.list = this.questList.filter(item => item.back === '0' && item.quest === '0' && item.status === '20')
|
||||||
|
} else if (this.fil === 1) {
|
||||||
|
this.list = this.questList.filter(item => item.back === '0' && item.quest === '1' && item.status === '20')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
questList () {
|
||||||
|
this.computeQuestList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.computeQuestList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
43
src/components/Selection.vue
Normal file
43
src/components/Selection.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<el-select v-if="adminList.length > 1" v-model="form.shop_id" filterable placeholder="所有客户" @input="updateShopId">
|
||||||
|
<el-option label="所有客户" value="">
|
||||||
|
<span class="l">All</span>
|
||||||
|
<span style="float: right; color: #8492a6; font-size: 13px">所有客户</span>
|
||||||
|
</el-option>
|
||||||
|
<el-option v-for="item in adminList" :key="item.id" :label="item.uname" :value="item.shop_id">
|
||||||
|
<span style="float: left">{{ item.name }}</span>
|
||||||
|
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.uname }}</span>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Selection',
|
||||||
|
props: {
|
||||||
|
value: String // 接收父级的值作为 prop
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
shop_id: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 获取管理员列表
|
||||||
|
adminList () {
|
||||||
|
return this.$store.state.adminList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
adminList () {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateShopId () {
|
||||||
|
this.$emit('input', this.form.shop_id) // 发送 input 事件给父级
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
89
src/components/Tag/HeartTag.vue
Normal file
89
src/components/Tag/HeartTag.vue
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<el-row type="flex" justify="space-around">
|
||||||
|
<el-col :span="8" class="fb f-s-14 fc">
|
||||||
|
{{ online ? showState : '离线' }}
|
||||||
|
</el-col>
|
||||||
|
<el-col :class="online ? heartAnimation ? 'icon-heart online' : 'icon-heart1 online' : 'icon-xinsui offline'"
|
||||||
|
class="iconfont f-s-26 fc" :span="8"></el-col>
|
||||||
|
<el-col :span="8" class="fb f-s-14 fc">
|
||||||
|
{{ getPlaneMode }}
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HeartTag',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
heartAnimation: false, // 控制心跳动画图标
|
||||||
|
online: false,
|
||||||
|
isOnlineSetTimeout: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
heartBeat: {
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
heartRandom: {
|
||||||
|
default: null,
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
getPlaneMode: {
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 显示状态
|
||||||
|
showState () {
|
||||||
|
let str = ''
|
||||||
|
if (Number(this.heartBeat) & 128) {
|
||||||
|
str += '已解'
|
||||||
|
} else {
|
||||||
|
str += '已锁'
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
heartRandom: {
|
||||||
|
handler (val) {
|
||||||
|
// 心跳动画
|
||||||
|
this.heartAnimation = true
|
||||||
|
setTimeout(() => {
|
||||||
|
this.heartAnimation = false
|
||||||
|
}, 500)
|
||||||
|
// 在线状态
|
||||||
|
if (this.isOnlineSetTimeout) { // 进入本次心跳 删除掉线计时 既以下会重新计时
|
||||||
|
clearInterval(this.isOnlineSetTimeout)
|
||||||
|
}
|
||||||
|
this.online = true
|
||||||
|
this.isOnlineSetTimeout = setTimeout(() => { // 计时10秒后 掉线
|
||||||
|
this.online = false
|
||||||
|
}, 10000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@/styles/theme.scss";
|
||||||
|
|
||||||
|
.el-row {
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 36px;
|
||||||
|
line-height: 36px;
|
||||||
|
background-color: rgba($color: $white-color, $alpha: 0.5);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.online {
|
||||||
|
background-color: rgba($color: $success-color, $alpha: 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.offline {
|
||||||
|
background-color: rgba($color: $danger-color, $alpha: 0.5);
|
||||||
|
}
|
||||||
|
</style>
|
73
src/components/Tag/PublicTag.vue
Normal file
73
src/components/Tag/PublicTag.vue
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<el-row class="p-r-10">
|
||||||
|
<el-col :class="state === 'danger' ? `${icon} dangerBG` : `${icon} normalBG`" class="iconfont f-s-22 fc "
|
||||||
|
:span="9"></el-col>
|
||||||
|
<el-col :span="unit !== '' ? 9 : 15" class="fb f-s-14 fr">
|
||||||
|
{{ val }}
|
||||||
|
</el-col>
|
||||||
|
<el-col v-if="unit !== ''" :span="6" class="fb f-s-14 fr">
|
||||||
|
{{ unit }}
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PublicTag',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
val: {
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
unit: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
type: String,
|
||||||
|
default: 'normal',
|
||||||
|
required: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@/styles/theme.scss";
|
||||||
|
|
||||||
|
.el-row {
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 36px;
|
||||||
|
line-height: 36px;
|
||||||
|
background-color: rgba($color: $graylight-color, $alpha: 0.5);
|
||||||
|
max-width: 185px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.el-col {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.normalBG {
|
||||||
|
background-color: rgba($color: $success-color, $alpha: 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dangerBG {
|
||||||
|
background-color: rgba($color: $danger-color, $alpha: 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
25
src/main.js
Normal file
25
src/main.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
import store from './store'
|
||||||
|
import '@/permission' // 路由守卫
|
||||||
|
import 'normalize.css/normalize.css'// A modern alternative to CSS resets
|
||||||
|
import '@/styles/index.scss'// 全局 css
|
||||||
|
import '@/styles/myIcon.scss'// 阿里巴巴图标
|
||||||
|
import 'mapbox-gl/dist/mapbox-gl.css'
|
||||||
|
|
||||||
|
// ElementUI
|
||||||
|
import ElementUI from 'element-ui'
|
||||||
|
import 'element-ui/lib/theme-chalk/index.css'
|
||||||
|
import 'element-ui/lib/theme-chalk/base.css'// fade/zoom 等
|
||||||
|
import locale from 'element-ui/lib/locale/lang/zh-CN' // 引入中文语言包
|
||||||
|
Vue.use(ElementUI, { locale })
|
||||||
|
|
||||||
|
// 设置vue创建时 提示信息 false不提示
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
router,
|
||||||
|
store,
|
||||||
|
render: h => h(App)
|
||||||
|
}).$mount('#app')
|
56
src/permission.js
Normal file
56
src/permission.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import router from './router'
|
||||||
|
import store from './store'
|
||||||
|
import NProgress from 'nprogress' // 进度条
|
||||||
|
import 'nprogress/nprogress.css' // 进度条 style
|
||||||
|
|
||||||
|
NProgress.configure({ showSpinner: false }) // 进度条 Configuration
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 路由守卫
|
||||||
|
*/
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
// start 进度条
|
||||||
|
NProgress.start()
|
||||||
|
// 设置title
|
||||||
|
document.title = getPageTitle(to.meta.title)
|
||||||
|
// 路由
|
||||||
|
store.commit('user/initUser')// 用户信息初始化 PS:token不存在 执行且执行一次
|
||||||
|
const token = store.state.user.token
|
||||||
|
if (token === null) {
|
||||||
|
if (to.path === '/login') {
|
||||||
|
next()
|
||||||
|
NProgress.done()
|
||||||
|
} else {
|
||||||
|
next('/login')
|
||||||
|
NProgress.done()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (to.path === '/login') {
|
||||||
|
next('/')
|
||||||
|
NProgress.done()
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
NProgress.done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 路由完成 结束进度条
|
||||||
|
*/
|
||||||
|
router.afterEach(() => {
|
||||||
|
NProgress.done() // 结束进度条
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: mateTile 和 全局title 组合
|
||||||
|
* @return: title
|
||||||
|
* @param {*} pageTitle 路由mate.tiele
|
||||||
|
*/
|
||||||
|
function getPageTitle (pageTitle) {
|
||||||
|
if (pageTitle) {
|
||||||
|
return `${pageTitle} - ${store.state.settings.title}`
|
||||||
|
} else {
|
||||||
|
return store.state.settings.title
|
||||||
|
}
|
||||||
|
}
|
167
src/router/index.js
Normal file
167
src/router/index.js
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import VueRouter from 'vue-router'
|
||||||
|
import Login from '@/views/login'
|
||||||
|
import Page404 from '@/views/404'
|
||||||
|
import Layout from '@/views/layout/index'
|
||||||
|
|
||||||
|
Vue.use(VueRouter)
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'Login',
|
||||||
|
component: Login,
|
||||||
|
meta: { title: '登录' },
|
||||||
|
roles: ['admin', 'editor'],
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/404',
|
||||||
|
name: 'Page404',
|
||||||
|
component: Page404,
|
||||||
|
meta: { title: '404' },
|
||||||
|
roles: ['admin', 'editor'],
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
redirect: '/home/index',
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/home',
|
||||||
|
component: Layout,
|
||||||
|
meta: { title: '概况', icon: 'iconfont icon-fuwudiqiu' },
|
||||||
|
redirect: '/home/index',
|
||||||
|
roles: ['admin', 'editor'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/home/index',
|
||||||
|
name: 'Home',
|
||||||
|
component: () => import('@/views/layout/components/main/home/index'),
|
||||||
|
meta: { title: '全图概况', icon: 'iconfont icon-fuwudiqiu' },
|
||||||
|
roles: ['admin', 'editor']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/register',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/register/index',
|
||||||
|
meta: { title: '飞机管理', icon: 'el-icon-edit-outline' },
|
||||||
|
roles: ['admin', 'editor'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/register/index',
|
||||||
|
component: () => import('@/views/layout/components/main/register/index'),
|
||||||
|
meta: { title: '飞机列表', icon: 'el-icon-tickets' },
|
||||||
|
roles: ['admin', 'editor']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/register/add/',
|
||||||
|
component: () => import('@/views/layout/components/main/register/add'),
|
||||||
|
meta: { title: '添加飞机', icon: 'el-icon-plus' },
|
||||||
|
roles: ['admin', 'editor']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/register/edit/:id',
|
||||||
|
component: () => import('@/views/layout/components/main/register/add'),
|
||||||
|
meta: { title: '更新飞机', icon: 'el-icon-edit' },
|
||||||
|
roles: ['admin', 'editor'],
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/register/crosFrequency',
|
||||||
|
component: () => import('@/views/layout/components/main/register/crosFrequency'),
|
||||||
|
meta: { title: '飞机对频', icon: 'el-icon-link' },
|
||||||
|
roles: ['admin', 'editor']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/route',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/route/index',
|
||||||
|
meta: { title: '航线管理', icon: 'iconfont icon-feihangluxian' },
|
||||||
|
roles: ['admin', 'editor'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/route/index',
|
||||||
|
component: () => import('@/views/layout/components/main/route/index'),
|
||||||
|
meta: { title: '航线列表', icon: 'iconfont icon-a-05-1-1jihuazhihanggenzong' },
|
||||||
|
roles: ['admin', 'editor']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/route/add/',
|
||||||
|
component: () => import('@/views/layout/components/main/route/add'),
|
||||||
|
meta: { title: '设计航线', icon: 'iconfont icon-huizhi' },
|
||||||
|
roles: ['admin', 'editor']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/route/edit/:id',
|
||||||
|
component: () => import('@/views/layout/components/main/route/add'),
|
||||||
|
meta: { title: '编辑航线', icon: 'iconfont icon-huizhi' },
|
||||||
|
roles: ['admin', 'editor'],
|
||||||
|
hidden: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/site',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/site/index',
|
||||||
|
meta: { title: '站点管理', icon: 'iconfont icon-zhandianguanli' },
|
||||||
|
roles: ['admin', 'editor'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/site/index',
|
||||||
|
component: () => import('@/views/layout/components/main/site/index'),
|
||||||
|
meta: { title: '站点列表', icon: 'el-icon-tickets' },
|
||||||
|
roles: ['admin', 'editor']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/site/add/',
|
||||||
|
component: () => import('@/views/layout/components/main/site/add'),
|
||||||
|
meta: { title: '添加站点', icon: 'el-icon-plus' },
|
||||||
|
roles: ['admin', 'editor']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/site/edit/:id',
|
||||||
|
component: () => import('@/views/layout/components/main/site/add'),
|
||||||
|
meta: { title: '更新站点', icon: 'el-icon-edit' },
|
||||||
|
roles: ['admin', 'editor'],
|
||||||
|
hidden: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/planes',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/planes/index',
|
||||||
|
meta: { title: '无人机', icon: 'iconfont icon-wurenji' },
|
||||||
|
roles: ['admin', 'editor'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/planes/index/:id/:name', // 动态加载路由时加ID参数
|
||||||
|
name: 'Planes',
|
||||||
|
component: () => import('@/views/layout/components/main/planes/index'),
|
||||||
|
meta: { title: '飞机加载中', icon: 'el-icon-loading' },
|
||||||
|
roles: ['admin', 'editor']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '*',
|
||||||
|
redirect: '/404',
|
||||||
|
roles: ['admin', 'editor'],
|
||||||
|
hidden: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = new VueRouter({
|
||||||
|
mode: 'history',
|
||||||
|
base: process.env.BASE_URL,
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
30
src/settings.js
Normal file
30
src/settings.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
module.exports = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 全局title
|
||||||
|
*/
|
||||||
|
title: '送餐系统',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: api服务器
|
||||||
|
* host 主站
|
||||||
|
* api接口地址
|
||||||
|
* api常规接口路径
|
||||||
|
* api登录接口路径
|
||||||
|
*/
|
||||||
|
host: 'https://szdot.top',
|
||||||
|
baseURL: 'https://szdot.top/flycube.php',
|
||||||
|
apiPath: '/mpApi/Index/',
|
||||||
|
apiLoginPath: '/mpApi/Login/',
|
||||||
|
/**
|
||||||
|
* @description: mqtt服务器
|
||||||
|
* mqttHost 地址
|
||||||
|
* mqttPort 端口
|
||||||
|
* mqttUserName 用户名
|
||||||
|
* mqttPassword 密码
|
||||||
|
*/
|
||||||
|
mqttHost: 'wss://szdot.top',
|
||||||
|
mqttPort: 8083,
|
||||||
|
mqttUserName: 'admin',
|
||||||
|
mqttPassword: '123456'
|
||||||
|
}
|
410
src/store/index.js
Normal file
410
src/store/index.js
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
import app from './modules/app'
|
||||||
|
import settings from './modules/settings'
|
||||||
|
import user from './modules/user'
|
||||||
|
import api from '@/utils/api'
|
||||||
|
import { Message, MessageBox } from 'element-ui'
|
||||||
|
|
||||||
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
const store = new Vuex.Store({
|
||||||
|
state: {
|
||||||
|
adminList: [], // 管理员列表
|
||||||
|
airList: [], // 所有飞机列表
|
||||||
|
siteList: [], // 站点列表
|
||||||
|
routeList: [], // 航线列表
|
||||||
|
questList: [], // 订单列表
|
||||||
|
logList: [], // 操作日志列表
|
||||||
|
crosFrequency: null // 对频macadd
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
/**
|
||||||
|
* @description: 设置管理员列表
|
||||||
|
*/
|
||||||
|
setAdminList (state, list) {
|
||||||
|
state.adminList = list
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 设置飞机列表
|
||||||
|
*/
|
||||||
|
setAirList (state, list) {
|
||||||
|
state.airList = list
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 设置站点列表
|
||||||
|
*/
|
||||||
|
setSiteList (state, list) {
|
||||||
|
state.siteList = list
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 设置航线列表
|
||||||
|
*/
|
||||||
|
setRouteList (state, list) {
|
||||||
|
state.routeList = list
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 设置订单列表
|
||||||
|
*/
|
||||||
|
setQuestList (state, list) {
|
||||||
|
state.questList = list
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 向操作日志列表最后插入新的信息
|
||||||
|
* @param {*} logData
|
||||||
|
*/
|
||||||
|
insertNewLog (state, logData) {
|
||||||
|
state.logList.push(logData)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 登记对频信息
|
||||||
|
*/
|
||||||
|
setCrosFrequency (state, macAdd) {
|
||||||
|
state.crosFrequency = macAdd
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
/**
|
||||||
|
* @description: 获取管理员列表
|
||||||
|
*/
|
||||||
|
async fetchAdminList ({ commit }) {
|
||||||
|
const res = await api.get('getAdminList')
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
commit('setAdminList', res.data.adminList)
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 获取飞机列表
|
||||||
|
*/
|
||||||
|
async fetchAirList ({ commit, state }) {
|
||||||
|
const res = await api.get('getAirList')
|
||||||
|
res.data.airList.forEach(plane => {
|
||||||
|
plane.planeState = { // 飞机状态初始化字段
|
||||||
|
heartBeat: null, // 心跳
|
||||||
|
heartRandom: null, // 每次接收到心跳创建一个随机数 用于watch监听
|
||||||
|
voltagBattery: null, // 电压信息
|
||||||
|
currentBattery: null, // 电流信息
|
||||||
|
batteryRemaining: null, // 电池电量
|
||||||
|
positionAlt: null, // 高度信息
|
||||||
|
groundSpeed: null, // 对地速度
|
||||||
|
satCount: null, // 卫星数量
|
||||||
|
latitude: null, // 纬度
|
||||||
|
longitude: null, // 经度
|
||||||
|
fixType: null, // 定位状态
|
||||||
|
state: 1, // 飞机状态 默认初始状态为1
|
||||||
|
pingNet: null, // 网速测试
|
||||||
|
getPlaneMode: null, // 飞机模式
|
||||||
|
loadweight: null, // 重量
|
||||||
|
hookstatus: null, // 钩子状态
|
||||||
|
position: []// [[经度,维度,海拔高度]]累计数组
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
commit('setAirList', res.data.airList)
|
||||||
|
} else {
|
||||||
|
commit('setSiteList', [])
|
||||||
|
Message.warning(res.data.msg)
|
||||||
|
}
|
||||||
|
return res.data.airList
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 创建新飞机
|
||||||
|
* @param {*} form 表单.飞机信息
|
||||||
|
* @return {*} 服务器返回信息
|
||||||
|
*/
|
||||||
|
async fetchAddAir ({ dispatch }, form) {
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
params.append('shop_id', form.shop_id)
|
||||||
|
params.append('name', form.name)
|
||||||
|
params.append('date', form.date)
|
||||||
|
if (form.state) {
|
||||||
|
params.append('state', '1')
|
||||||
|
} else {
|
||||||
|
params.append('state', '0')
|
||||||
|
}
|
||||||
|
params.append('desc', form.desc)
|
||||||
|
const res = await api.post('addAir', params)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
await dispatch('fetchAirList')// 刷新飞机列表
|
||||||
|
Message.success(res.data.msg)
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 更新新飞机数据
|
||||||
|
* @param {*} form 表单.飞机信息
|
||||||
|
* @return {*} 服务器返回信息
|
||||||
|
*/
|
||||||
|
async fetchSaveAir ({ dispatch }, form) {
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
params.append('shop_id', form.shop_id)
|
||||||
|
params.append('name', form.name)
|
||||||
|
params.append('date', form.date)
|
||||||
|
if (form.state) {
|
||||||
|
params.append('state', '1')
|
||||||
|
} else {
|
||||||
|
params.append('state', '0')
|
||||||
|
}
|
||||||
|
params.append('desc', form.desc)
|
||||||
|
params.append('id', form.id)
|
||||||
|
const res = await api.post('saveAir', params)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
await dispatch('fetchAirList')// 刷新飞机列表
|
||||||
|
Message.success(res.data.msg)
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 删除指定序号飞机
|
||||||
|
* @param {*} idArray 飞机序号数组
|
||||||
|
*/
|
||||||
|
fetchDelAir ({ dispatch }, idArr) {
|
||||||
|
if (idArr.length === 0) {
|
||||||
|
Message.error('未勾选记录')
|
||||||
|
} else {
|
||||||
|
MessageBox.confirm('请谨慎操作 确认删除?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
params.append('idArr', idArr)
|
||||||
|
api.post('deleteAir', params).then(res => {
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
Message.success(res.data.msg)
|
||||||
|
dispatch('fetchAirList')// 刷新飞机列表
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
Message.info('取消操作')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 获取站点列表
|
||||||
|
*/
|
||||||
|
async fetchSiteList ({ commit }) {
|
||||||
|
const res = await api.get('getSiteList')
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
commit('setSiteList', res.data.siteList)
|
||||||
|
} else {
|
||||||
|
commit('setSiteList', [])
|
||||||
|
Message.warning(res.data.msg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 锁定站点
|
||||||
|
* @param {*} form 表单.站点信息
|
||||||
|
* @return {*} 服务器返回信息
|
||||||
|
*/
|
||||||
|
async fetchLockSite ({ dispatch }, form) {
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
params.append('id', form.id)
|
||||||
|
params.append('runing', form.runing)
|
||||||
|
const res = await api.post('lockSite', params)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
await dispatch('fetchSiteList')// 刷新站点列表
|
||||||
|
Message.success(res.data.msg)
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 创建新站点
|
||||||
|
* @param {*} form 表单.站点信息
|
||||||
|
* @return {*} 服务器返回信息
|
||||||
|
*/
|
||||||
|
async fetchAddSite ({ dispatch }, form) {
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
params.append('shop_id', form.shop_id)
|
||||||
|
params.append('sitename', form.sitename)
|
||||||
|
params.append('desc', form.desc)
|
||||||
|
params.append('upFile', form.upFile)
|
||||||
|
params.append('size', form.size)
|
||||||
|
params.append('bindroute', form.bindroute)
|
||||||
|
const res = await api.post('addSite', params)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
await dispatch('fetchSiteList')// 刷新站点列表
|
||||||
|
Message.success(res.data.msg)
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 创建新站点
|
||||||
|
* @param {*} form 表单.站点信息
|
||||||
|
* @return {*} 服务器返回信息
|
||||||
|
*/
|
||||||
|
async fetchSaveSite ({ dispatch }, form) {
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
params.append('id', form.id)
|
||||||
|
params.append('shop_id', form.shop_id)
|
||||||
|
params.append('sitename', form.sitename)
|
||||||
|
params.append('desc', form.desc)
|
||||||
|
params.append('upFile', form.upFile)
|
||||||
|
params.append('size', form.size)
|
||||||
|
params.append('bindroute', form.bindroute)
|
||||||
|
const res = await api.post('saveSite', params)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
await dispatch('fetchSiteList')// 刷新站点列表
|
||||||
|
Message.success(res.data.msg)
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 删除指定序号站点
|
||||||
|
* @param {*} idArray 站点序号数组
|
||||||
|
*/
|
||||||
|
fetchDelSite ({ dispatch }, idArr) {
|
||||||
|
if (idArr.length === 0) {
|
||||||
|
Message.error('未勾选记录')
|
||||||
|
} else {
|
||||||
|
MessageBox.confirm('请谨慎操作 确认删除?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
params.append('idArr', idArr)
|
||||||
|
api.post('deleteSite', params).then(res => {
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
Message.success(res.data.msg)
|
||||||
|
dispatch('fetchSiteList')// 刷新站点列表
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
Message.info('取消操作')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 获取航线列表
|
||||||
|
*/
|
||||||
|
async fetchRouteList ({ commit }) {
|
||||||
|
const res = await api.get('getRouteList')
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
commit('setRouteList', res.data.routeList)
|
||||||
|
} else {
|
||||||
|
Message.warning(res.data.msg)
|
||||||
|
}
|
||||||
|
return res.data.routeList
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 创建新航线
|
||||||
|
* @param {*} form 表单.航线信息
|
||||||
|
* @return {*} 服务器返回信息
|
||||||
|
*/
|
||||||
|
async fetchAddRoute ({ dispatch }, form) {
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
params.append('shop_id', form.shop_id)
|
||||||
|
params.append('name', form.name)
|
||||||
|
params.append('desc', form.desc)
|
||||||
|
params.append('upFile', form.upFile)
|
||||||
|
const res = await api.post('addRoute', params)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
await dispatch('fetchRouteList')// 刷新站点列表
|
||||||
|
Message.success(res.data.msg)
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 更新航线
|
||||||
|
* @param {*} form 表单.航线信息
|
||||||
|
* @return {*} 服务器返回信息
|
||||||
|
*/
|
||||||
|
async fetchSaveRoute ({ dispatch }, form) {
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
params.append('id', form.id)
|
||||||
|
params.append('shop_id', form.shop_id)
|
||||||
|
params.append('name', form.name)
|
||||||
|
params.append('desc', form.desc)
|
||||||
|
params.append('upFile', form.upFile)
|
||||||
|
const res = await api.post('saveRoute', params)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
await dispatch('fetchRouteList')// 刷新站点列表
|
||||||
|
Message.success(res.data.msg)
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 删除指定序号航线
|
||||||
|
* @param {*} idArray 航线序号数组
|
||||||
|
*/
|
||||||
|
fetchDelRoute ({ dispatch }, idArr) {
|
||||||
|
if (idArr.length === 0) {
|
||||||
|
Message.error('未勾选记录')
|
||||||
|
} else {
|
||||||
|
MessageBox.confirm('请谨慎操作 确认删除?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
params.append('idArr', idArr)
|
||||||
|
api.post('deleteRoute', params).then(res => {
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
Message.success(res.data.msg)
|
||||||
|
dispatch('fetchRouteList')// 刷新航线列表
|
||||||
|
dispatch('fetchSiteList')// 刷新站点列表
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
Message.info('取消操作')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 获取订单列表 ps:待发货及待收货 并且不是退款成功状态
|
||||||
|
* @return {*} 列表
|
||||||
|
*/
|
||||||
|
async fetchQuestList ({ commit }) {
|
||||||
|
const res = await api.get('getQuestList')
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
commit('setQuestList', res.data.questList)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 向操作日志插入信息 并在积攒多条之后 向服务器提交
|
||||||
|
* @param {logData} content 日志内容 logData.color 时间
|
||||||
|
* @param {logData} color 标点颜色 默认#409EFF
|
||||||
|
* @param {logData} timestamp 插入时间 默认当前时间
|
||||||
|
*/
|
||||||
|
fetchLog ({ commit }, logData) {
|
||||||
|
/* 插入日志 */
|
||||||
|
logData.color = typeof logData.color !== 'undefined' ? logData.color : '#409EFF'
|
||||||
|
logData.timestamp = typeof logData.timestamp !== 'undefined' ? logData.color : new Date().getTime()
|
||||||
|
commit('insertNewLog', logData)
|
||||||
|
/* 积攒 向服务器提交 日志 待续写... */
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modules: {
|
||||||
|
app,
|
||||||
|
settings,
|
||||||
|
user
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default store
|
63
src/store/modules/app.js
Normal file
63
src/store/modules/app.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
const state = {
|
||||||
|
mqttState: false, // mqtt连接状态
|
||||||
|
isCollapse: localStorage.getItem('isCollapse') ? !!+localStorage.getItem('isCollapse') : true, // 侧边导航栏 显隐
|
||||||
|
isMobile: null, // 是否是pc端 true电脑端 false为移动端
|
||||||
|
defaultLngLat: null, // 地图默认经纬度
|
||||||
|
defaultZoom: null // 地图默认缩放
|
||||||
|
}
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
// 导航栏 显隐
|
||||||
|
setCollapse () {
|
||||||
|
state.isCollapse = !state.isCollapse
|
||||||
|
if (state.isCollapse) {
|
||||||
|
localStorage.setItem('isCollapse', 1)
|
||||||
|
} else {
|
||||||
|
localStorage.setItem('isCollapse', 0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 判断是否pc端 还是移动端 true电脑端 false移动端
|
||||||
|
setIsMobile () {
|
||||||
|
const userAgentInfo = navigator.userAgent
|
||||||
|
const agents = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod']
|
||||||
|
for (let i = 0; i < agents.length; i++) {
|
||||||
|
if (userAgentInfo.indexOf(agents[i]) > 0) {
|
||||||
|
state.isMobile = false
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
state.isMobile = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 设置地图默认经纬度
|
||||||
|
setDefaultLngLat (state, lngLat) {
|
||||||
|
state.defaultLngLat = lngLat
|
||||||
|
localStorage.setItem('defaultLngLat', JSON.stringify(lngLat))
|
||||||
|
},
|
||||||
|
// 设置地图默认缩放值
|
||||||
|
setDefaultZoom (state, zoom) {
|
||||||
|
state.defaultZoom = zoom
|
||||||
|
localStorage.setItem('defaultZoom', zoom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
}
|
||||||
|
const getters = {
|
||||||
|
// 获取地图默认经纬度 缓存中没有从 localStorage中获取 也没有设置为0
|
||||||
|
getDefaultLngLat () {
|
||||||
|
return state.defaultLngLat !== null ? state.defaultLngLat : localStorage.getItem('defaultLngLat') !== null ? JSON.parse(localStorage.getItem('defaultLngLat')) : { lng: 0, lat: 0 }
|
||||||
|
},
|
||||||
|
// 获取地图默认缩放值 缓存中没有从 localStorage中获取 也没有设置为1
|
||||||
|
getDefaultZoom () {
|
||||||
|
return state.defaultZoom !== null ? state.defaultZoom : localStorage.getItem('defaultZoom') !== null ? localStorage.getItem('defaultZoom') : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
mutations,
|
||||||
|
actions,
|
||||||
|
getters
|
||||||
|
}
|
38
src/store/modules/settings.js
Normal file
38
src/store/modules/settings.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import defaultSettings from '@/settings'
|
||||||
|
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
host,
|
||||||
|
baseURL,
|
||||||
|
apiPath,
|
||||||
|
apiLoginPath,
|
||||||
|
mqttHost,
|
||||||
|
mqttPort,
|
||||||
|
mqttUserName,
|
||||||
|
mqttPassword
|
||||||
|
} = defaultSettings
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
title: title,
|
||||||
|
host: host,
|
||||||
|
baseURL: baseURL,
|
||||||
|
apiPath: apiPath,
|
||||||
|
apiLoginPath: apiLoginPath,
|
||||||
|
mqttHost: mqttHost,
|
||||||
|
mqttPort: mqttPort,
|
||||||
|
mqttUserName: mqttUserName,
|
||||||
|
mqttPassword: mqttPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
mutations,
|
||||||
|
actions
|
||||||
|
}
|
42
src/store/modules/user.js
Normal file
42
src/store/modules/user.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state: {
|
||||||
|
token: null,
|
||||||
|
name: null,
|
||||||
|
uname: null,
|
||||||
|
photo: null,
|
||||||
|
shop_id: null,
|
||||||
|
power: null
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
// 用户信息初始化
|
||||||
|
initUser (state) {
|
||||||
|
if (state.token == null) {
|
||||||
|
if (localStorage.getItem('token') != null) {
|
||||||
|
state.token = localStorage.getItem('token')
|
||||||
|
state.name = localStorage.getItem('name')
|
||||||
|
state.uname = localStorage.getItem('uname')
|
||||||
|
state.photo = localStorage.getItem('photo')
|
||||||
|
state.shop_id = localStorage.getItem('shop_id')
|
||||||
|
state.power = localStorage.getItem('power')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 清除用户信息
|
||||||
|
destroyUser (state) {
|
||||||
|
state.token = null
|
||||||
|
state.name = null
|
||||||
|
state.uname = null
|
||||||
|
state.photo = null
|
||||||
|
state.shop_id = null
|
||||||
|
state.power = null
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
// 异步 清除用户信息
|
||||||
|
async destroyUserAsync (context) {
|
||||||
|
await context.commit('destroyUser')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
src/styles/element-ui.scss
Normal file
67
src/styles/element-ui.scss
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// cover some element-ui styles
|
||||||
|
|
||||||
|
.el-breadcrumb__inner,
|
||||||
|
.el-breadcrumb__inner a {
|
||||||
|
font-weight: 400 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload {
|
||||||
|
input[type="file"] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload__input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// to fixed https://github.com/ElemeFE/element/issues/2461
|
||||||
|
.el-dialog {
|
||||||
|
transform: none;
|
||||||
|
left: 0;
|
||||||
|
position: relative;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
// refine element ui upload
|
||||||
|
.upload-container {
|
||||||
|
.el-upload {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-upload-dragger {
|
||||||
|
width: 178px !important;
|
||||||
|
height: 178px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dropdown
|
||||||
|
.el-dropdown-menu {
|
||||||
|
a {
|
||||||
|
display: block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// to fix el-date-picker css style
|
||||||
|
.el-range-separator {
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.navbarBadge .is-fixed {
|
||||||
|
right: 30px !important;
|
||||||
|
top: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-drawer__header{
|
||||||
|
margin-bottom:15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__nav .is-top{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.el-tabs__header{
|
||||||
|
margin-bottom: -16px;
|
||||||
|
}
|
302
src/styles/index.scss
Normal file
302
src/styles/index.scss
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
@import "@/styles/theme.scss";
|
||||||
|
@import '@/styles/element-ui.scss';
|
||||||
|
@import '@/styles/transition.scss';
|
||||||
|
|
||||||
|
//通用
|
||||||
|
* {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
color: $maintext-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.router-link-active {
|
||||||
|
color: $maintext-color;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:focus,
|
||||||
|
a:active {
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearfix {
|
||||||
|
&:after {
|
||||||
|
visibility: hidden;
|
||||||
|
display: block;
|
||||||
|
font-size: 0;
|
||||||
|
content: " ";
|
||||||
|
clear: both;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animation {
|
||||||
|
-webkit-transition: all 0.2s ease;
|
||||||
|
-moz-transition: all 0.2s ease;
|
||||||
|
-ms-transition: all 0.2s ease;
|
||||||
|
-o-transition: all 0.2s ease;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
// main-container global css
|
||||||
|
.app-container {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #fefefe;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
//mapboxgl
|
||||||
|
.mapboxgl-ctrl-bottom-left a {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapboxgl-ctrl.mapboxgl-ctrl-attrib {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//public
|
||||||
|
.borderLN {
|
||||||
|
border-top-left-radius: 0px !important;
|
||||||
|
border-bottom-left-radius: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.borderRN {
|
||||||
|
border-top-right-radius: 0px !important;
|
||||||
|
border-bottom-right-radius: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.l {
|
||||||
|
float: left !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.r {
|
||||||
|
float: right !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearB {
|
||||||
|
clear: both !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border {
|
||||||
|
border: 1px solid #DCDFE6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainFontColor {
|
||||||
|
color: #303133 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.normalFontColor {
|
||||||
|
color: #606266 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondaryFontColor {
|
||||||
|
color: #909399 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seatFontColor {
|
||||||
|
color: #C0C4CC !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fr {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vm {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-white {
|
||||||
|
background-color: $white-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-b-n {
|
||||||
|
border-bottom: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-t-n {
|
||||||
|
border-top: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-l-n {
|
||||||
|
border-left: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-r-n {
|
||||||
|
border-right: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-n {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fb {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-row {
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
.el-header {
|
||||||
|
color: $maintext-color;
|
||||||
|
padding: 0px;
|
||||||
|
background-color: $border4-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-main {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//m-l-0 p-l-0 马根 帕丁
|
||||||
|
$position: (
|
||||||
|
"t":"top",
|
||||||
|
"b":"bottom",
|
||||||
|
"l":"left",
|
||||||
|
"r":"right",
|
||||||
|
);
|
||||||
|
$type: (
|
||||||
|
"p":"padding",
|
||||||
|
"m":"margin"
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $item in $type {
|
||||||
|
@each $list in $position {
|
||||||
|
@for $i from 0 through 20 {
|
||||||
|
$val: $i * 1;
|
||||||
|
|
||||||
|
.#{nth($item,1)}-#{nth($list,1)}-#{$val} {
|
||||||
|
#{nth($item,2)}-#{nth($list,2)}: #{$val}px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from 4 through 20 {
|
||||||
|
$val: $i * 5;
|
||||||
|
|
||||||
|
.#{nth($item,1)}-#{nth($list,1)}-#{$val} {
|
||||||
|
#{nth($item,2)}-#{nth($list,2)}: #{$val}px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//m-0 p-0 马根 帕丁
|
||||||
|
$type: (
|
||||||
|
"p":"padding",
|
||||||
|
"m":"margin"
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $item in $type {
|
||||||
|
@for $i from 0 through 100 {
|
||||||
|
$val: $i * 1;
|
||||||
|
|
||||||
|
.#{nth($item,1)}-#{$val} {
|
||||||
|
#{nth($item,2)}: #{$val}px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//w-10 h-10 宽度百分比 高度百分比
|
||||||
|
$type: (
|
||||||
|
"w":"width",
|
||||||
|
"h":"height"
|
||||||
|
);
|
||||||
|
$mark: (
|
||||||
|
"%"
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $item in $type {
|
||||||
|
@for $i from 0 through 10 {
|
||||||
|
$val: $i * 10;
|
||||||
|
|
||||||
|
.#{nth($item,1)}-#{$val} {
|
||||||
|
#{nth($item,2)}: #{$val}#{$mark};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//w-10px h-10px 宽度px 高度px 参数10 20 30...
|
||||||
|
$type: (
|
||||||
|
"w":"width",
|
||||||
|
"h":"height"
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $item in $type {
|
||||||
|
@for $i from 1 through 80 {
|
||||||
|
$val: $i*10;
|
||||||
|
|
||||||
|
.#{nth($item,1)}-#{$val}px {
|
||||||
|
#{nth($item,2)}: #{$val}px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//f-s-12 设置字体大小 1-50 60-200 50之前1递增 60以后10递增
|
||||||
|
$type: (
|
||||||
|
"f-s":"font-size",
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $item in $type {
|
||||||
|
@for $val from 1 through 50 {
|
||||||
|
.#{nth($item,1)}-#{$val} {
|
||||||
|
#{nth($item,2)}: #{$val}px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from 6 through 20 {
|
||||||
|
$val: $i*10;
|
||||||
|
|
||||||
|
.#{nth($item,1)}-#{$val} {
|
||||||
|
#{nth($item,2)}: #{$val}px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//l-h-50 字体行高 12-100 1递增
|
||||||
|
$type: (
|
||||||
|
"l-h":"line-height",
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $item in $type {
|
||||||
|
@for $val from 12 through 100 {
|
||||||
|
.#{nth($item,1)}-#{$val} {
|
||||||
|
#{nth($item,2)}: #{$val}px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
src/styles/myIcon.scss
Normal file
1
src/styles/myIcon.scss
Normal file
@ -0,0 +1 @@
|
|||||||
|
@import 'https://at.alicdn.com/t/c/font_3703467_4teeodr1wrk.css'; //iconfont阿里巴巴
|
41
src/styles/theme.scss
Normal file
41
src/styles/theme.scss
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
//变量主题颜色
|
||||||
|
//主色
|
||||||
|
$brand-color: #409EFF;
|
||||||
|
//辅色
|
||||||
|
$success-color: #67C23A;
|
||||||
|
$warning-color: #E6A23C;
|
||||||
|
$danger-color: #F56C6C;
|
||||||
|
//字体颜色
|
||||||
|
$info-color: #909399;
|
||||||
|
$maintext-color: #303133;
|
||||||
|
$normaltext-color: #606266;
|
||||||
|
$secondarytext-color: #909399;
|
||||||
|
$holdtext-color: #C0C4CC;
|
||||||
|
//边框颜色
|
||||||
|
$border1-color: #DCDFE6;
|
||||||
|
$border2-color: #E4E7ED;
|
||||||
|
$border3-color: #EBEEF5;
|
||||||
|
$border4-color: #F2F6FC;
|
||||||
|
//基础
|
||||||
|
$black-color: #000000;
|
||||||
|
$white-color: #FFFFFF;
|
||||||
|
$graylight-color: #F2F6FC;
|
||||||
|
|
||||||
|
:export {
|
||||||
|
brand-color: $brand-color;
|
||||||
|
success-color: $success-color;
|
||||||
|
warning-color: $warning-color;
|
||||||
|
danger-color: $danger-color;
|
||||||
|
info-color: $info-color;
|
||||||
|
maintext-color: $maintext-color;
|
||||||
|
normaltext-color: $normaltext-color;
|
||||||
|
secondarytext-color: $secondarytext-color;
|
||||||
|
holdtext-color: $holdtext-color;
|
||||||
|
border1-color: $border1-color;
|
||||||
|
border2-color: $border2-color;
|
||||||
|
border3-color: $border3-color;
|
||||||
|
border4-color: $border4-color;
|
||||||
|
black-color: $black-color;
|
||||||
|
white-color: $white-color;
|
||||||
|
graylight-color: $graylight-color;
|
||||||
|
}
|
48
src/styles/transition.scss
Normal file
48
src/styles/transition.scss
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// global transition css
|
||||||
|
|
||||||
|
/* fade */
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: opacity 0.28s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter,
|
||||||
|
.fade-leave-active {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fade-transform */
|
||||||
|
.fade-transform-leave-active,
|
||||||
|
.fade-transform-enter-active {
|
||||||
|
transition: all .5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-transform-enter {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-transform-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* breadcrumb transition */
|
||||||
|
.breadcrumb-enter-active,
|
||||||
|
.breadcrumb-leave-active {
|
||||||
|
transition: all .5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-enter,
|
||||||
|
.breadcrumb-leave-active {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-move {
|
||||||
|
transition: all .5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-leave-active {
|
||||||
|
position: absolute;
|
||||||
|
}
|
68
src/utils/api/index.js
Normal file
68
src/utils/api/index.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import router from '@/router'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
|
// axios缺省设置
|
||||||
|
axios.defaults.baseURL = store.state.settings.baseURL
|
||||||
|
// axios.defaults.headers.post['Content-Type'] = 'application/json'
|
||||||
|
export default {
|
||||||
|
get,
|
||||||
|
post
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 异步 get方法 访问api
|
||||||
|
* @return: json.data对象
|
||||||
|
* @param {string} controller 控制器名称
|
||||||
|
* @param {bool} islogin 默认为index路径 否则访问login路径
|
||||||
|
*/
|
||||||
|
async function get (controller, islogin = false) {
|
||||||
|
const path = init(islogin)
|
||||||
|
try {
|
||||||
|
const res = await axios.get(path + controller)
|
||||||
|
if (res.data.status === -1) { // 权限过期
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
router.go(0)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching data:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 异步 post方法 访问api
|
||||||
|
* @return: json.data对象
|
||||||
|
* @param {string} controller 控制器名称
|
||||||
|
* @param {obj} params post参数
|
||||||
|
* @param {bool} islogin 默认为index路径 否则访问login路径
|
||||||
|
*/
|
||||||
|
async function post (controller, params, islogin = false) {
|
||||||
|
const path = init(islogin)
|
||||||
|
try {
|
||||||
|
const res = await axios.post(path + controller, params)
|
||||||
|
if (res.data.status === -1) { // 权限过期
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
router.go(0)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching data:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 判断访问常规还是登录接口路径 设置token头文件
|
||||||
|
* @return {*} 接口路径
|
||||||
|
* @param {bool} islogin
|
||||||
|
*/
|
||||||
|
function init (islogin) {
|
||||||
|
let path
|
||||||
|
if (islogin) {
|
||||||
|
axios.defaults.headers.common.Token = 'login'
|
||||||
|
path = store.state.settings.apiLoginPath
|
||||||
|
} else {
|
||||||
|
axios.defaults.headers.common.Token = store.state.user.token
|
||||||
|
path = store.state.settings.apiPath
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
38
src/utils/api/table.js
Normal file
38
src/utils/api/table.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import store from '@/store'
|
||||||
|
import api from '@/utils/api'
|
||||||
|
// import { Message, MessageBox } from 'element-ui'
|
||||||
|
import { Message } from 'element-ui'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 向对频api 提交数据
|
||||||
|
*/
|
||||||
|
export async function apiCrosFrequency (params) {
|
||||||
|
const data = new URLSearchParams()// post对象参数 转成 字符串连接
|
||||||
|
data.append('macAdd', params.macAdd)
|
||||||
|
data.append('id', params.id)
|
||||||
|
const res = await api.post('crosFrequency', data)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
Message.success(res.data.msg)
|
||||||
|
store.dispatch('fetchAirList')// 更新飞机列表
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 向改变订单承接任务api 提交数据 ()
|
||||||
|
* @param {*} id 订单id
|
||||||
|
* @param {*} state "quest"修改quest字段 "status"修改status字段
|
||||||
|
*/
|
||||||
|
export function questAss (id, state, val) {
|
||||||
|
const data = new URLSearchParams()// post对象参数 转成 字符串连接
|
||||||
|
data.append('id', id)
|
||||||
|
data.append('state', state)
|
||||||
|
data.append('val', val)
|
||||||
|
api.post('questAss', data).then(res => {
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
Message.success(res.data.msg)
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
24
src/utils/api/user.js
Normal file
24
src/utils/api/user.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import api from '@/utils/api'
|
||||||
|
import router from '@/router'
|
||||||
|
import { Message } from 'element-ui'
|
||||||
|
|
||||||
|
export function login (params) {
|
||||||
|
const data = new URLSearchParams()// post对象参数 转成 字符串连接
|
||||||
|
data.append('username', params.username)
|
||||||
|
data.append('password', params.password)
|
||||||
|
api.post('login', data, true).then(
|
||||||
|
res => {
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
localStorage.setItem('token', res.data.token)
|
||||||
|
localStorage.setItem('name', res.data.adminInfo.name)
|
||||||
|
localStorage.setItem('uname', res.data.adminInfo.uname)
|
||||||
|
localStorage.setItem('photo', res.data.adminInfo.photo)
|
||||||
|
localStorage.setItem('shop_id', res.data.adminInfo.shop_id)
|
||||||
|
localStorage.setItem('power', res.data.adminInfo.power)
|
||||||
|
router.replace('/')
|
||||||
|
} else {
|
||||||
|
Message.error(res.data.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
131
src/utils/index.js
Normal file
131
src/utils/index.js
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/**
|
||||||
|
* Parse the time to string
|
||||||
|
* @param {(Object|string|number)} time
|
||||||
|
* @param {string} cFormat
|
||||||
|
* @returns {string | null}
|
||||||
|
*/
|
||||||
|
export function parseTime (time, cFormat) {
|
||||||
|
if (arguments.length === 0 || !time) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||||
|
let date
|
||||||
|
if (typeof time === 'object') {
|
||||||
|
date = time
|
||||||
|
} else {
|
||||||
|
if ((typeof time === 'string')) {
|
||||||
|
if ((/^[0-9]+$/.test(time))) {
|
||||||
|
time = parseInt(time)
|
||||||
|
} else {
|
||||||
|
time = time.replace(new RegExp(/-/gm), '/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
||||||
|
time = time * 1000
|
||||||
|
}
|
||||||
|
date = new Date(time)
|
||||||
|
}
|
||||||
|
const formatObj = {
|
||||||
|
y: date.getFullYear(),
|
||||||
|
m: date.getMonth() + 1,
|
||||||
|
d: date.getDate(),
|
||||||
|
h: date.getHours(),
|
||||||
|
i: date.getMinutes(),
|
||||||
|
s: date.getSeconds(),
|
||||||
|
a: date.getDay()
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
|
||||||
|
const value = formatObj[key]
|
||||||
|
// Note: getDay() returns 0 on Sunday
|
||||||
|
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
|
||||||
|
return value.toString().padStart(2, '0')
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
return time_str
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} time
|
||||||
|
* @param {string} option
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function formatTime (time, option) {
|
||||||
|
if (('' + time).length === 10) {
|
||||||
|
time = parseInt(time) * 1000
|
||||||
|
} else {
|
||||||
|
time = +time
|
||||||
|
}
|
||||||
|
const d = new Date(time)
|
||||||
|
const now = Date.now()
|
||||||
|
|
||||||
|
const diff = (now - d) / 1000
|
||||||
|
|
||||||
|
if (diff < 30) {
|
||||||
|
return '刚刚'
|
||||||
|
} else if (diff < 3600) {
|
||||||
|
// less 1 hour
|
||||||
|
return Math.ceil(diff / 60) + '分钟前'
|
||||||
|
} else if (diff < 3600 * 24) {
|
||||||
|
return Math.ceil(diff / 3600) + '小时前'
|
||||||
|
} else if (diff < 3600 * 24 * 2) {
|
||||||
|
return '1天前'
|
||||||
|
}
|
||||||
|
if (option) {
|
||||||
|
return parseTime(time, option)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
d.getMonth() +
|
||||||
|
1 +
|
||||||
|
'月' +
|
||||||
|
d.getDate() +
|
||||||
|
'日' +
|
||||||
|
d.getHours() +
|
||||||
|
'时' +
|
||||||
|
d.getMinutes() +
|
||||||
|
'分'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} url
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
export function param2Obj (url) {
|
||||||
|
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
|
||||||
|
if (!search) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
const obj = {}
|
||||||
|
const searchArr = search.split('&')
|
||||||
|
searchArr.forEach(v => {
|
||||||
|
const index = v.indexOf('=')
|
||||||
|
if (index !== -1) {
|
||||||
|
const name = v.substring(0, index)
|
||||||
|
const val = v.substring(index + 1, v.length)
|
||||||
|
obj[name] = val
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
export function isExternal (path) {
|
||||||
|
return /^(https?:|mailto:|tel:)/.test(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 统计当前被选中的ID组
|
||||||
|
* @return: 被选中记录 的ID组
|
||||||
|
* @param {*} selection 列表中被选中的记录
|
||||||
|
*/
|
||||||
|
export function countSelIdArr (selection) {
|
||||||
|
const idArr = []
|
||||||
|
selection.map((item) => idArr.push(item.id))
|
||||||
|
return idArr
|
||||||
|
}
|
89
src/utils/mqtt/index.js
Normal file
89
src/utils/mqtt/index.js
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import mqtt from 'mqtt'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
client: null, // mqtt连接 对象
|
||||||
|
url: store.state.settings.mqttHost, // mqtt服务器地址
|
||||||
|
options: { // mqtt服务器 参数配置
|
||||||
|
port: store.state.settings.mqttPort,
|
||||||
|
connectTimeout: 3000,
|
||||||
|
clientId: store.state.user.shop_id,
|
||||||
|
clean: true,
|
||||||
|
keepalive: 60,
|
||||||
|
username: store.state.settings.mqttUserName,
|
||||||
|
password: store.state.settings.mqttPassword
|
||||||
|
},
|
||||||
|
mqttConf,
|
||||||
|
mqttDestroy,
|
||||||
|
doSubscribe,
|
||||||
|
publishFun
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: mqtt对象初始化
|
||||||
|
*/
|
||||||
|
function mqttConf () {
|
||||||
|
if (!this.client) {
|
||||||
|
// 链接mqtt
|
||||||
|
this.client = mqtt.connect(this.url, this.options)
|
||||||
|
// mqtt服务器中断
|
||||||
|
this.client.on('error', () => {
|
||||||
|
store.dispatch('fetchLog', { content: '指令服务器异常中断' })
|
||||||
|
})
|
||||||
|
// 端口之后复连
|
||||||
|
this.client.on('reconnect', () => {
|
||||||
|
store.dispatch('fetchLog', { content: '重新连接指令服务器' })
|
||||||
|
})
|
||||||
|
// 监听断开连接事件
|
||||||
|
this.client.on('offline', () => {
|
||||||
|
store.dispatch('fetchLog', { content: '指令服务器异常中断' })
|
||||||
|
store.state.app.mqttState = false // 标记mqtt链接失败 掉线
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 获取主题消息
|
||||||
|
* @param {string} topic 主题名称
|
||||||
|
* @param {fun} callBack 回调 传一个对象{topic订阅的主题,msg接收到主题的数据} 在父级给获取
|
||||||
|
*/
|
||||||
|
function doSubscribe (topic, callBack) {
|
||||||
|
// 订阅主题
|
||||||
|
this.client.on('connect', () => {
|
||||||
|
if (store.state.app.mqttState === false) {
|
||||||
|
store.dispatch('fetchLog', { content: '成功连接指令服务器' })
|
||||||
|
}
|
||||||
|
store.state.app.mqttState = true // 标记mqtt链接成功 在线
|
||||||
|
// 订阅一个主题
|
||||||
|
this.client.subscribe(topic, { qos: 2 }, err => {
|
||||||
|
if (!err) {
|
||||||
|
store.dispatch('fetchLog', { content: topic + '主题订阅成功' })
|
||||||
|
} else {
|
||||||
|
store.dispatch('fetchLog', { content: topic + '主题订阅失败' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 获取订阅主题的消息
|
||||||
|
this.client.on('message', (topic, message) => {
|
||||||
|
callBack({ topic: topic, msg: message.toString() })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 向指定topic发送消息
|
||||||
|
* @param {*} topic 发送信息到的主题 不包括前缀
|
||||||
|
* @param {*} msg 要发送的信息
|
||||||
|
*/
|
||||||
|
function publishFun (topic, msg) {
|
||||||
|
// topic要保持一致
|
||||||
|
this.client.publish(topic, msg, {
|
||||||
|
qos: 2
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @Description: 销毁mqtt对象
|
||||||
|
*/
|
||||||
|
function mqttDestroy () {
|
||||||
|
if (this.client) {
|
||||||
|
this.client.end() // 离开页面的时候 关闭mqtt连接
|
||||||
|
this.client = null
|
||||||
|
}
|
||||||
|
}
|
254
src/views/404.vue
Normal file
254
src/views/404.vue
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
<template>
|
||||||
|
<div class="wscn-http404-container">
|
||||||
|
<div class="wscn-http404">
|
||||||
|
<div class="pic-404">
|
||||||
|
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
|
||||||
|
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||||
|
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||||
|
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||||
|
</div>
|
||||||
|
<div class="bullshit">
|
||||||
|
<div class="bullshit__oops">页面走丢了!</div>
|
||||||
|
<div class="bullshit__info">All rights reserved
|
||||||
|
<a style="color:#20a0ff" href="https://flicube.com" target="_blank">飞行魔方官网</a>
|
||||||
|
</div>
|
||||||
|
<div class="bullshit__headline">{{ message }}</div>
|
||||||
|
<div class="bullshit__info">Please check that the URL you entered is correct, or click the button below
|
||||||
|
to return to the homepage.</div>
|
||||||
|
<a href="/" class="bullshit__return-home">回首页</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Page404',
|
||||||
|
computed: {
|
||||||
|
message () {
|
||||||
|
return 'The webmaster said that you can not enter this page...'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.wscn-http404-container {
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
position: absolute;
|
||||||
|
top: 40%;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wscn-http404 {
|
||||||
|
position: relative;
|
||||||
|
width: 1200px;
|
||||||
|
padding: 0 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.pic-404 {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
width: 600px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__parent {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__child {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
&.left {
|
||||||
|
width: 80px;
|
||||||
|
top: 17px;
|
||||||
|
left: 220px;
|
||||||
|
opacity: 0;
|
||||||
|
animation-name: cloudLeft;
|
||||||
|
animation-duration: 2s;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mid {
|
||||||
|
width: 46px;
|
||||||
|
top: 10px;
|
||||||
|
left: 420px;
|
||||||
|
opacity: 0;
|
||||||
|
animation-name: cloudMid;
|
||||||
|
animation-duration: 2s;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
animation-delay: 1.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
width: 62px;
|
||||||
|
top: 100px;
|
||||||
|
left: 500px;
|
||||||
|
opacity: 0;
|
||||||
|
animation-name: cloudRight;
|
||||||
|
animation-duration: 2s;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cloudLeft {
|
||||||
|
0% {
|
||||||
|
top: 17px;
|
||||||
|
left: 220px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
20% {
|
||||||
|
top: 33px;
|
||||||
|
left: 188px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
80% {
|
||||||
|
top: 81px;
|
||||||
|
left: 92px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
top: 97px;
|
||||||
|
left: 60px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cloudMid {
|
||||||
|
0% {
|
||||||
|
top: 10px;
|
||||||
|
left: 420px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
20% {
|
||||||
|
top: 40px;
|
||||||
|
left: 360px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
top: 130px;
|
||||||
|
left: 180px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
top: 160px;
|
||||||
|
left: 120px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cloudRight {
|
||||||
|
0% {
|
||||||
|
top: 100px;
|
||||||
|
left: 500px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
20% {
|
||||||
|
top: 120px;
|
||||||
|
left: 460px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
80% {
|
||||||
|
top: 180px;
|
||||||
|
left: 340px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
top: 200px;
|
||||||
|
left: 300px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bullshit {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
width: 300px;
|
||||||
|
padding: 30px 0;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__oops {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 40px;
|
||||||
|
color: #1482f0;
|
||||||
|
opacity: 0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
animation-name: slideUp;
|
||||||
|
animation-duration: 0.5s;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__headline {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 24px;
|
||||||
|
color: #222;
|
||||||
|
font-weight: bold;
|
||||||
|
opacity: 0;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
animation-name: slideUp;
|
||||||
|
animation-duration: 0.5s;
|
||||||
|
animation-delay: 0.1s;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__info {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 21px;
|
||||||
|
color: grey;
|
||||||
|
opacity: 0;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
animation-name: slideUp;
|
||||||
|
animation-duration: 0.5s;
|
||||||
|
animation-delay: 0.2s;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__return-home {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
width: 110px;
|
||||||
|
height: 36px;
|
||||||
|
background: #1482f0;
|
||||||
|
border-radius: 100px;
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
opacity: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 36px;
|
||||||
|
cursor: pointer;
|
||||||
|
animation-name: slideUp;
|
||||||
|
animation-duration: 0.5s;
|
||||||
|
animation-delay: 0.3s;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideUp {
|
||||||
|
0% {
|
||||||
|
transform: translateY(60px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
87
src/views/layout/components/BlogBox.vue
Normal file
87
src/views/layout/components/BlogBox.vue
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<div class="fixed-bottom p-l-5" :class="maxWidth">
|
||||||
|
<el-button @click="handleOpenBlog" class="l p-3" type="text" size="mini" icon="iconfont icon-chuangkoufangda"
|
||||||
|
circle></el-button>
|
||||||
|
<div class="l p-l-10" v-if="newLog">
|
||||||
|
<span class="l m-t-5 m-r-5 logDot" :style="{ background: newLog.color }"></span>
|
||||||
|
<span>{{ newLog.timestamp | parseTime('{h}:{i}:{s}') }}</span>
|
||||||
|
<span class="m-l-10">{{ newLog.content }}</span>
|
||||||
|
</div>
|
||||||
|
<el-drawer title="程序日志" :modal-append-to-body="false" :visible.sync="drawer" direction="btt" size="50%">
|
||||||
|
<el-timeline :reverse="true" class="p-l-50 p-r-50">
|
||||||
|
<el-timeline-item v-for="(activity, index) in log" :key="index" :icon="activity.icon" :type="activity.type"
|
||||||
|
:color="activity.color" :size="activity.size"
|
||||||
|
:timestamp="activity.timestamp | parseTime('{y}-{m}-{d} {h}:{i}:{s}')">
|
||||||
|
{{ activity.content }}
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { parseTime } from '@/utils'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'BlogBox',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
drawer: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
maxWidth () {
|
||||||
|
return this.$store.state.app.isCollapse ? 'min' : 'max'
|
||||||
|
},
|
||||||
|
log () {
|
||||||
|
return this.$store.state.logList
|
||||||
|
},
|
||||||
|
newLog () {
|
||||||
|
return this.$store.state.logList[this.$store.state.logList.length - 1]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleOpenBlog () {
|
||||||
|
this.drawer = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
parseTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.fixed-bottom {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1002;
|
||||||
|
line-height: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background: #F2F6FC;
|
||||||
|
-webkit-box-shadow: 0 0px 4px rgba(0, 21, 41, 0.3);
|
||||||
|
box-shadow: 0 0px 4px rgba(0, 21, 41, 0.3);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.max {
|
||||||
|
width: calc(100% - 210px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.min {
|
||||||
|
width: calc(100% - 60px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logDot {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
254
src/views/layout/components/headbar.vue
Normal file
254
src/views/layout/components/headbar.vue
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 订单音效 -->
|
||||||
|
<audio controls="controls" hidden src="@/assets/sound/newMsg.mp3" ref="newMsg"></audio>
|
||||||
|
<audio controls="controls" hidden src="@/assets/sound/rebackMsg.mp3" ref="rebackMsg"></audio>
|
||||||
|
<!-- menu缩进按钮 -->
|
||||||
|
<div class="w-50px h-50px fc l" id="menuTabB" @click="handleCollapse">
|
||||||
|
<i class="iconfont f-s-26" :class="isCollapse ? 'icon-a-yousuojin3x' : 'icon-a-zuosuojin3x'"></i>
|
||||||
|
</div>
|
||||||
|
<!-- 面包屑 -->
|
||||||
|
<Breadcrumb class="l l-h-50 m-l-5" />
|
||||||
|
<!-- 头像 -->
|
||||||
|
<div class="right-menu m-r-5">
|
||||||
|
<el-dropdown class="avatar-container" trigger="click">
|
||||||
|
<div class="avatar-wrapper">
|
||||||
|
<img :src="avatar + '?imageView2/1/w/80/h/80'" class="user-avatar">
|
||||||
|
<i class="el-icon-caret-bottom" />
|
||||||
|
</div>
|
||||||
|
<el-dropdown-menu slot="dropdown" class="user-dropdown">
|
||||||
|
<el-dropdown-item divided>
|
||||||
|
<RouterLink to="/">
|
||||||
|
主控
|
||||||
|
</RouterLink>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided @click.native="logout">
|
||||||
|
<span style="display:block;">退出登录</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
<!-- 页面刷新 -->
|
||||||
|
<div class="l-h-50 p-r-15 r">
|
||||||
|
<el-button size="small" icon="iconfont icon-shuaxin" @click="refreshPage" circle></el-button>
|
||||||
|
</div>
|
||||||
|
<!-- 订单信息按钮 -->
|
||||||
|
<el-badge :hidden="orderCount == 0 ? true : false" :value="orderCount" class="navbarBadge l-h-50 p-r-15 r">
|
||||||
|
<el-button @click="drawer = true" size="small" :icon="orderIcon" circle></el-button>
|
||||||
|
</el-badge>
|
||||||
|
<el-drawer title="待处理订单" :visible.sync="drawer" size="40%" :append-to-body="true" :modal-append-to-body="false">
|
||||||
|
<el-tabs type="card">
|
||||||
|
<el-tab-pane label="未接订单">
|
||||||
|
<QuestTabs :fil='0' />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="已接订单">
|
||||||
|
<QuestTabs :fil='1' />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="申请退款订单">
|
||||||
|
<QuestTabs :fil='2' />
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-drawer>
|
||||||
|
<!-- mqtt状态灯 -->
|
||||||
|
<div class="l-h-50 p-r-15 r">{{ mqttState === true ? "在线" : "掉线" }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import mqtt from '@/utils/mqtt'
|
||||||
|
import Breadcrumb from '@/components/Breadcrumb'
|
||||||
|
import QuestTabs from '@/components/QuestTabs'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Headbar',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
orderCount: null, // 订单总数
|
||||||
|
rebackCount: null, // 退款订单总数
|
||||||
|
drawer: false,
|
||||||
|
getQuestInterval: null, // 用于销毁时间轴
|
||||||
|
animationTrumpet: true // 喇叭动画
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Breadcrumb,
|
||||||
|
QuestTabs
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* @description: 侧边栏显隐
|
||||||
|
*/
|
||||||
|
isCollapse () {
|
||||||
|
return this.$store.state.app.isCollapse
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: mqtt服务器连接状态
|
||||||
|
*/
|
||||||
|
mqttState () {
|
||||||
|
return this.$store.state.app.mqttState
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 用户头像地址
|
||||||
|
*/
|
||||||
|
avatar () {
|
||||||
|
return this.$store.state.settings.host + this.$store.state.user.photo
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 获取订单列表 ps:待发货及待收货 并没有发起退款和正在退款状态
|
||||||
|
*/
|
||||||
|
questList () {
|
||||||
|
return this.$store.state.questList
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 面包条 订单图标动画
|
||||||
|
* @return {*} 图标样式
|
||||||
|
*/
|
||||||
|
orderIcon () {
|
||||||
|
if (this.orderCount === 0) {
|
||||||
|
return 'iconfont icon-meiyoudingdan-01'
|
||||||
|
} else {
|
||||||
|
if (this.animationTrumpet) {
|
||||||
|
return 'iconfont icon-tongzhi'
|
||||||
|
} else {
|
||||||
|
return 'iconfont icon-jinjidingdan'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* @description: 切换侧边栏 显隐
|
||||||
|
*/
|
||||||
|
handleCollapse () {
|
||||||
|
this.$store.commit('app/setCollapse')
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 计算订单数量 订单变化播放声音 显示在图标右上角小红圈内
|
||||||
|
* @param {*} list 要处理的订单列表
|
||||||
|
*/
|
||||||
|
computeQuestList (list) {
|
||||||
|
if (this.getQuestInterval !== null) {
|
||||||
|
clearInterval(this.getQuestInterval)
|
||||||
|
}
|
||||||
|
const waitShip = list.filter(item => item.status === '20')// 过滤待处理订单
|
||||||
|
if (this.orderCount < waitShip.length && this.orderCount != null) { // 有新的数据 播放提示声音
|
||||||
|
this.playMusic('newMsg')// 新消息 声音播放
|
||||||
|
}
|
||||||
|
this.orderCount = waitShip.length// 待处理订单总数(待发货订单总数)
|
||||||
|
const reBack = list.filter(item => item.back === '1')// 过滤退款订单
|
||||||
|
if (this.rebackCount < reBack.length && this.rebackCount != null) { // 有新的数据 播放提示声音
|
||||||
|
this.playMusic('rebackMsg')// 退款 声音播放
|
||||||
|
}
|
||||||
|
this.rebackCount = reBack.length// 待处理订单总数(待发货订单总数)
|
||||||
|
this.getQuestInterval = setInterval(() => {
|
||||||
|
this.animationTrumpet = !this.animationTrumpet// 面包条订单图标跳动
|
||||||
|
}, 500)
|
||||||
|
},
|
||||||
|
// 新消息 声音播放
|
||||||
|
playMusic (domId) {
|
||||||
|
const audioElement = this.$refs[domId]
|
||||||
|
if (!audioElement) {
|
||||||
|
console.error(`音频元素 ${domId} 不存在`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
audioElement.currentTime = 0 // 从头开始播放
|
||||||
|
audioElement.play()
|
||||||
|
.then(() => {
|
||||||
|
console.log('音频播放成功')
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('音频播放失败', error)
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
audioElement.pause() // 停止
|
||||||
|
}, 11000)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 刷新当前页面
|
||||||
|
*/
|
||||||
|
refreshPage () {
|
||||||
|
mqtt.mqttDestroy()// 断开mqtt
|
||||||
|
this.$router.go(0)// 刷新页面
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 登出
|
||||||
|
*/
|
||||||
|
logout () {
|
||||||
|
this.$store.dispatch('user/destroyUserAsync').then(
|
||||||
|
this.$router.push('/login')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
questList (val) {
|
||||||
|
this.computeQuestList(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@/styles/theme.scss";
|
||||||
|
|
||||||
|
#menuTabB:hover {
|
||||||
|
background-color: $border4-color;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dropdown-menu {
|
||||||
|
span {
|
||||||
|
color: $maintext-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-menu {
|
||||||
|
float: right;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 50px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-menu-item {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 8px;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 18px;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
|
||||||
|
&.hover-effect {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background .3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(0, 0, 0, .025)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-container {
|
||||||
|
margin-right: 30px;
|
||||||
|
|
||||||
|
.avatar-wrapper {
|
||||||
|
margin-top: 5px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon-caret-bottom {
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
right: -20px;
|
||||||
|
top: 25px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
83
src/views/layout/components/main/home/index.vue
Normal file
83
src/views/layout/components/main/home/index.vue
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-100">
|
||||||
|
<map-box ref="mapbox" :key="mapBoxKey" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MapBox from '@/components/MapBox'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Home',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
mapBoxKey: 0 // 初始化一个变量用于控制map-box组件的重新渲染
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
MapBox
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
airList () {
|
||||||
|
return this.$store.state.airList
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 侧边栏显隐
|
||||||
|
*/
|
||||||
|
isCollapse () {
|
||||||
|
return this.$store.state.app.isCollapse
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* @description: 创建飞机图标
|
||||||
|
*/
|
||||||
|
makePlanes (planes) {
|
||||||
|
this.$refs.mapbox.removePlanes()// 先清除画布上现有的飞机
|
||||||
|
planes.forEach((plane, index) => { // 创建所有飞机
|
||||||
|
let planeDefaultLngLat
|
||||||
|
if (localStorage.getItem(plane.name) !== null) { // 从本地缓存 拿飞机得初始位置
|
||||||
|
planeDefaultLngLat = JSON.parse(localStorage.getItem(plane.name))
|
||||||
|
plane.lng = planeDefaultLngLat.lng
|
||||||
|
plane.lat = planeDefaultLngLat.lat
|
||||||
|
} else {
|
||||||
|
plane.lng = 0
|
||||||
|
plane.lat = 0
|
||||||
|
}
|
||||||
|
this.$refs.mapbox.makePlane(plane, index)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
if (this.airList.length > 0) {
|
||||||
|
this.makePlanes(this.airList)// 创建飞机图标
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
/**
|
||||||
|
* @description: 飞机列表更新时候 更新地图
|
||||||
|
*/
|
||||||
|
airList: {
|
||||||
|
handler (val) {
|
||||||
|
this.makePlanes(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 侧边栏缩进有变化时 地图重新自适应
|
||||||
|
*/
|
||||||
|
isCollapse: {
|
||||||
|
handler (val) {
|
||||||
|
if (val) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.mapBoxKey++ // 更新mapBoxKey以触发map-box组件的重新渲染
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
142
src/views/layout/components/main/planes/index.vue
Normal file
142
src/views/layout/components/main/planes/index.vue
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-100">
|
||||||
|
<map-box ref="mapbox" :key="mapBoxKey">
|
||||||
|
<template #content>
|
||||||
|
<PlaneStatus />
|
||||||
|
<ControllerTabs :plane="plane" @mapXOffset="mapXOffset" @makeRoute="makeRoute" @clearRoute="clearRoute" />
|
||||||
|
</template>
|
||||||
|
</map-box>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MapBox from '@/components/MapBox'
|
||||||
|
import ControllerTabs from '@/components/ControllerTabs'
|
||||||
|
import PlaneStatus from '@/components/PlaneStatus'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Planes',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
mapBoxKey: '', // 初始化一个变量用于控制map-box组件的重新渲染
|
||||||
|
planesId: this.$route.params.id,
|
||||||
|
localCount: 0// 本地存储计数器
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
MapBox,
|
||||||
|
ControllerTabs,
|
||||||
|
PlaneStatus
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
plane () {
|
||||||
|
if (!this.$store.state.airList) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return this.$store.state.airList.find(plane => plane.id === this.planesId)
|
||||||
|
},
|
||||||
|
position () {
|
||||||
|
if (this.plane) {
|
||||||
|
if (this.plane.planeState.position.length > 0) {
|
||||||
|
return this.plane.planeState.position
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 侧边栏显隐
|
||||||
|
*/
|
||||||
|
isCollapse () {
|
||||||
|
return this.$store.state.app.isCollapse
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* @description: 创建飞机图标
|
||||||
|
*/
|
||||||
|
makePlane (plane) {
|
||||||
|
let planeDefaultLngLat
|
||||||
|
if (localStorage.getItem(plane.name)) { // 从本地缓存 拿飞机得初始位置
|
||||||
|
planeDefaultLngLat = JSON.parse(localStorage.getItem(plane.name))
|
||||||
|
plane.lng = planeDefaultLngLat.lng
|
||||||
|
plane.lat = planeDefaultLngLat.lat
|
||||||
|
} else {
|
||||||
|
plane.lng = 0
|
||||||
|
plane.lat = 0
|
||||||
|
}
|
||||||
|
this.$refs.mapbox.removePlanes()// 先清除画布上现有的飞机
|
||||||
|
this.$refs.mapbox.makePlane(plane)// 创建飞机
|
||||||
|
this.$refs.mapbox.goto({ lng: plane.lng, lat: plane.lat })// 跳转到飞机位置
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 创建航线
|
||||||
|
*/
|
||||||
|
makeRoute (routeData) {
|
||||||
|
this.$refs.mapbox.makeRoute(routeData)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 清楚航线
|
||||||
|
*/
|
||||||
|
clearRoute () {
|
||||||
|
this.$refs.mapbox.clearRoute()
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 屏幕横移
|
||||||
|
* @param {*} val 正数向左移动 负数向右移动
|
||||||
|
*/
|
||||||
|
mapXOffset (val) {
|
||||||
|
this.$refs.mapbox.mapXOffset(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
if (this.plane !== undefined) {
|
||||||
|
this.makePlane(this.plane)// 创建飞机图标
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
/**
|
||||||
|
* @description: 有飞机数据之后 在地图上创建飞机
|
||||||
|
*/
|
||||||
|
plane: {
|
||||||
|
handler (val) {
|
||||||
|
this.makePlane(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 更新飞机位置 并画出轨迹 跟随飞机
|
||||||
|
*/
|
||||||
|
position: {
|
||||||
|
handler (val) {
|
||||||
|
const len = val.length
|
||||||
|
if (len > 2) {
|
||||||
|
const lng = val[len - 1][0]
|
||||||
|
const lat = val[len - 1][1]
|
||||||
|
this.localCount++// 计数器 叠加
|
||||||
|
if (this.localCount % 100 === 1) {
|
||||||
|
localStorage.setItem(this.plane.name, `{ "lng": ${lng}, "lat": ${lat} }`)
|
||||||
|
}
|
||||||
|
this.$refs.mapbox.setPlaneLngLat({ lng: lng, lat: lat }, 0, val, false)// 更新飞机位置 并画出轨迹 跟随飞机
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 侧边栏缩进有变化时 地图重新自适应
|
||||||
|
*/
|
||||||
|
isCollapse: {
|
||||||
|
handler (val) {
|
||||||
|
if (val) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.mapBoxKey++ // 更新mapBoxKey以触发map-box组件的重新渲染
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
156
src/views/layout/components/main/register/add.vue
Normal file
156
src/views/layout/components/main/register/add.vue
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-row class="m-t-0">
|
||||||
|
<el-col :span="24" class="p-r-5">
|
||||||
|
<el-container>
|
||||||
|
<el-header height="42px" class="l-h-42 p-l-10 p-r-10 border border-b-n">
|
||||||
|
<div class="l">
|
||||||
|
<i v-if="pageState === 'add'" class="iconfont el-icon-plus f-s-20"></i>
|
||||||
|
<i v-else class="iconfont el-icon-edit f-s-20"></i>
|
||||||
|
<font class="m-l-10 f-s-18 fb">{{ $route.meta.title }}</font>
|
||||||
|
</div>
|
||||||
|
</el-header>
|
||||||
|
<el-main class="border p-20">
|
||||||
|
<el-form ref="form" :model="form" label-width="120px">
|
||||||
|
<el-form-item label="所属客户">
|
||||||
|
<el-select v-model="form.shop_id" filterable placeholder="请选择,也可输入搜索">
|
||||||
|
<el-option v-for="item in adminList" :key="item.id" :label="item.uname" :value="item.shop_id">
|
||||||
|
<span style="float: left">{{ item.name }}</span>
|
||||||
|
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.uname
|
||||||
|
}}</span>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="飞机名称">
|
||||||
|
<el-input v-model="form.name" placeholder="起名可以是中文" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="购买日期">
|
||||||
|
<el-date-picker v-if="pageState == 'add' ? true : false" v-model="form.date" type="date"
|
||||||
|
placeholder="选择日期" format="yyyy 年 MM 月 dd 日" value-format="timestamp">
|
||||||
|
</el-date-picker>
|
||||||
|
<el-date-picker v-else v-model="form.date" type="date" placeholder="选择日期" format="yyyy 年 MM 月 dd 日"
|
||||||
|
value-format="timestamp" disabled>
|
||||||
|
</el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="form.onoff === true ? '启用' : '停用'">
|
||||||
|
<el-switch v-model="form.onoff" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="飞机描述">
|
||||||
|
<el-input v-model="form.desc" type="textarea" placeholder="非必填" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="pageState == 'add' ? true : false">
|
||||||
|
<el-button type="primary" icon="el-icon-plus" @click="addAir">创建</el-button>
|
||||||
|
<el-button @click="setForm({})" class="iconfont icon-qingchu">
|
||||||
|
<font class="m-l-5">重填</font>
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-else>
|
||||||
|
<el-button type="primary" icon="el-icon-edit" @click="saveAir">更新</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'RegisterAdd',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
shop_id: '',
|
||||||
|
name: '',
|
||||||
|
date: '',
|
||||||
|
onoff: true,
|
||||||
|
desc: ''
|
||||||
|
},
|
||||||
|
airId: this.$route.params.id, // get参数 获取飞机id 没有为添加页面
|
||||||
|
pageState: '', // 页面状态
|
||||||
|
plane: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 获取管理员列表
|
||||||
|
adminList () {
|
||||||
|
return this.$store.state.adminList
|
||||||
|
},
|
||||||
|
// 获取飞机列表
|
||||||
|
airList () {
|
||||||
|
return this.$store.state.airList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 设置表单
|
||||||
|
setForm (data) {
|
||||||
|
if (data.desc == null) {
|
||||||
|
data.desc = ''
|
||||||
|
}
|
||||||
|
this.form.shop_id = data.shop_id
|
||||||
|
this.form.name = data.name
|
||||||
|
this.form.date = data.date
|
||||||
|
this.form.onoff = data.onoff
|
||||||
|
this.form.desc = data.desc
|
||||||
|
if (Object.keys(data).length === 0) {
|
||||||
|
this.$message.warning('清空表单')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 初始化页面 添加or编辑
|
||||||
|
initPage () {
|
||||||
|
if (this.airId === undefined) {
|
||||||
|
this.pageState = 'add'
|
||||||
|
} else {
|
||||||
|
this.pageState = 'edit'
|
||||||
|
this.plane = this.airList.find((item) => item.id === this.airId)
|
||||||
|
if (this.plane) {
|
||||||
|
const data = {
|
||||||
|
shop_id: this.plane.shop_id,
|
||||||
|
name: this.plane.name,
|
||||||
|
date: this.plane.apply_time + '000',
|
||||||
|
onoff: this.plane.onoff === '1',
|
||||||
|
desc: this.plane.describe
|
||||||
|
}
|
||||||
|
this.setForm(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 创建新飞机
|
||||||
|
*/
|
||||||
|
async addAir () {
|
||||||
|
const res = await this.$store.dispatch('fetchAddAir', this.form)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
this.$router.push('/register/index')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 更新飞机
|
||||||
|
*/
|
||||||
|
async saveAir () {
|
||||||
|
this.form.id = this.airId
|
||||||
|
const res = await this.$store.dispatch('fetchSaveAir', this.form)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
this.$router.push('/register/index')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
airList () {
|
||||||
|
this.initPage()// 初始化页面
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
if (this.airList.length > 0) {
|
||||||
|
this.initPage()// 初始化页面
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.line {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
112
src/views/layout/components/main/register/crosFrequency.vue
Normal file
112
src/views/layout/components/main/register/crosFrequency.vue
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-row class="m-t-0">
|
||||||
|
<el-col :span="24" class="p-r-5">
|
||||||
|
<el-container>
|
||||||
|
<el-header height="42px" class="l-h-42 p-l-10 p-r-10 border border-b-n">
|
||||||
|
<div class="l">
|
||||||
|
<i class="iconfont el-icon-link f-s-20"></i>
|
||||||
|
<font class="m-l-10 f-s-18 fb">飞机对频</font>
|
||||||
|
</div>
|
||||||
|
</el-header>
|
||||||
|
<el-main class="border p-20">
|
||||||
|
<el-form label-width="120px">
|
||||||
|
<el-form-item label="物理ID">
|
||||||
|
<el-tag :type="macAdd === null ? 'danger' : ''">{{ macAdd === null ? 'NoThing' : macAdd }}</el-tag>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="客户分配">
|
||||||
|
<el-select v-model="shop_id" filterable placeholder="请选择" @blur="air_id = ''; disabled = true"
|
||||||
|
:disabled="macAdd === null ? true : false">
|
||||||
|
<el-option v-for="item in adminList" :key="item.id" :label="item.uname" :value="item.shop_id">
|
||||||
|
<span style="float: left">{{ item.name }}</span>
|
||||||
|
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.uname
|
||||||
|
}}</span>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="飞机列表">
|
||||||
|
<el-radio-group v-if="airList.length != 0" v-model="air_id" @change="disabled = false;">
|
||||||
|
<el-radio class="m-b-10 m-l-0" v-for="item in airList" :key="item.id" :label="item.id" border>
|
||||||
|
<span></span>{{ item.name }}
|
||||||
|
{{ item.macadd }}
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<span v-else>No data</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button icon="iconfont icon-jiekou m-r-5" :type="macAdd === null ? 'info' : 'primary'"
|
||||||
|
@click="onSubmit" :disabled="macAdd === null ? 'disabled' : false">对频
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { apiCrosFrequency } from '@/utils/api/table'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'crosFrequency',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
shop_id: null,
|
||||||
|
air_id: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 获取管理员列表
|
||||||
|
adminList () {
|
||||||
|
return this.$store.state.adminList
|
||||||
|
},
|
||||||
|
// 过滤掉 不对应客户的 飞机列表
|
||||||
|
airList () {
|
||||||
|
return this.$store.state.airList.filter((item) => item.shop_id === this.shop_id)
|
||||||
|
},
|
||||||
|
// 对频信息 物理地址
|
||||||
|
macAdd () {
|
||||||
|
return this.$store.state.crosFrequency
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* @Description: 检查飞机发布过来的 mac地址 是否已经存在
|
||||||
|
* @Return: bool
|
||||||
|
*/
|
||||||
|
isMacAdd () {
|
||||||
|
let b = false
|
||||||
|
this.$store.state.airList.map((item) => {
|
||||||
|
if (item.macadd === this.macAdd) {
|
||||||
|
b = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return b
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 提交表单
|
||||||
|
*/
|
||||||
|
onSubmit () {
|
||||||
|
const params = { macAdd: this.macAdd, id: this.air_id }
|
||||||
|
if (this.isMacAdd()) {
|
||||||
|
this.$confirm('已经有同名mac地址,请谨慎操作?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
apiCrosFrequency(params)// 对频
|
||||||
|
}).catch(() => {
|
||||||
|
this.$message.info('取消对频')
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
apiCrosFrequency(params)// 对频
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
144
src/views/layout/components/main/register/index.vue
Normal file
144
src/views/layout/components/main/register/index.vue
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<!-- 组合按钮 -->
|
||||||
|
<el-button-group>
|
||||||
|
<el-button type="primary" icon="el-icon-plus" @click="$router.replace('/register/add')">添加</el-button>
|
||||||
|
<el-button type="danger" icon="el-icon-delete" @click="deleteAir(countSelIdArr($refs.myTable.selection))">删除
|
||||||
|
</el-button>
|
||||||
|
<el-button type="warning" icon="el-icon-edit" @click="toEditPage()">编辑</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
<!-- 用户select选项 -->
|
||||||
|
<el-button-group class="m-l-20">
|
||||||
|
<Selection v-model="form.shop_id" />
|
||||||
|
</el-button-group>
|
||||||
|
<!-- 飞机表格 -->
|
||||||
|
<el-table class="m-t-20 w-100" ref="myTable"
|
||||||
|
:data="airListArr.slice((currentPage - 1) * pageSize, currentPage * pageSize)" border tooltip-effect="dark">
|
||||||
|
<el-table-column align="center" type="selection" width="40">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" prop="id" label="id" width="50">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="name" label="名称" width="150" min-width="150">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="macID" width="175" min-width="175">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag :type="scope.row.macadd !== '' ? 'success' : 'danger'">
|
||||||
|
<i style="" class="iconfont m-r-5 f-s-16 l-h-32"
|
||||||
|
:class="scope.row.macadd !== '' ? 'icon-jiekou' : 'icon-cuowu'"></i>
|
||||||
|
<font>{{ scope.row.macadd !== '' ? scope.row.macadd : '未对频' }}</font>
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态" min-width="150" show-overflow-tooltip>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag :type="scope.row.onoff === '1' ? 'success' : 'danger'">
|
||||||
|
<i style="" class="iconfont m-r-5 f-s-16 l-h-32"
|
||||||
|
:class="scope.row.onoff === '1' ? 'icon-qiyong' : 'icon-ic_tingyong'"></i>
|
||||||
|
<font>{{ scope.row.onoff === '1' ? '启用' : '停用' }}</font>
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="注册时间" width="100" min-width="80">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
{{ scope.row.apply_time | parseTime('{y}-{m}-{d}') }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="controler" label="操作" width="200" min-width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button-group>
|
||||||
|
<el-button type="primary" icon="el-icon-search"
|
||||||
|
@click="$router.replace(`/planes/index/${scope.row.id}/${scope.row.name}`)">查看</el-button>
|
||||||
|
<el-button type="danger" icon="el-icon-delete" @click="deleteAir([scope.row.id])">删除</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-pagination class="m-t-20" layout="prev, pager, next" :current-page.sync="currentPage" :page-size="pageSize"
|
||||||
|
:total="airList.length">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { parseTime, countSelIdArr } from '@/utils'
|
||||||
|
import Selection from '@/components/Selection'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Register',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
pageSize: 8, // 每页显示记录条数
|
||||||
|
currentPage: 1, // 当前页
|
||||||
|
form: {
|
||||||
|
shop_id: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Selection
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 获取飞机列表
|
||||||
|
airList () {
|
||||||
|
return this.$store.state.airList
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 过滤掉 不对应客户的 飞机列表
|
||||||
|
* @return: 飞机列表
|
||||||
|
*/
|
||||||
|
airListArr () {
|
||||||
|
if (this.form.shop_id !== '') {
|
||||||
|
return this.airList.filter((item) => item.shop_id === this.form.shop_id)
|
||||||
|
} else {
|
||||||
|
return this.airList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
countSelIdArr,
|
||||||
|
/**
|
||||||
|
* @description: 跳转到编辑页面
|
||||||
|
*/
|
||||||
|
toEditPage () {
|
||||||
|
const selId = this.countSelIdArr(this.$refs.myTable.selection)
|
||||||
|
switch (selId.length) {
|
||||||
|
case 0:
|
||||||
|
this.$message.error('请选择一条需要编辑的记录')
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
this.$router.push('/register/edit/' + selId['0'])
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
this.$message.error('只能选择一条记录')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 删除飞机
|
||||||
|
*/
|
||||||
|
deleteAir (idArr) {
|
||||||
|
this.$store.dispatch('fetchDelAir', idArr)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
airList () {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
parseTime,
|
||||||
|
countSelIdArr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@/styles/theme.scss";
|
||||||
|
|
||||||
|
.el-tag {
|
||||||
|
i {
|
||||||
|
vertical-align: middle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
228
src/views/layout/components/main/route/add.vue
Normal file
228
src/views/layout/components/main/route/add.vue
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-100">
|
||||||
|
<map-box ref="mapbox">
|
||||||
|
<template #content>
|
||||||
|
<el-row class="w-40 m-t-20 m-l-20">
|
||||||
|
<el-col :span="24" class="p-r-5">
|
||||||
|
<el-container>
|
||||||
|
<el-header height="42px" class="l-h-42 p-l-10 p-r-10 border border-b-n">
|
||||||
|
<div class="l">
|
||||||
|
<i v-if="pageState === 'add'" class="iconfont el-icon-plus f-s-20"></i>
|
||||||
|
<i v-else class="iconfont el-icon-edit f-s-20"></i>
|
||||||
|
<font class="m-l-10 f-s-18 fb">{{ $route.meta.title }}</font>
|
||||||
|
</div>
|
||||||
|
</el-header>
|
||||||
|
<el-main class="border p-20 mainBox bg-white">
|
||||||
|
<el-form ref="form" :model="form" label-width="120px">
|
||||||
|
<el-form-item label="所属客户">
|
||||||
|
<el-select v-model="form.shop_id" filterable placeholder="请选择,也可输入搜索">
|
||||||
|
<el-option v-for="item in $store.state.adminList" :key="item.id" :label="item.uname"
|
||||||
|
:value="item.shop_id" :disabled="pageState == 'edit' ? true : false">
|
||||||
|
<span style="float: left">{{ item.name }}</span>
|
||||||
|
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.uname
|
||||||
|
}}</span>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="航线标题">
|
||||||
|
<el-input v-model="form.name" placeholder="航线标题" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="航线文件上传">
|
||||||
|
<el-upload class="upload-demo" drag name="file" :action="action" :headers="myheader"
|
||||||
|
:on-success="handleUpSuccess" :on-exceed="handleExceed" :on-error="handleUpErr"
|
||||||
|
:on-remove="handleRemove" :limit="1" :file-list="fileList" :before-upload="beforeAvatarUpload">
|
||||||
|
<i class="el-icon-upload"></i>
|
||||||
|
<div class="el-upload__text"><em>点击上传</em></div>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="航线描述">
|
||||||
|
<el-input v-model="form.desc" type="textarea" placeholder="非必填" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="pageState == 'add' ? true : false">
|
||||||
|
<el-button type="primary" icon="el-icon-plus" @click="addRoute">创建</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-else>
|
||||||
|
<el-button type="primary" icon="el-icon-edit" @click="saveRoute">更新</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
</map-box>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MapBox from '@/components/MapBox'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RouteAdd',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
action: this.$store.state.settings.baseURL + this.$store.state.settings.apiPath + 'upTxtFile',
|
||||||
|
myheader: { token: this.$store.state.user.token },
|
||||||
|
form: {
|
||||||
|
shop_id: '',
|
||||||
|
name: '',
|
||||||
|
desc: '',
|
||||||
|
upFile: '',
|
||||||
|
route_data: ''
|
||||||
|
},
|
||||||
|
routeId: this.$route.params.id,
|
||||||
|
fileList: [],
|
||||||
|
pageState: ''// 页面状态
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
MapBox
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* @description: 获取管理员列表
|
||||||
|
*/
|
||||||
|
adminList () {
|
||||||
|
return this.$store.state.adminList
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 获取航线列表
|
||||||
|
*/
|
||||||
|
routeList () {
|
||||||
|
return this.$store.state.routeList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/* 文件上传表单 */
|
||||||
|
handleExceed () {
|
||||||
|
this.$message.warning('需先删除之前上传文件')
|
||||||
|
},
|
||||||
|
handleRemove () {
|
||||||
|
this.form.upFile = ''
|
||||||
|
},
|
||||||
|
handleUpSuccess (res) {
|
||||||
|
this.$refs.mapbox.makeRoute(res.content)// 绘出航线
|
||||||
|
if (res.status === 0) {
|
||||||
|
this.fileList = []
|
||||||
|
this.$message.error(res.msg)
|
||||||
|
} else {
|
||||||
|
this.form.upFile = res.data
|
||||||
|
this.$message.success(res.msg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleUpErr () {
|
||||||
|
this.$message.error('接口访问失败')
|
||||||
|
},
|
||||||
|
beforeAvatarUpload (file) {
|
||||||
|
const isTxt = file.type === 'text/plain' || file.type === 'application/json'
|
||||||
|
if (!isTxt) {
|
||||||
|
this.$message.error('航点文件仅支持txt或json格式!')
|
||||||
|
}
|
||||||
|
return isTxt
|
||||||
|
},
|
||||||
|
/* ED 文件上传表单 */
|
||||||
|
// 设置表单
|
||||||
|
setForm (data) {
|
||||||
|
if (data.desc == null) {
|
||||||
|
data.desc = ''
|
||||||
|
}
|
||||||
|
this.form.shop_id = data.shop_id
|
||||||
|
this.form.name = data.name
|
||||||
|
this.form.upFile = data.upFile
|
||||||
|
this.form.desc = data.desc
|
||||||
|
this.form.route_data = data.route_data
|
||||||
|
if (Object.keys(data).length === 0) {
|
||||||
|
this.$message.warning('清空表单')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 初始化页面 添加or编辑
|
||||||
|
initPage () {
|
||||||
|
if (this.routeId === undefined) {
|
||||||
|
this.pageState = 'add'
|
||||||
|
} else {
|
||||||
|
this.pageState = 'edit'
|
||||||
|
this.route = this.routeList.find((item) => item.id === this.routeId)
|
||||||
|
if (this.route) {
|
||||||
|
const data = {
|
||||||
|
shop_id: this.route.shop_id,
|
||||||
|
name: this.route.name,
|
||||||
|
route_data: this.route.route_data,
|
||||||
|
upFile: ''
|
||||||
|
}
|
||||||
|
this.setForm(data)
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.mapbox.makeRoute(JSON.parse(this.route.route_data))// 更新页面 绘出航线 初始化
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 创建新站点
|
||||||
|
*/
|
||||||
|
async addRoute () {
|
||||||
|
const res = await this.$store.dispatch('fetchAddRoute', this.form)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
this.$router.push('/route/index')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 更新站点
|
||||||
|
*/
|
||||||
|
async saveRoute () {
|
||||||
|
this.form.id = this.routeId
|
||||||
|
const res = await this.$store.dispatch('fetchSaveRoute', this.form)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
this.$router.push('/route/index')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
routeList () {
|
||||||
|
this.initPage()// 初始化页面
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
if (this.routeList.length > 0) {
|
||||||
|
this.initPage()// 初始化页面
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.line {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-header,
|
||||||
|
.el-main {
|
||||||
|
z-index: 1001;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-uploader .el-upload {
|
||||||
|
border: 1px dashed #d9d9d9;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-uploader .el-upload:hover {
|
||||||
|
border-color: #409EFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-uploader-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #8c939d;
|
||||||
|
width: 178px;
|
||||||
|
height: 178px;
|
||||||
|
line-height: 178px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 178px;
|
||||||
|
height: 178px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
127
src/views/layout/components/main/route/index.vue
Normal file
127
src/views/layout/components/main/route/index.vue
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<!-- 组合按钮 -->
|
||||||
|
<el-button-group>
|
||||||
|
<el-button type="primary" icon="el-icon-plus" @click="$router.replace('/route/add')">添加</el-button>
|
||||||
|
<el-button type="danger" icon="el-icon-delete" @click="deleteRoute(countSelIdArr($refs.myTable.selection))">删除
|
||||||
|
</el-button>
|
||||||
|
<el-button type="warning" icon="el-icon-edit" @click="toEditPage()">编辑</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
<!-- 用户select选项 -->
|
||||||
|
<el-button-group class="m-l-20">
|
||||||
|
<Selection v-model="form.shop_id" />
|
||||||
|
</el-button-group>
|
||||||
|
<!-- 航线表格 -->
|
||||||
|
<el-table class="m-t-20 w-100" ref="myTable"
|
||||||
|
:data="routeListArr.slice((currentPage - 1) * pageSize, currentPage * pageSize)" border tooltip-effect="dark">
|
||||||
|
<el-table-column align="center" type="selection" width="40">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" prop="id" label="id" width="50">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="name" label="任务标题" width="150" min-width="150">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="route_data" label="航线" min-width="150" show-overflow-tooltip>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="controler" label="操作" width="150" min-width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button-group>
|
||||||
|
<el-button type="danger" icon="el-icon-delete" @click="deleteRoute([scope.row.id])">删除</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-pagination class="m-t-20" layout="prev, pager, next" :current-page.sync="currentPage" :page-size="pageSize"
|
||||||
|
:total="routeListArr.length">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { countSelIdArr } from '@/utils'
|
||||||
|
import Selection from '@/components/Selection'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Route',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
pageSize: 8, // 每页显示记录条数
|
||||||
|
currentPage: 1, // 当前页
|
||||||
|
form: {
|
||||||
|
shop_id: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Selection
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* @description: 获取航线列表
|
||||||
|
*/
|
||||||
|
routeList () {
|
||||||
|
return this.$store.state.routeList
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 过滤掉 不对应客户的 航线列表
|
||||||
|
* @return: 航线列表
|
||||||
|
*/
|
||||||
|
routeListArr () {
|
||||||
|
if (this.form.shop_id !== '') {
|
||||||
|
return this.routeList.filter((item) => item.shop_id === this.form.shop_id)
|
||||||
|
} else {
|
||||||
|
return this.routeList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
countSelIdArr,
|
||||||
|
/**
|
||||||
|
* @description: 跳转到编辑页面
|
||||||
|
*/
|
||||||
|
toEditPage () {
|
||||||
|
const selId = this.countSelIdArr(this.$refs.myTable.selection)
|
||||||
|
switch (selId.length) {
|
||||||
|
case 0:
|
||||||
|
this.$message.error('请选择一条需要编辑的记录')
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
this.$router.push('/route/edit/' + selId['0'])
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
this.$message.error('只能选择一条记录')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 删除航线
|
||||||
|
*/
|
||||||
|
deleteRoute (idArr) {
|
||||||
|
this.$store.dispatch('fetchDelRoute', idArr)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
routeList () {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
countSelIdArr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@/styles/theme.scss";
|
||||||
|
|
||||||
|
.el-tag {
|
||||||
|
i {
|
||||||
|
vertical-align: middle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-placeholder {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
260
src/views/layout/components/main/site/add.vue
Normal file
260
src/views/layout/components/main/site/add.vue
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-row class="m-t-0">
|
||||||
|
<el-col :span="24" class="p-r-5">
|
||||||
|
<el-container>
|
||||||
|
<el-header height="42px" class="l-h-42 p-l-10 p-r-10 border border-b-n">
|
||||||
|
<div class="l">
|
||||||
|
<i v-if="pageState === 'add'" class="iconfont el-icon-plus f-s-20"></i>
|
||||||
|
<i v-else class="iconfont el-icon-edit f-s-20"></i>
|
||||||
|
<font class="m-l-10 f-s-18 fb">{{ $route.meta.title }}</font>
|
||||||
|
{{ form.bindroute }}
|
||||||
|
</div>
|
||||||
|
</el-header>
|
||||||
|
<el-main class="border p-20">
|
||||||
|
<el-form ref="form" :model="form" label-width="120px">
|
||||||
|
<el-form-item label="所属客户">
|
||||||
|
<el-select v-model="form.shop_id" filterable placeholder="请选择,也可输入搜索">
|
||||||
|
<el-option v-for="item in $store.state.adminList" :key="item.id" :label="item.uname"
|
||||||
|
:value="item.shop_id" :disabled="pageState == 'edit' ? true : false">
|
||||||
|
<span style="float: left">{{ item.name }}</span>
|
||||||
|
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.uname
|
||||||
|
}}</span>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="站点名称">
|
||||||
|
<el-input v-model="form.sitename" placeholder="取餐站点的名字" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="二维码尺寸">
|
||||||
|
<el-slider v-model="form.size" :format-tooltip="formatTooltip"></el-slider>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="中心LOGO替换">
|
||||||
|
<el-upload class="avatar-uploader" drag name="file" :action="action" :headers="myheader"
|
||||||
|
:show-file-list="false" :on-success="handleUpSuccess" :on-error="handleUpErr"
|
||||||
|
:before-upload="beforeAvatarUpload">
|
||||||
|
<img v-if="form.upFile != ''"
|
||||||
|
:src="$store.state.settings.host + '/Data/UploadFiles/temp/' + form.upFile" class="avatar" />
|
||||||
|
<img v-else-if="defaultQr != '' && form.upFile == ''"
|
||||||
|
:src="$store.state.settings.host + '/Data/UploadFiles/qr/' + defaultQr" class="avatar" />
|
||||||
|
<template v-else>
|
||||||
|
<i class="el-icon-plus f-s-30 m-t-70 seatFontColor"></i>
|
||||||
|
<div class="el-upload__text"><em>空为默认LOGO</em></div>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="绑定航线">
|
||||||
|
<el-transfer v-model="form.bindroute" :data="routeData" :titles="['可绑定航线', '已绑定航线']"></el-transfer>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="站点描述">
|
||||||
|
<el-input v-model="form.desc" type="textarea" placeholder="非必填" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="pageState == 'add' ? true : false">
|
||||||
|
<el-button type="primary" icon="el-icon-plus" @click="addSite">创建</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-else>
|
||||||
|
<el-button type="primary" icon="el-icon-edit" @click="saveSite">更新</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'SiteAdd',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
action: this.$store.state.settings.baseURL + this.$store.state.settings.apiPath + 'upImgFile',
|
||||||
|
myheader: { token: this.$store.state.user.token },
|
||||||
|
form: {
|
||||||
|
shop_id: '',
|
||||||
|
sitename: '',
|
||||||
|
desc: '',
|
||||||
|
upFile: '',
|
||||||
|
size: 100,
|
||||||
|
bindroute: []
|
||||||
|
},
|
||||||
|
siteId: this.$route.params.id,
|
||||||
|
pageState: '', // 页面状态
|
||||||
|
defaultQr: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* @description: 获取管理员列表
|
||||||
|
*/
|
||||||
|
adminList () {
|
||||||
|
return this.$store.state.adminList
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 获取站点列表
|
||||||
|
*/
|
||||||
|
siteList () {
|
||||||
|
return this.$store.state.siteList
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 获取航线列表
|
||||||
|
*/
|
||||||
|
routeList () {
|
||||||
|
return this.$store.state.routeList
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 根据用户过滤出 对应的航线列表信息 ps:妈的估计只有三天之内的我才能看懂
|
||||||
|
*/
|
||||||
|
routeData () {
|
||||||
|
// 过滤匹配 shop_id 的项,并重组数组
|
||||||
|
const filteredRoutes = this.routeList.filter(item => item.shop_id === this.form.shop_id)
|
||||||
|
.map(item => ({ key: item.id, label: item.name }))
|
||||||
|
// 将 siteList 中的 bind_route 字段拆分为数组 新建的话把所有已绑定的航线 push到一个数组 更新的话除了自身绑定的航线 其余的push到一个数组
|
||||||
|
const bindRouteArray = []
|
||||||
|
this.siteList.forEach(element => {
|
||||||
|
if (this.pageState === 'edit') {
|
||||||
|
if (element.id !== this.siteId && element.bind_route !== null) {
|
||||||
|
element.bind_route.split(',').forEach(ele => {
|
||||||
|
bindRouteArray.push(ele)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (element.bind_route !== null) {
|
||||||
|
element.bind_route.split(',').forEach(ele => {
|
||||||
|
bindRouteArray.push(ele)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 使用 bindRouteArray 过滤 filteredRoutes
|
||||||
|
const finalFilteredRoutes = filteredRoutes.filter((item) =>
|
||||||
|
!bindRouteArray.includes(item.key.toString())
|
||||||
|
)
|
||||||
|
return finalFilteredRoutes
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* @description: 设置滑动条值
|
||||||
|
* @param {*} 值
|
||||||
|
*/
|
||||||
|
formatTooltip (val) {
|
||||||
|
return val * 10 + 280 + '像素'
|
||||||
|
},
|
||||||
|
/* 文件上传表单 */
|
||||||
|
handleUpSuccess (res) {
|
||||||
|
if (res.status === 0) {
|
||||||
|
this.$message.error(res.msg)
|
||||||
|
} else {
|
||||||
|
this.form.upFile = res.data
|
||||||
|
this.$message.success(res.msg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeAvatarUpload (file) {
|
||||||
|
const isJPG = file.type === 'image/jpeg'
|
||||||
|
if (!isJPG) {
|
||||||
|
this.$message.error('上传图片只能是JPG格式!')
|
||||||
|
}
|
||||||
|
return isJPG
|
||||||
|
},
|
||||||
|
handleUpErr () {
|
||||||
|
this.$message.error('接口访问失败')
|
||||||
|
},
|
||||||
|
// 设置表单
|
||||||
|
setForm (data) {
|
||||||
|
if (data.desc == null) {
|
||||||
|
data.desc = ''
|
||||||
|
}
|
||||||
|
this.form.shop_id = data.shop_id
|
||||||
|
this.form.sitename = data.sitename
|
||||||
|
this.form.desc = data.desc
|
||||||
|
this.form.bindroute = data.bindroute
|
||||||
|
this.defaultQr = data.defaultQr
|
||||||
|
if (Object.keys(data).length === 0) {
|
||||||
|
this.$message.warning('清空表单')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 初始化页面 添加or编辑
|
||||||
|
initPage () {
|
||||||
|
if (this.siteId === undefined) {
|
||||||
|
this.pageState = 'add'
|
||||||
|
} else {
|
||||||
|
this.pageState = 'edit'
|
||||||
|
this.site = this.siteList.find((item) => item.id === this.siteId)
|
||||||
|
if (this.site) {
|
||||||
|
const data = {
|
||||||
|
shop_id: this.site.shop_id,
|
||||||
|
sitename: this.site.sitename,
|
||||||
|
desc: this.site.describe,
|
||||||
|
bindroute: this.site.bind_route !== null ? this.site.bind_route.split(',') : [], // 此数据库字段为字符串 分割成数组
|
||||||
|
defaultQr: this.site.qr
|
||||||
|
}
|
||||||
|
this.setForm(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 创建新站点
|
||||||
|
*/
|
||||||
|
async addSite () {
|
||||||
|
const res = await this.$store.dispatch('fetchAddSite', this.form)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
this.$router.push('/site/index')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 更新站点
|
||||||
|
*/
|
||||||
|
async saveSite () {
|
||||||
|
this.form.id = this.siteId
|
||||||
|
const res = await this.$store.dispatch('fetchSaveSite', this.form)
|
||||||
|
if (res.data.status === 1) {
|
||||||
|
this.$router.push('/site/index')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
siteList () {
|
||||||
|
this.initPage()// 初始化页面
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
if (this.siteList.length > 0) {
|
||||||
|
this.initPage()// 初始化页面
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.line {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-uploader .el-upload {
|
||||||
|
border: 1px dashed #d9d9d9;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-uploader .el-upload:hover {
|
||||||
|
border-color: #409EFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-uploader-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #8c939d;
|
||||||
|
width: 178px;
|
||||||
|
height: 178px;
|
||||||
|
line-height: 178px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 178px;
|
||||||
|
height: 178px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
143
src/views/layout/components/main/site/index.vue
Normal file
143
src/views/layout/components/main/site/index.vue
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<!-- 组合按钮 -->
|
||||||
|
<el-button-group>
|
||||||
|
<el-button type="primary" icon="el-icon-plus" @click="$router.replace('/site/add')">添加</el-button>
|
||||||
|
<el-button type="danger" icon="el-icon-delete" @click="deleteSite(countSelIdArr($refs.myTable.selection))">删除
|
||||||
|
</el-button>
|
||||||
|
<el-button type="warning" icon="el-icon-edit" @click="toEditPage()">编辑</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
<!-- 用户select选项 -->
|
||||||
|
<el-button-group class="m-l-20">
|
||||||
|
<Selection v-model="form.shop_id" />
|
||||||
|
</el-button-group>
|
||||||
|
<!-- 站点表格 -->
|
||||||
|
<el-table class="m-t-20 w-100" ref="myTable"
|
||||||
|
:data="siteListArr.slice((currentPage - 1) * pageSize, currentPage * pageSize)" border tooltip-effect="dark">
|
||||||
|
<el-table-column align="center" type="selection" width="40">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" prop="id" label="id" width="50">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="sitename" label="站点名称" width="120" min-width="100">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="菊花码缩率图" width="120" min-width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-image :src="$store.state.settings.host + '/Data/UploadFiles/qr/' + scope.row.qr"
|
||||||
|
:preview-src-list="[$store.state.settings.host + '/Data/UploadFiles/qr/' + scope.row.qr]">
|
||||||
|
</el-image>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="describe" label="站点描述" min-width="80" show-overflow-tooltip>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="已绑航线" width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag class="iconfont" :class="scope.row.bind_route !== null ? 'icon-feihangluxian' : 'icon-ic_tingyong'"
|
||||||
|
:type="scope.row.bind_route !== null ? '' : 'danger'">
|
||||||
|
<font class="m-l-5">{{ scope.row.bind_route !== null ? scope.row.bind_route :
|
||||||
|
"未绑定" }}</font>
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="controler" label="操作" width="140" min-width="140">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button-group>
|
||||||
|
<el-button type="danger" icon="el-icon-delete" @click="deleteSite([scope.row.id])">删除</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-pagination class="m-t-20" layout="prev, pager, next" :current-page.sync="currentPage" :page-size="pageSize"
|
||||||
|
:total="siteListArr.length">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { countSelIdArr } from '@/utils'
|
||||||
|
import Selection from '@/components/Selection'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Site',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
pageSize: 8, // 每页显示记录条数
|
||||||
|
currentPage: 1, // 当前页
|
||||||
|
form: {
|
||||||
|
shop_id: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Selection
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* @description: 获取站点列表
|
||||||
|
*/
|
||||||
|
siteList () {
|
||||||
|
return this.$store.state.siteList
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 过滤掉 不对应客户的 站点列表
|
||||||
|
* @return: 站点列表
|
||||||
|
*/
|
||||||
|
siteListArr () {
|
||||||
|
if (this.form.shop_id !== '') {
|
||||||
|
return this.siteList.filter((item) => item.shop_id === this.form.shop_id)
|
||||||
|
} else {
|
||||||
|
return this.siteList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
countSelIdArr,
|
||||||
|
/**
|
||||||
|
* @description: 跳转到编辑页面
|
||||||
|
*/
|
||||||
|
toEditPage () {
|
||||||
|
const selId = this.countSelIdArr(this.$refs.myTable.selection)
|
||||||
|
switch (selId.length) {
|
||||||
|
case 0:
|
||||||
|
this.$message.error('请选择一条需要编辑的记录')
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
this.$router.push('/site/edit/' + selId['0'])
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
this.$message.error('只能选择一条记录')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 删除站点
|
||||||
|
*/
|
||||||
|
deleteSite (idArr) {
|
||||||
|
this.$store.dispatch('fetchDelSite', idArr)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
siteList () {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
countSelIdArr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@/styles/theme.scss";
|
||||||
|
|
||||||
|
.el-tag {
|
||||||
|
i {
|
||||||
|
vertical-align: middle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-placeholder {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
204
src/views/layout/components/menubar.vue
Normal file
204
src/views/layout/components/menubar.vue
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- logo -->
|
||||||
|
<div class="sidebar-logo-container" :class="{ 'collapse': isCollapse }">
|
||||||
|
<router-link v-if="isCollapse" key="isCollapse" class="sidebar-logo-link" to="/">
|
||||||
|
<img v-if="logo" src="@/assets/logo.png" class="sidebar-logo">
|
||||||
|
<h1 v-else class="sidebar-title">{{ title }}</h1>
|
||||||
|
</router-link>
|
||||||
|
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||||
|
<img v-if="logo" src="@/assets/logo.png" class="sidebar-logo">
|
||||||
|
<h1 class="sidebar-title">{{ title }}</h1>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
<!-- end logo -->
|
||||||
|
<!-- menu -->
|
||||||
|
<el-menu class="border-n" :router="true" :default-active="activeMenu" :unique-opened="true" background-color="#304156"
|
||||||
|
text-color="rgb(191, 203, 217)" active-text-color="#409EFF" :collapse-transition="false" :collapse="isCollapse">
|
||||||
|
<template v-for="(route, index) in routes">
|
||||||
|
<el-menu-item v-if="route.children.length < 2" :key="route.path" :index="route.children[0].path">
|
||||||
|
<i class="fc" :class="route.children[0].meta.icon"></i>
|
||||||
|
<span slot="title">{{ route.children[0].meta.title }}</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-submenu v-else :key="index" :index="route.path">
|
||||||
|
<template slot="title">
|
||||||
|
<i class="fc" :class="route.meta.icon"></i>
|
||||||
|
<span>{{ route.meta.title }}</span>
|
||||||
|
</template>
|
||||||
|
<el-menu-item v-for="child in route.children" :key="child.path" :index="child.path">
|
||||||
|
<i class="fc" :class="child.meta.icon"></i>
|
||||||
|
<span>{{ child.meta.title }}</span>
|
||||||
|
</el-menu-item>
|
||||||
|
</el-submenu>
|
||||||
|
</template>
|
||||||
|
</el-menu>
|
||||||
|
<!-- end menu -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Menubar',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
title: '无人机控制终端',
|
||||||
|
logo: '@/assets/logo.png'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* @description: 递归过滤路由列表,同时过滤掉父级和子级中 hidden 为 true 或权限不足的项
|
||||||
|
*/
|
||||||
|
routes () {
|
||||||
|
const filterRoutes = (routes, userPower) => {
|
||||||
|
return routes.filter(item => {
|
||||||
|
const roles = item.roles || [] // 设置默认空数组
|
||||||
|
if (item.hidden === true || roles.indexOf(userPower) === -1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
item.children = filterRoutes(item.children, userPower) // 递归处理子级
|
||||||
|
if (item.children.length === 0) {
|
||||||
|
return false // 如果子级都被过滤掉了,也过滤掉当前项
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const userPower = this.$store.state.user.power.trim()
|
||||||
|
return filterRoutes(this.$router.options.routes, userPower)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 当前激活的 路由路径
|
||||||
|
*/
|
||||||
|
activeMenu () {
|
||||||
|
const route = this.$route
|
||||||
|
const { meta, path } = route
|
||||||
|
if (meta.activeMenu) {
|
||||||
|
return meta.activeMenu
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 获取飞机列表
|
||||||
|
*/
|
||||||
|
airList () {
|
||||||
|
return this.$store.state.airList.filter(element => element.shop_id === this.$store.state.user.shop_id)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @description: 侧边导航栏状态
|
||||||
|
*/
|
||||||
|
isCollapse () {
|
||||||
|
return this.$store.state.app.isCollapse
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* @description: 动态加载路由
|
||||||
|
*/
|
||||||
|
loadRoute () {
|
||||||
|
const arr = new Array(0)
|
||||||
|
this.airList.map((item, index) => {
|
||||||
|
arr[index] = {
|
||||||
|
path: '/planes/index/' + item.id + '/' + item.name,
|
||||||
|
name: 'Planes',
|
||||||
|
component: () => import('@/views/layout/components/main/planes/index.vue'),
|
||||||
|
meta: { title: item.name, icon: 'iconfont icon-wurenji' },
|
||||||
|
roles: ['admin', 'editor']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.routes.map((element) => {
|
||||||
|
if (element.meta.title === '无人机') {
|
||||||
|
element.children = arr
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
/**
|
||||||
|
* @description: 监听飞机列表 有个新刷新导航栏
|
||||||
|
*/
|
||||||
|
airList () {
|
||||||
|
this.loadRoute()
|
||||||
|
this.$forceUpdate()// 刷新本组件
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@/styles/theme.scss";
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 18px !important;
|
||||||
|
margin-right: 12px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-submenu__title i,
|
||||||
|
.el-menu-item i {
|
||||||
|
color: rgb(191, 203, 217);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-submenu {
|
||||||
|
.el-menu-item {
|
||||||
|
background-color: #1f2d3d !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-submenu .el-menu-item:hover {
|
||||||
|
background-color: #001528 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarLogoFade-enter,
|
||||||
|
.sidebarLogoFade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: $maintext-color !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-logo-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
background: $graylight-color;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 100;
|
||||||
|
box-shadow: 0 1px 1px rgba(0, 21, 41, .08);
|
||||||
|
|
||||||
|
& .sidebar-logo-link {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
& .sidebar-logo {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .sidebar-title {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 50px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.collapse {
|
||||||
|
.sidebar-logo {
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
162
src/views/layout/index.vue
Normal file
162
src/views/layout/index.vue
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<template>
|
||||||
|
<el-container id="layoutBox">
|
||||||
|
<el-aside class="animation" :class="isCollapse ? 'menuW' : 'menuS'">
|
||||||
|
<Menubar />
|
||||||
|
</el-aside>
|
||||||
|
<el-container>
|
||||||
|
<el-header>
|
||||||
|
<Headbar />
|
||||||
|
</el-header>
|
||||||
|
<el-main>
|
||||||
|
<router-view :key="rouKey" />
|
||||||
|
</el-main>
|
||||||
|
<el-footer>
|
||||||
|
<BlogBox />
|
||||||
|
</el-footer>
|
||||||
|
</el-container>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import mqtt from '@/utils/mqtt'
|
||||||
|
import Menubar from '@/views/layout/components/menubar'
|
||||||
|
import Headbar from '@/views/layout/components/headbar'
|
||||||
|
import BlogBox from '@/views/layout/components/BlogBox'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Layout',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
frequencySetTimeout: null // 对频信息保持
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Menubar,
|
||||||
|
Headbar,
|
||||||
|
BlogBox
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
rouKey () {
|
||||||
|
return this.$route.path
|
||||||
|
},
|
||||||
|
isCollapse () {
|
||||||
|
return this.$store.state.app.isCollapse
|
||||||
|
},
|
||||||
|
shop_id () {
|
||||||
|
return this.$store.state.user.shop_id
|
||||||
|
},
|
||||||
|
airList () {
|
||||||
|
return this.$store.state.airList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
/* init */
|
||||||
|
this.$store.commit('app/setIsMobile')// 获取客户端平台类型
|
||||||
|
this.$store.dispatch('fetchAdminList')// 获取管理员列表
|
||||||
|
this.$store.dispatch('fetchSiteList')// 获取站点列表
|
||||||
|
this.$store.dispatch('fetchRouteList')// 获取航线列表
|
||||||
|
this.$store.dispatch('fetchQuestList')// 获取订单列表 ps:待发货及待收货 并且不是退款成功状态
|
||||||
|
this.$store.dispatch('fetchAirList')// 获取飞机列表
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
/**
|
||||||
|
* @description: 异步拿到飞机列表之后 再进行一些初始化操作
|
||||||
|
* @param {*} res 飞机列表
|
||||||
|
*/
|
||||||
|
airList (res) {
|
||||||
|
/* mqtt */
|
||||||
|
mqtt.mqttConf()// 连接mqtt
|
||||||
|
// 订阅飞机信息
|
||||||
|
mqtt.doSubscribe('planeState/+', (mqttRes) => {
|
||||||
|
res.forEach(plane => {
|
||||||
|
if (mqttRes.topic.indexOf(plane.macadd) > -1) {
|
||||||
|
// 反序列化 mqtt信息
|
||||||
|
const jsonData = JSON.parse(mqttRes.msg.trim())
|
||||||
|
// 更新mqtt信息 选择性更新状态
|
||||||
|
for (const key in jsonData) {
|
||||||
|
if (key === 'heartBeat') { // 每次接收到心跳 heartRandom属性 创建一个随机数 用于watch监听
|
||||||
|
plane.planeState.heartRandom = Math.random()
|
||||||
|
}
|
||||||
|
if (key === 'position') { // 如果是飞机位置信息 则不是直接刷新状态 而是累计 到数组 以便于画出飞机路径
|
||||||
|
let position = jsonData.position
|
||||||
|
position = position.replace(/([a-zA-Z0-9]+):/g, '"$1":')// mcu过来的json格式修正一下 键没有引号 改成"lng":
|
||||||
|
position = JSON.parse(position)
|
||||||
|
plane.planeState.position.push([position.lng / 10e6, position.lat / 10e6, Number(position.alt)])
|
||||||
|
if (plane.planeState.position.length > 1000) {
|
||||||
|
plane.planeState.position.shift()// 删除最早的经纬度
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
plane.planeState[key] = jsonData[key]// 按订阅信息 刷新飞机状态
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 订阅对频
|
||||||
|
mqtt.doSubscribe('crosFrequency/+', (res) => {
|
||||||
|
if (res.topic.indexOf('crosFrequency') > -1) {
|
||||||
|
this.$store.commit('setCrosFrequency', res.msg)// 设置对频信息
|
||||||
|
clearTimeout(this.frequencySetTimeout)// 接收到新的订阅信息 清除之前的倒计时
|
||||||
|
this.frequencySetTimeout = setTimeout(() => { // 倒计时10秒 随之清除缓存中对频信息
|
||||||
|
this.$store.commit('setCrosFrequency', null)
|
||||||
|
}, 10000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 订阅游客下单频道
|
||||||
|
mqtt.doSubscribe(`refreshQuestList/${this.shop_id}`, (res) => {
|
||||||
|
if (res.topic.indexOf(`refreshQuestList/${this.shop_id}`) > -1) {
|
||||||
|
this.$store.dispatch('fetchSiteList')// 刷新站点列表
|
||||||
|
this.$store.dispatch('fetchQuestList')// 刷新订单列表 ps:待发货及待收货 并且不是退款成功状态
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
mqtt.mqttDestroy()// 断开mqtt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@/styles/theme.scss";
|
||||||
|
|
||||||
|
#layoutBox {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-aside {
|
||||||
|
background-color: #304156;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuW {
|
||||||
|
width: 60px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuS {
|
||||||
|
width: 210px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-header {
|
||||||
|
padding: 0 !important;
|
||||||
|
height: 50px !important;
|
||||||
|
line-height: 50px !important;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 99;
|
||||||
|
box-shadow: 0 0px 4px rgba(0, 21, 41, .08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-main {
|
||||||
|
padding: 0 !important;
|
||||||
|
height: 100%;
|
||||||
|
background-color: $white-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-footer {
|
||||||
|
height: 24px !important;
|
||||||
|
}
|
||||||
|
</style>
|
188
src/views/login.vue
Normal file
188
src/views/login.vue
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
<template>
|
||||||
|
<div class="login-container">
|
||||||
|
<div class="title-container f-s-32 fb m-t-20 m-l-20 l-h-32">
|
||||||
|
<img src="@/assets/logo.png" class="m-r-15">
|
||||||
|
<font class="f1 f-s-22">飞行魔方</font>
|
||||||
|
<font class="f2 f-s-10 m-l-5" style="vertical-align :top">v.1.0.1</font>
|
||||||
|
</div>
|
||||||
|
<el-form ref="loginForm" :model="loginForm" class="login-form" auto-complete="on" label-position="left">
|
||||||
|
<h3 class="f-s-30 m-b-15 fc">Authorize</h3>
|
||||||
|
<el-form-item prop="username">
|
||||||
|
<span class="svg-container"><i class="iconfont icon-yonghuziliao f-s-24"></i></span>
|
||||||
|
<el-input ref="username" v-model="loginForm.username" placeholder="用户" name="username" type="text" tabindex="1"
|
||||||
|
auto-complete="on" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="password">
|
||||||
|
<span class="svg-container">
|
||||||
|
<i class="iconfont icon-suoding f-s-24"></i>
|
||||||
|
</span>
|
||||||
|
<el-input :key="passwordType" ref="password" v-model="loginForm.password" :type="passwordType" placeholder="密码"
|
||||||
|
name="password" tabindex="2" auto-complete="on" @keyup.enter.native="handleLogin" />
|
||||||
|
<span class="show-pwd" @click="showPwd">
|
||||||
|
<i class="f-s-24"
|
||||||
|
:class="passwordType === 'password' ? 'iconfont icon-biyanjing' : 'iconfont icon-yanjing'"></i>
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-button icon="el-icon-user" :loading="loading" type="primary" style="width:100%;margin-bottom:30px;"
|
||||||
|
@click.native.prevent="handleLogin">登录</el-button>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { login } from '@/utils/api/user'
|
||||||
|
export default {
|
||||||
|
name: 'Login',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loginForm: {
|
||||||
|
username: '',
|
||||||
|
password: ''
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
passwordType: 'password'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* @description: 显示密码
|
||||||
|
*/
|
||||||
|
showPwd () {
|
||||||
|
if (this.passwordType === 'password') {
|
||||||
|
this.passwordType = ''
|
||||||
|
} else {
|
||||||
|
this.passwordType = 'password'
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.password.focus()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
login,
|
||||||
|
/**
|
||||||
|
* @description:提交登陆
|
||||||
|
*/
|
||||||
|
handleLogin () {
|
||||||
|
login({
|
||||||
|
username: this.loginForm.username,
|
||||||
|
password: this.loginForm.password
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
$bg: #283443;
|
||||||
|
$light_gray: #fff;
|
||||||
|
$cursor: #fff;
|
||||||
|
|
||||||
|
@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
|
||||||
|
.login-container .el-input input {
|
||||||
|
color: $cursor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset element-ui css */
|
||||||
|
.login-container {
|
||||||
|
.el-input {
|
||||||
|
display: inline-block;
|
||||||
|
height: 47px;
|
||||||
|
width: 85%;
|
||||||
|
|
||||||
|
input {
|
||||||
|
background: transparent;
|
||||||
|
border: 0px;
|
||||||
|
border-radius: 0px;
|
||||||
|
padding: 12px 5px 12px 15px;
|
||||||
|
color: $light_gray;
|
||||||
|
height: 47px;
|
||||||
|
caret-color: $cursor;
|
||||||
|
|
||||||
|
&:-webkit-autofill {
|
||||||
|
box-shadow: 0 0 0px 1000px $bg inset !important;
|
||||||
|
-webkit-text-fill-color: $cursor !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #454545;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$bg: #2d3a4b;
|
||||||
|
$dark_gray: #889aa4;
|
||||||
|
$light_gray: #eee;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color: $light_gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container {
|
||||||
|
min-height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
background-color: $bg;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
position: relative;
|
||||||
|
width: 520px;
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 160px 35px 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
&:first-of-type {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg-container {
|
||||||
|
padding: 6px 5px 6px 15px;
|
||||||
|
color: $dark_gray;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 30px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-container {
|
||||||
|
color: $light_gray;
|
||||||
|
|
||||||
|
img {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.f2 {
|
||||||
|
vertical-align: top;
|
||||||
|
color: $dark_gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-pwd {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 7px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: $dark_gray;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user