Merge remote-tracking branch 'origin/test'
@ -301,3 +301,11 @@ export function getOrgTreeById(orgId) {
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
/** 查询学生在地图下仿真使用时长和实训使用时长 */
|
||||
export function getUserDuration(params) {
|
||||
return request({
|
||||
url: `api/company/usage/students`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
@ -451,6 +451,27 @@ export function getSimulationDestinationList(group) {
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
/** 获取PSL盘状态 */
|
||||
export function getPslStatus(group, standCode) {
|
||||
return request({
|
||||
url: `/simulation/${group}/${standCode}/psl/status`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
/** 按下IBP盘按钮
|
||||
* @param {String} button
|
||||
* YXJZ-允许/禁止;
|
||||
* HSJC-互锁解除;
|
||||
* KM-开门;
|
||||
* GM-关门;
|
||||
* SD-试灯;
|
||||
*/
|
||||
export function pressPslButton(group, standCode, button) {
|
||||
return request({
|
||||
url: `/simulation/${group}/${standCode}/psl/${button}`,
|
||||
method: 'put'
|
||||
});
|
||||
}
|
||||
/** 查询运行线或者列车经过的站台 */
|
||||
export function getStandListByRunLineOrTrainCode(group, params) {
|
||||
return request({
|
||||
|
BIN
src/assets/psl_images/green_btn.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
src/assets/psl_images/green_off.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/psl_images/green_on.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/psl_images/key.png
Normal file
After Width: | Height: | Size: 12 KiB |
22
src/assets/psl_images/psl_icons.js
Normal file
@ -0,0 +1,22 @@
|
||||
import red_on from "@/assets/psl_images/red_on.png";
|
||||
import red_off from "@/assets/psl_images/red_off.png";
|
||||
import red_btn from "@/assets/psl_images/red_btn.png";
|
||||
import green_on from "@/assets/psl_images/green_on.png";
|
||||
import green_off from "@/assets/psl_images/green_off.png";
|
||||
import green_btn from "@/assets/psl_images/green_btn.png";
|
||||
import top_on from "@/assets/psl_images/top_on.png";
|
||||
import top_off from "@/assets/psl_images/top_off.png";
|
||||
import key from "@/assets/psl_images/key.png";
|
||||
const icons = {
|
||||
red_on,
|
||||
red_off,
|
||||
red_btn,
|
||||
green_on,
|
||||
green_off,
|
||||
green_btn,
|
||||
top_on,
|
||||
top_off,
|
||||
key
|
||||
};
|
||||
|
||||
export default icons;
|
BIN
src/assets/psl_images/red_btn.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
src/assets/psl_images/red_off.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/psl_images/red_on.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
src/assets/psl_images/top_off.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/psl_images/top_on.png
Normal file
After Width: | Height: | Size: 13 KiB |
223
src/jmapNew/theme/components/menus/dialog/psl.vue
Normal file
@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-dialogDrag
|
||||
class="xian-01__systerm psl-panel"
|
||||
:title="title"
|
||||
:visible.sync="show"
|
||||
width="500px"
|
||||
:before-close="doClose"
|
||||
:z-index="2000"
|
||||
:modal="false"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div class="psl-title">
|
||||
<div>{{ title }}</div>
|
||||
</div>
|
||||
<div class="psl-content">
|
||||
<!-- <div class="content-row">
|
||||
<div class="btn">
|
||||
<div class="btn-text">
|
||||
有滑动门/应急门未关闭且锁紧指示灯
|
||||
</div>
|
||||
<img
|
||||
:src="getIcon('top', indicators.lock ? 'on' : 'off')"
|
||||
/>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="content-row">
|
||||
<div class="cell">
|
||||
<div class="btn-text">PSL操作指示灯</div>
|
||||
<img
|
||||
:src="
|
||||
getIcon('red', indicators.pslczLight ? 'on' : 'off')
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="btn-text">互锁解除指示灯</div>
|
||||
<img
|
||||
:src="
|
||||
getIcon('red', indicators.hsjcLight ? 'on' : 'off')
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-row">
|
||||
<div class="cell">
|
||||
<div class="btn-text">开门状态指示灯</div>
|
||||
<img
|
||||
:src="getIcon('red', indicators.kmLight ? 'on' : 'off')"
|
||||
/>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="btn-text">关门状态指示灯</div>
|
||||
<img
|
||||
:src="
|
||||
getIcon('green', indicators.gmLight ? 'on' : 'off')
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-row">
|
||||
<div class="cell btn">
|
||||
<div class="btn-text">禁止</div>
|
||||
<img
|
||||
:src="getIcon('key')"
|
||||
:class="['key', { 'key-on': indicators.yxjzKey }]"
|
||||
@click="btnClickHandler('YXJZ')"
|
||||
/>
|
||||
<div class="btn-text-opt">允许</div>
|
||||
</div>
|
||||
<div class="cell btn">
|
||||
<div class="btn-text">禁止</div>
|
||||
<img
|
||||
:src="getIcon('key')"
|
||||
:class="['key', { 'key-on': indicators.hsjcKey }]"
|
||||
@click="btnClickHandler('HSJC')"
|
||||
/>
|
||||
<div class="btn-text-opt">互锁解除</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-row">
|
||||
<div class="cell btn">
|
||||
<div class="btn-text">开门按钮</div>
|
||||
<img
|
||||
:src="getIcon('red', 'btn')"
|
||||
@click="btnClickHandler('KM')"
|
||||
/>
|
||||
</div>
|
||||
<div class="cell btn">
|
||||
<div class="btn-text">关门按钮</div>
|
||||
<img
|
||||
:src="getIcon('green', 'btn')"
|
||||
@click="btnClickHandler('GM')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-row">
|
||||
<div class="cell btn">
|
||||
<div class="btn-text">试灯按钮</div>
|
||||
<img class="btn-sd btn-img"
|
||||
:src="getIcon('green', 'btn')"
|
||||
@mousedown.left="btnClickHandler('SD')"
|
||||
@mouseup.left="btnClickHandler('SD')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import icons from "@/assets/psl_images/psl_icons";
|
||||
import { getPslStatus, pressPslButton } from "@/api/simulation";
|
||||
let standCode, group;
|
||||
export default {
|
||||
name: "PSL",
|
||||
data() {
|
||||
return {
|
||||
dialogShow: false,
|
||||
indicators: {
|
||||
// lock: false,
|
||||
pslczLight: false,
|
||||
hsjcLight: false,
|
||||
kmLight: false,
|
||||
gmLight: false,
|
||||
yxjzKey: false,
|
||||
hsjcKey: false
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return "PSL";
|
||||
},
|
||||
|
||||
show() {
|
||||
return this.dialogShow && !this.$store.state.menuOperation.break;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doShow(selected) {
|
||||
this.dialogShow = true;
|
||||
this.$nextTick(function() {
|
||||
this.$store.dispatch("training/emitTipFresh");
|
||||
});
|
||||
standCode = selected.code;
|
||||
group = this.$route.query.group;
|
||||
this.initData(group, standCode);
|
||||
},
|
||||
|
||||
initData(group, standCode) {
|
||||
getPslStatus(group, standCode)
|
||||
.then(resp => {
|
||||
if (resp.data) {
|
||||
this.indicators = resp.data;
|
||||
}
|
||||
})
|
||||
.catch(err => {});
|
||||
},
|
||||
|
||||
doClose() {
|
||||
this.loading = false;
|
||||
this.dialogShow = false;
|
||||
this.$store.dispatch("training/emitTipFresh");
|
||||
},
|
||||
getIcon(type, status) {
|
||||
return status !== undefined
|
||||
? icons[`${type}_${status}`]
|
||||
: icons[type];
|
||||
},
|
||||
btnClickHandler(btnType) {
|
||||
pressPslButton(group, standCode, btnType);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"$store.state.socket.simulationPslStatus": function(val) {
|
||||
if (val && val[standCode]) {
|
||||
Object.assign(this.indicators, val[standCode])
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style vars="{icons}" lang="scss" scoped>
|
||||
.psl-title {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
color: #000;
|
||||
font-size: 18px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
.psl-content {
|
||||
.content-row {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
margin: 10px 0;
|
||||
.cell {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 120px;
|
||||
height: 100px;
|
||||
.key {
|
||||
transition: 0.1s;
|
||||
}
|
||||
.key-on {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.btn-text-opt {
|
||||
position: absolute;
|
||||
left: 80%;
|
||||
top: 35%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -9,6 +9,7 @@
|
||||
<notice-info ref="noticeInfo" pop-class="xian-01__systerm" />
|
||||
<stand-back-strategy ref="standBackStrategy" />
|
||||
<set-fault ref="setFault" pop-class="xian-01__systerm" />
|
||||
<psl ref="psl" pop-class="xian-01__systerm" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -22,6 +23,7 @@ import StandStopTime from './dialog/standStopTime';
|
||||
import StandDetainTrainAll from './dialog/standDetainTrainAll';
|
||||
import NoticeInfo from '@/jmapNew/theme/components/menus/childDialog/noticeInfo';
|
||||
import SetFault from '@/jmapNew/theme/components/menus/dialog/setFault';
|
||||
import Psl from '@/jmapNew/theme/components/menus/dialog/psl';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { DeviceMenu, OperateMode } from '@/scripts/ConstDic';
|
||||
import CMD from '@/scripts/cmdPlugin/CommandEnum';
|
||||
@ -39,7 +41,8 @@ export default {
|
||||
StandBackStrategy,
|
||||
StandStopTime,
|
||||
StandDetainTrainAll,
|
||||
SetFault
|
||||
SetFault,
|
||||
Psl
|
||||
},
|
||||
props: {
|
||||
selected: {
|
||||
@ -173,9 +176,16 @@ export default {
|
||||
// 故障模式菜单列表
|
||||
if (this.operatemode === OperateMode.FAULT) {
|
||||
this.menu = this.menuForce;
|
||||
console.log(this.menu);
|
||||
if (this.$store.state.training.prdType === '01') { //角色为车站值班员
|
||||
this.menu = [
|
||||
...this.menu,
|
||||
{
|
||||
label: 'PSL',
|
||||
handler: this.openPsl
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
doShow(point) {
|
||||
this.initMenu();
|
||||
@ -295,9 +305,11 @@ export default {
|
||||
// 触发故障管理
|
||||
triggerFaultManagement() {
|
||||
this.$store.dispatch('training/setTriggerFaultCount', this.selected);
|
||||
},
|
||||
// 打开PSL面板
|
||||
openPsl(){
|
||||
this.$refs.psl.doShow(this.selected)
|
||||
}
|
||||
// PSL
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -161,6 +161,7 @@ const CreateLesson = () => import('@/views/organization/draftLessonManage/create
|
||||
const EditLessonContent = () => import('@/views/organization/draftLessonManage/editLessonContent');
|
||||
const SortLesson = () => import('@/views/organization/draftLessonManage/sortLesson');
|
||||
const StudentManage = () => import('@/views/organization/classManage/studentManage/index');
|
||||
const DurationView = () => import('@/views/organization/classManage/durationView');
|
||||
const GradeList = () => import('@/views/organization/examManage/gradeList');
|
||||
const CreateRule = () => import('@/views/organization/ruleManage/createRule');
|
||||
const TotalGrade = () => import('@/views/organization/ruleManage/totalGrade');
|
||||
@ -585,6 +586,11 @@ export const publicAsyncRoute = [
|
||||
component: StudentManage,
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'durationView',
|
||||
component: DurationView,
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'examRule/draft/:mode/:ruleId/:lessonId',
|
||||
component: PublishExamRuleDraft,
|
||||
|
@ -189,6 +189,9 @@ function handle(state, data) {
|
||||
store.dispatch('training/over');
|
||||
}
|
||||
break;
|
||||
case 'Simulation_PslStatus':
|
||||
state.simulationPslStatus = msg;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@ -333,7 +336,8 @@ const socket = {
|
||||
simulationAlarmInfo: [], // 仿真报警信息
|
||||
deviceStateMessages: null, // 新版订阅设备状态消息
|
||||
simulationSpeed: 1, // 仿真倍速
|
||||
simulationPause: false
|
||||
simulationPause: false,
|
||||
simulationPslStatus: [], //PSL面板按钮状态信息
|
||||
},
|
||||
getters: {
|
||||
},
|
||||
|
@ -2,9 +2,9 @@ export function getBaseUrl() {
|
||||
let BASE_API;
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// BASE_API = 'https://joylink.club/jlcloud';
|
||||
// BASE_API = 'https://test.joylink.club/jlcloud';
|
||||
BASE_API = 'https://test.joylink.club/jlcloud';
|
||||
// BASE_API = 'http://192.168.8.107:9000'; // 袁琪
|
||||
BASE_API = 'http://192.168.3.83:9000'; // 旭强 有线
|
||||
// BASE_API = 'http://192.168.3.83:9000'; // 旭强 有线
|
||||
// BASE_API = 'http://192.168.8.114:9000'; // 旭强 无线
|
||||
// BASE_API = 'http://192.168.3.120:9000'; // 张赛
|
||||
// BASE_API = 'http://192.168.8.140:9000'; // 杜康
|
||||
|
170
src/views/organization/classManage/durationView.vue
Normal file
@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<div style="padding: 20px 0;">
|
||||
<div style="padding: 20px;font-size: 24px;text-align: center;">{{ $route.query.className }}</div>
|
||||
<el-button type="text" style="position: fixed;right: 20px;top: 70px;" @click="back">返回</el-button>
|
||||
<el-card class="box-card" style="padding: 22px 2px 0 2px;">
|
||||
<el-form ref="form" :model="form" :rules="rules">
|
||||
<el-row>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="地图:" prop="mapId">
|
||||
<el-select v-model="form.mapId" size="small" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in mapIdList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="开始日期:" prop="startTime">
|
||||
<el-date-picker
|
||||
v-model="form.startTime"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
:placeholder="$t('global.chooseDate')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="结束日期:" prop="endTime">
|
||||
<el-date-picker
|
||||
v-model="form.endTime"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
:placeholder="$t('global.chooseDate')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item>
|
||||
<div style="text-align: right;">
|
||||
<el-button type="primary" size="small" @click="queryFunction">查询</el-button>
|
||||
<el-button type="primary" size="small" @click="resetFunction">重置</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card class="box-card" style="margin-top: 15px">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
stripe
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column
|
||||
prop="userName"
|
||||
label="姓名"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="account"
|
||||
label="学号"
|
||||
/>
|
||||
<el-table-column prop="simulationDuration" label="仿真时长">
|
||||
<template slot-scope="scope">
|
||||
<span> {{ parseTime(scope.row.simulationDuration) }} </span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="trainingDuration" label="实训时长">
|
||||
<template slot-scope="scope">
|
||||
<span> {{ parseTime(scope.row.trainingDuration) }} </span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getUserDuration } from '@/api/company';
|
||||
import { getMapListByProjectCode } from '@/api/jmap/map';
|
||||
import { getSessionStorage } from '@/utils/auth';
|
||||
|
||||
export default {
|
||||
name: 'StudentManage',
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
mapId: '',
|
||||
startTime: '',
|
||||
endTime: ''
|
||||
},
|
||||
tableData: [],
|
||||
mapIdList: [],
|
||||
loadingDurationView: false,
|
||||
rules: {
|
||||
mapId: [
|
||||
{ required: true, message: '请选择地图', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
project() {
|
||||
return getSessionStorage('project');
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
getMapListByProjectCode(this.project).then(response => {
|
||||
this.mapIdList = response.data;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
queryFunction() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
let startTime = '';
|
||||
let endTime = '';
|
||||
if (this.form.startTime) {
|
||||
startTime = this.form.startTime + ' 00:00:00';
|
||||
}
|
||||
if (this.form.endTime) {
|
||||
endTime = this.form.endTime + ' 23:59:59';
|
||||
}
|
||||
const param = { mapId: this.form.mapId, startTime: startTime, endTime: endTime, clsId: this.$route.query.classId };
|
||||
getUserDuration(param).then(resp => {
|
||||
this.tableData = resp.data;
|
||||
}).catch((error) => {
|
||||
this.$message.error(`获取学生使用时长失败:${error.message}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
refresh() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.durationViewListPage.refresh(true);
|
||||
});
|
||||
},
|
||||
parseTime(time) {
|
||||
if (!time) { return ''; }
|
||||
const hours = Math.floor(time / 3600);
|
||||
const minutes = Math.floor((time % 3600) / 60);
|
||||
return hours + '小时' + minutes + '分';
|
||||
},
|
||||
resetFunction() {
|
||||
this.form.startTime = '';
|
||||
this.form.endTime = '';
|
||||
this.queryFunction();
|
||||
},
|
||||
back() {
|
||||
this.$router.go(-1);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.title_content {
|
||||
width: 100%;
|
||||
margin: 20px 0 30px;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.box-card{
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
@ -67,7 +67,7 @@ export default {
|
||||
{
|
||||
type: 'button',
|
||||
title: this.$t('global.operate'),
|
||||
width: '350',
|
||||
width: '450',
|
||||
buttons: [
|
||||
{
|
||||
name: '编辑班级',
|
||||
@ -87,6 +87,11 @@ export default {
|
||||
handleClick: this.handleGradeCheck,
|
||||
type: 'primary'
|
||||
},
|
||||
{
|
||||
name: '时长查看',
|
||||
handleClick: this.handleDurationView,
|
||||
type: 'primary'
|
||||
},
|
||||
{
|
||||
name: '删除',
|
||||
handleClick: this.handleDeleteClass,
|
||||
@ -139,6 +144,9 @@ export default {
|
||||
handleStudentDetail( index, row ) {
|
||||
this.$router.push({ path: '/info/studentManage', query: { classId: row.id, className: row.name, creatorId: row.creatorId} });
|
||||
},
|
||||
handleDurationView(index, row) {
|
||||
this.$router.push({ path: '/info/durationView', query: { classId: row.id, className: row.name } });
|
||||
},
|
||||
classCreate() {
|
||||
this.$refs.createClass.doShow();
|
||||
}
|
||||
|