1、新增洗车机管理页面

2、新增环卫巡航页面
3、新增磅秤台账页面
This commit is contained in:
lankuixing 2024-07-31 22:04:10 +08:00
parent 6db5831718
commit 52e60f5d65
5 changed files with 583 additions and 112 deletions

View File

@ -36,13 +36,15 @@ vue混入代码主要是自适应相关的代码
布局目录,多了一个管理页
## 组件库文档地址
https://1x.antdv.com/components/icon/
## 路由说明
登录页 /login
### 管理页
用户管理 /manager/user
角色管理 /manager/role
菜单管理 /manager/menu
@ -50,25 +52,30 @@ https://1x.antdv.com/components/icon/
工序管理 /manager/organized
### 用户端
首页 /home
#### 有组织
有组织排放 /youzuzhi/paifang 待联调
有组织排放统计 /youzuzhi/paifangtongji 待联调
#### 无组织
无组织排放 /wuzuzhi/paifang 待联调
洗车机管理 /wuzuzhi/xichejiguanli 未做
洗车机管理 /wuzuzhi/xichejiguanli (已做)待联调
环境治理 /wuzuzhi/huanjingzhili 待联调
环卫巡航 /wuzuzhi/huanweixunhang 未做
环卫巡航 /wuzuzhi/huanweixunhang (已做)待联调
#### 清洁运输
清洁运输 /cleanTravel/home
厂区车辆台账 /cleanTravel/ledger
清洁运输趋势 /cleanTravel/trend
磅秤台账 /cleanTravel/bangcheng 未做
磅秤台账 /cleanTravel/bangcheng (已做)待联调
#### 视频广场
视频广场 /video/list 未做

View File

@ -1,9 +1,8 @@
<template>
<div style="text-align: left">
<div class="table-search" v-if="searchItems.length > 0">
<a-form class="ant-advanced-search-form" :form="form" @submit="handleSearch" layout="inline" @change="handleSearchFormChange">
<a-form class="ant-advanced-search-form" :form="form" @submit="handleSearch" layout="inline"
@change="handleSearchFormChange">
<a-form-item v-for="menuItem in searchItems" :key="menuItem.key" :label="menuItem.label">
<a-input
v-decorator="[menuItem.key]"
@ -15,10 +14,13 @@
:placeholder="menuItem.placeholder || ''"
v-else-if="menuItem.type === 'select'"
>
<a-select-option :value="cItem.value" v-for="cItem in menuItem.children || []" :key="cItem.value">{{ cItem.label }}</a-select-option>
<a-select-option :value="cItem.value" v-for="cItem in menuItem.children || []" :key="cItem.value">
{{ cItem.label }}
</a-select-option>
</a-select>
<a-range-picker
:placeholder="menuItem.placeholder || ''" v-decorator="[menuItem.key]" v-else-if="menuItem.type === 'dateRange'" format="YYYY-MM-DD" />
:placeholder="menuItem.placeholder || ''" v-decorator="[menuItem.key]"
v-else-if="menuItem.type === 'dateRange'" format="YYYY-MM-DD"/>
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit" v-if="!hideSearch">查询</a-button>
@ -27,18 +29,24 @@
</a-form-item>
</a-form>
</div>
<a-divider v-if="!hideButton && searchItems.length > 0" />
<a-divider v-if="!hideButton && searchItems.length > 0"/>
<div class="table-operations" v-if="!hideButton">
<a-button @click="addRow" type="primary">
添加
</a-button>
</div>
<a-table v-bind="tableProps" :pagination="page" :row-key="primaryKey" :columns="realColumns" :data-source="tableDataSource" @change="handleChange" >
<a-table v-bind="tableProps" :pagination="page" :row-key="primaryKey" :columns="realColumns"
:data-source="tableDataSource" @change="handleChange">
<span slot="action" slot-scope="text, record">
<a-button type="danger" size="small" @click="deleteRow(record)">删除</a-button>
<a-divider type="vertical" />
<a-divider type="vertical"/>
<a-button type="primary" size="small" @click="editRow(record)">编辑</a-button>
</span>
<span slot="details" slot-scope="text, record">
<!-- <a-button type="danger" size="small" @click="deleteRow(record)">删除</a-button>-->
<!-- <a-divider type="vertical"/>-->
<a-button type="primary" size="small" @click="viewDetails(record)">详情</a-button>
</span>
</a-table>
<a-modal
:title="dialog.title"
@ -61,7 +69,9 @@
v-else-if="menuItem.type === 'select'"
v-bind="menuItem.props || {}"
>
<a-select-option :value="cItem.value" v-for="cItem in menuItem.children || []" :key="cItem.value">{{ cItem.label }}</a-select-option>
<a-select-option :value="cItem.value" v-for="cItem in menuItem.children || []" :key="cItem.value">
{{ cItem.label }}
</a-select-option>
</a-select>
<a-tree-select
v-bind="menuItem.props || {}"
@ -74,9 +84,12 @@
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
v-else-if="menuItem.type === 'treeSelect'"
>
<a-select-option :value="cItem.value" v-for="cItem in menuItem.children || []" :key="cItem.value">{{ cItem.label }}</a-select-option>
<a-select-option :value="cItem.value" v-for="cItem in menuItem.children || []" :key="cItem.value">
{{ cItem.label }}
</a-select-option>
</a-tree-select>
<a-range-picker v-bind="menuItem.props || {}" v-decorator="gtDecorator(menuItem)" v-else-if="menuItem.type === 'dateRange'" format="YYYY-MM-DD" />
<a-range-picker v-bind="menuItem.props || {}" v-decorator="gtDecorator(menuItem)"
v-else-if="menuItem.type === 'dateRange'" format="YYYY-MM-DD"/>
</a-form-item>
</a-form>
</a-modal>
@ -85,12 +98,12 @@
<script>
export default {
name: "user",
data () {
data() {
return {
isValidate: false,
tableDataSource: [],
form: this.$form.createForm(this, { name: `form_${new Date().valueOf()}` }),
editForm: this.$form.createForm(this, { name: `edit_form_${new Date().valueOf()}` }),
form: this.$form.createForm(this, {name: `form_${new Date().valueOf()}`}),
editForm: this.$form.createForm(this, {name: `edit_form_${new Date().valueOf()}`}),
dialog: {
title: '',
visible: false,
@ -126,12 +139,12 @@ export default {
//
formItems: {
type: Array,
default: ()=> []
default: () => []
},
//
columns: {
type: Array,
default: ()=> []
default: () => []
},
primaryKey: {
type: String,
@ -144,36 +157,37 @@ export default {
},
},
computed: {
searchItems () {
searchItems() {
return this.formItems.filter(item => !!item.isSearch)
},
realColumns () {
if (this.hideAction) return [ ...this.columns ]
realColumns() {
if (this.hideAction) return [...this.columns]
return [
...this.columns,
{
title: '操作',
dataIndex: 'action',
scopedSlots: { customRender: 'action' },
scopedSlots: {customRender: 'action'},
fixed: 'right',
width: 155
},
]
}
},
watch: {
},
watch: {},
mounted() {
this.handleSearch()
},
methods: {
gtDecorator (menuItem) {
viewDetails(data){
this.$emit('viewDetails',data)
},
gtDecorator(menuItem) {
if (menuItem.rules) {
this.isValidate = true
return [
menuItem.key,
{ rules: menuItem.rules }
{rules: menuItem.rules}
]
}
return [
@ -181,9 +195,9 @@ export default {
]
},
handleSearchFormChange(changedFields) {
this.searchFormModel = { ...this.searchFormModel, ...changedFields }
this.searchFormModel = {...this.searchFormModel, ...changedFields}
},
addRow () {
addRow() {
this.handleCancel()
this.$emit('edit', false)
this.dialog.visible = true
@ -191,7 +205,7 @@ export default {
async editRow(row) {
if (this.apiConf.detailApi) {
const {api, method, detailHandler} = this.apiConf.detailApi
const { data } = await this[`$${method}`](api, { [this.primaryKey]: row[this.primaryKey] })
const {data} = await this[`$${method}`](api, {[this.primaryKey]: row[this.primaryKey]})
this.selectRow = detailHandler && detailHandler(data) || data
} else {
this.selectRow = row
@ -215,7 +229,7 @@ export default {
cancelText: '再想想',
onOk() {
const { api,deleteKey, paramsType, method } = self.apiConf.deleteApi
const {api, deleteKey, paramsType, method} = self.apiConf.deleteApi
let params = {}
if (paramsType === 'Array') {
params = [row[self.primaryKey]]
@ -229,9 +243,8 @@ export default {
});
},
handleChange ({ pageSize, current }) {
handleChange({pageSize, current}) {
this.page.pageSize = pageSize
this.page.current = current
this.handleSearch()
@ -242,8 +255,8 @@ export default {
async handleSearch(e) {
e && e.preventDefault();
const values = this.form.getFieldsValue()
const { api, noPage, method } = this.apiConf.listApi
const { data } = await this[`$${method}`](api, {
const {api, noPage, method} = this.apiConf.listApi
const {data} = await this[`$${method}`](api, {
...values,
pageSize: this.page.pageSize,
current: this.page.current,
@ -261,13 +274,13 @@ export default {
this.editForm.validateFields(async (err, values) => {
if (!err) {
if (this.dialog.isEdit) {
const { api, method, updateKey } = this.apiConf.editApi
const {api, method, updateKey} = this.apiConf.editApi
await this[`$${method}`](api, {
[updateKey || this.primaryKey]: this.selectRow[this.primaryKey],
...values
})
} else {
const { api, method } = this.apiConf.addApi
const {api, method} = this.apiConf.addApi
await this[`$${method}`](api, values)
}
this.handleCancel()
@ -277,13 +290,13 @@ export default {
} else {
const values = this.editForm.getFieldsValue()
if (this.dialog.isEdit) {
const { api, method, updateKey } = this.apiConf.editApi
const {api, method, updateKey} = this.apiConf.editApi
await this[`$${method}`](api, {
[updateKey || this.primaryKey]: this.selectRow[this.primaryKey],
...values
})
} else {
const { api, method } = this.apiConf.addApi
const {api, method} = this.apiConf.addApi
await this[`$${method}`](api, values)
}
this.handleCancel()
@ -306,9 +319,11 @@ export default {
.table-operations {
margin-bottom: 16px;
}
.ant-form-item {
margin-bottom: 0;
}
.ant-divider-horizontal {
margin: 16px 0;
}

View File

@ -1,37 +1,31 @@
<template>
<div>
<a-page-header
:ghost="false"
style="border-bottom: 1px solid rgb(235, 237, 240)"
title="厂区车辆台账"
title="磅秤台账"
@back="() => $router.go(-1)"
/>
<a-row :gutter="16">
<a-col :span="6">
<a-card title="运输车辆" size="small">
<p>card content</p>
</a-card>
</a-col>
<a-col :span="6">
<a-card title="国Ⅵ输车辆" size="small">
<p>card content</p>
</a-card>
</a-col>
<a-col :span="6">
<a-card title="非道路工程机械" size="small">
<p>card content</p>
</a-card>
</a-col>
<a-col :span="6">
<a-card title="燃油运输车辆" size="small">
<p>card content</p>
</a-card>
</a-col>
</a-row>
<div class="type-button">
<div :class="['tabs-button',activeTabsButton == null?'active-tabs-button':'']" @click="tabsChange(null)">
全部
</div>
<div :class="['tabs-button',activeTabsButton == 1?'active-tabs-button':'']" @click="tabsChange(1)">
东门
</div>
<div :class="['tabs-button',activeTabsButton == 2?'active-tabs-button':'']" @click="tabsChange(2)">
东门
</div>
<div :class="['tabs-button',activeTabsButton == 3?'active-tabs-button':'']" @click="tabsChange(3)">
东门
</div>
<div :class="['tabs-button',activeTabsButton == 4?'active-tabs-button':'']" @click="tabsChange(4)">
东门
</div>
</div>
<a-divider />
<div class="data-info">
<div class="left-table">
<Curd
hide-action
hide-search
@ -40,23 +34,62 @@
:api-conf="apiConf"
:form-items="formItems"
@edit="editHandler"
@viewDetails="viewDetails"
>
<template slot="search-button" slot-scope="{ search }">
<a-button type="primary" @click="exportList(search)">导出历史数据</a-button>
</template>
</Curd>
</div>
<div class="right-details" v-if="isShowInfoDetails">
<div class="header-title">驾驶人详情</div>
<img src=""/>
<div class="group-button">
<div :class="['_button',activeButton == 1?'active_button':'']" @click="viewImgByType(1)">出入场抓拍图</div>
<div :class="['_button',activeButton == 2?'active_button':'']" @click="viewImgByType(2)">行驶证-正面</div>
<div :class="['_button',activeButton == 3?'active_button':'']" @click="viewImgByType(3)">行驶证-反面</div>
<div :class="['_button',activeButton == 4?'active_button':'']" @click="viewImgByType(4)">环保清单</div>
</div>
<div class="details-info">
<div class="details-info-item">
<div class="_title">排放标准</div>
<div class="_title">国0</div>
</div>
<div class="details-info-item">
<div class="_title">VIN</div>
<div class="_title">国0</div>
</div>
<div class="details-info-item">
<div class="_title">发动机号</div>
<div class="_title">国0</div>
</div>
<div class="details-info-item">
<div class="_title">注册日期</div>
<div class="_title">国0</div>
</div>
</div>
<div class="footer-button">
<Button type="primary" @click="closeDetails">关闭详情</Button>
</div>
</div>
</div>
</div>
</template>
<script>
import Curd from "../../components/smallCommon/Curd.vue";
import moment from 'moment'
export default {
name: "user",
layout: "user",
components: {
Curd
},
data () {
data() {
return {
isEdit: false,
roles: [],
@ -80,7 +113,7 @@ export default {
dataIndex: 'newCar',
key: 'newCar',
title: '新能源',
customRender (text) {
customRender(text) {
if (text) return '是'
return '否'
}
@ -94,16 +127,26 @@ export default {
dataIndex: 'createDateTime',
key: 'createDateTime',
title: '出厂日期'
}
},
{
title: '操作',
key: 'operation',
scopedSlots: {customRender: 'details'},
fixed: 'right',
width: 100
},
],
apiConf: {
listApi: { api: '/api/Ledger/list', method: 'get' },
}
listApi: {api: '/api/Ledger/list', method: 'get'},
},
isShowInfoDetails: false,
activeButton: 1,
activeTabsButton: null
}
},
computed: {
formItems () {
formItems() {
return [
{
type: 'dateRange',
@ -112,7 +155,7 @@ export default {
isSearch: true,
placeholder: ['请选择开始日期', '请选择结束日期'],
rules: [
{ required: true, message: '请选择时间范围' }
{required: true, message: '请选择时间范围'}
],
fields: ['startTime', 'endTime']
}
@ -123,13 +166,30 @@ export default {
},
methods: {
editHandler (isEdit) {
editHandler(isEdit) {
this.isEdit = isEdit
},
exportList ({ time }) {
const [ start, end ] = time
exportList({time}) {
const [start, end] = time
const fmt = 'YYYY-MM-DD'
window.open(`http://101.43.201.20:5000/api/Ledger/export?start=${moment(start).format(fmt)}&end=${moment(end).format(fmt)}`, '_blank')
},
viewDetails(data) {
console.log('data:', data)
this.isShowInfoDetails = true
},
//
viewImgByType(value) {
this.activeButton = value
},
//
closeDetails() {
this.isShowInfoDetails = false
},
//
tabsChange(value){
this.activeTabsButton = value
}
}
@ -137,4 +197,99 @@ export default {
</script>
<style scoped lang="less">
.type-button {
margin: 20px 300px;
display: flex;
flex-flow: row;
justify-content: space-between;
.tabs-button {
height: 40px;
cursor: pointer;
border: 1px solid #e8e8e8;
width: 200px;
text-align: center;
line-height: 40px;
}
.active-tabs-button {
background: #0088ff;
color: #FFFFFF;
}
}
.data-info {
display: flex;
flex-flow: row;
.left-table {
flex: 1;
}
.right-details {
width: 600px;
height: 700px;
display: flex;
flex-flow: column;
align-items: center;
border: 1px solid #e8e8e8;
margin-left: 10px;
.header-title {
font-size: 18px;
text-align: center;
}
img {
margin-top: 10px;
width: 500px;
height: 300px;
}
.group-button {
margin-top: 20px;
display: flex;
flex-flow: row;
width: 500px;
justify-content: space-between;
._button {
width: 100px;
height: 50px;
border: 1px solid #777777;
cursor: pointer;
text-align: center;
line-height: 50px;
}
.active_button {
background: #0088ff;
color: #FFFFFF;
}
}
.details-info {
padding-left: 50px;
width: 100%;
flex: 1;
display: flex;
flex-flow: column;
justify-content: space-around;
align-items: flex-start;
.details-info-item {
display: flex;
flex-flow: row;
}
}
.footer-button {
height: 30px;
margin-bottom: 10px;
cursor: pointer;
}
}
}
</style>

View File

@ -1,13 +1,38 @@
<template>
<div class="huanweixunhang">
<a-page-header
:ghost="false"
style="border-bottom: 1px solid rgb(235, 237, 240)"
title="环卫巡航"
@back="() => $router.go(-1)"
/>
<div class="_map">
<Map></Map>
</div>
</div>
</template>
<script>
import Map from "../../components/Map.vue";
export default {
name: "huanweixunhang"
name: "huanweixunhang",
components: {
Map
}
}
</script>
<template>
<div class="huanweixunhang"></div>
</template>
<style scoped lang="less">
.huanweixunhang {
height: 100vh;
display: flex;
flex-flow: column;
._map {
height: 0;
flex: 1;
position: relative;
}
}
</style>

View File

@ -1,13 +1,282 @@
<template>
<div class="xichejiguanli">
<a-page-header
:ghost="false"
style="border-bottom: 1px solid rgb(235, 237, 240)"
title="洗车机管理"
@back="() => $router.go(-1)"
/>
<div class="_body">
<div class="left-list">
<div :class="['car-washing-machine-tabs',activeCarTabs ==1?'active-car-washing-machine-tabs':'']"
@click="carChange(1)">
<div class="rank-num">1</div>
<div class="car-washing-machine-name">西门洗车</div>
<div class="state-icon">
正常
</div>
</div>
<div :class="['car-washing-machine-tabs',activeCarTabs ==2?'active-car-washing-machine-tabs':'']"
@click="carChange(2)">
<div class="rank-num">1</div>
<div class="car-washing-machine-name">西门洗车</div>
<div class="state-icon">
正常
</div>
</div>
<div :class="['car-washing-machine-tabs',activeCarTabs ==3?'active-car-washing-machine-tabs':'']"
@click="carChange(3)">
<div class="rank-num">1</div>
<div class="car-washing-machine-name">西门洗车</div>
<div class="state-icon">
正常
</div>
</div>
</div>
<div class="center-content">
<div class="map-content">
<Map></Map>
</div>
<div class="table-content">
<Curd
hide-action
hide-search
hide-button
:columns="columns"
:api-conf="apiConf"
:form-items="formItems"
@edit="editHandler"
>
<template slot="search-button" slot-scope="{ search }">
<a-button type="primary" @click="exportList(search)">导出历史数据</a-button>
</template>
</Curd>
</div>
</div>
<div class="right-content">
<div class="_title">洗车机视频</div>
<div class="car-wash-video">
</div>
<div class="_title">洗车机运行信息</div>
<div class="car-run-info">
<div class="_title" style="text-align: center;">xxx洗车机</div>
<div class="details-info">
<div class="details-info-item">
<div class="_title">运行状态</div>
<div class="_title">正常</div>
</div>
<div class="details-info-item">
<div class="_title">冲洗压力</div>
<div class="_title">500KPA</div>
</div>
<div class="details-info-item">
<div class="_title">冲洗电流</div>
<div class="_title">50A</div>
</div>
<div class="details-info-item">
<div class="_title">冲洗电压</div>
<div class="_title">500KV</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Map from "../../components/Map.vue";
import Curd from "../../components/smallCommon/Curd.vue";
import moment from "moment/moment";
export default {
name: "xichejiguanli"
name: "xichejiguanli",
components: {
Map,
Curd
},
data() {
return {
activeCarTabs: 1,
columns: [
{
dataIndex: 'outTime',
key: 'outTime',
title: '进场日期'
},
{
dataIndex: 'carNum',
key: 'carNum',
title: '车牌号'
},
{
dataIndex: 'carModel',
key: 'carModel',
title: '车型'
},
{
dataIndex: 'createDateTime',
key: 'createDateTime',
title: '清洗时间'
}
],
apiConf: {
listApi: {api: '/api/Ledger/list', method: 'get'},
},
}
},
computed: {
formItems() {
return [
{
type: 'dateRange',
key: 'time',
label: '日期',
isSearch: true,
placeholder: ['请选择开始日期', '请选择结束日期'],
rules: [
{required: true, message: '请选择时间范围'}
],
fields: ['startTime', 'endTime']
}
]
}
},
methods: {
carChange(value) {
this.activeCarTabs = value
},
editHandler(isEdit) {
this.isEdit = isEdit
},
exportList({time}) {
const [start, end] = time
const fmt = 'YYYY-MM-DD'
window.open(`http://101.43.201.20:5000/api/Ledger/export?start=${moment(start).format(fmt)}&end=${moment(end).format(fmt)}`, '_blank')
},
}
}
</script>
<template>
<div class="xichejiguanli"></div>
</template>
<style scoped lang="less">
.xichejiguanli {
height: 100vh;
display: flex;
flex-flow: column;
._body {
flex: 1;
display: flex;
flex-flow: row;
padding: 20px;
.left-list {
width: 240px;
height: 100%;
border: 1px solid #e8e8e8;
border-radius: 10px;
.car-washing-machine-tabs {
height: 40px;
display: flex;
flex-flow: row;
align-items: center;
padding: 10px;
cursor: pointer;
border: 1px solid #e8e8e8;
margin-top: 10px;
&:first-child {
margin-top: 0;
}
border-radius: 10px;
.rank-num {
}
.car-washing-machine-name {
padding-left: 10px;
flex: 1;
}
}
.active-car-washing-machine-tabs {
background: #0088ff;
color: #FFFFFF;
}
}
.center-content {
flex: 1;
margin: 0 10px;
border: 1px solid #e8e8e8;
border-radius: 10px;
display: flex;
flex-flow: column;
.map-content {
height: 400px;
width: 100%;
position: relative;
}
.table-content {
flex: 1;
padding-top: 10px;
}
}
.right-content {
width: 300px;
border: 1px solid #e8e8e8;
border-radius: 10px;
padding: 10px;
._title {
height: 30px;
line-height: 30px;
font-size: 16px;
font-weight: 700;
}
.car-wash-video {
margin: 20px 0;
height: 400px;
border: 1px solid #e8e8e8;
}
.car-run-info {
margin-top: 20px;
border: 1px solid #e8e8e8;
height: 260px;
padding: 10px;
display: flex;
flex-flow: column;
.details-info {
width: 100%;
flex: 1;
display: flex;
flex-flow: column;
justify-content: space-around;
align-items: flex-start;
.details-info-item {
display: flex;
flex-flow: row;
}
}
}
}
}
}
</style>