@@ -2,7 +2,7 @@ | |||
export default { | |||
onLaunch: function() {}, | |||
onShow: function() {}, | |||
onHide: function() {} | |||
onHide: function() {}, | |||
} | |||
</script> | |||
@@ -11,7 +11,7 @@ | |||
@import "uview-ui/index.scss"; | |||
@font-face { | |||
font-family: 'Alimama ShuHeiTi';//字体别名 | |||
font-family: 'Alimama ShuHeiTi'; //字体别名 | |||
src: url('/static/fonts/alimamashuheiti.ttf') format('truetype'); | |||
font-weight: normal; | |||
font-style: normal; | |||
@@ -0,0 +1,38 @@ | |||
import http from '@/http/api.js' | |||
const url = '/api/business/classRoomCallApi' | |||
// 获取列表 | |||
export const page = (params) => { | |||
return http.request({ | |||
url: url + '/getPageList', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
// 获取不分页列表 | |||
export const list = (params) => { | |||
return http.request({ | |||
url: url + '/getNoPageList', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
// 获取点名列表某一项所有人员 | |||
export const detail = (data) => { | |||
return http.request({ | |||
url: '/api/business/personApi/pageQuery', | |||
method: 'post', | |||
data | |||
}) | |||
} | |||
// 获取点名任务列表 | |||
export const taskPage = (params) => { | |||
return http.request({ | |||
url: url + '/getTaskPageList', | |||
method: 'get', | |||
params | |||
}) | |||
} |
@@ -3,11 +3,23 @@ import { | |||
} from '@/http/config.js'; | |||
import Request from '@/utils/luch-request/index.js'; | |||
const http = new Request(options); | |||
import store from "@/store" | |||
http.interceptors.request.use((config) => { // 可使用async await 做异步操作 | |||
// 假设有token值需要在头部需要携带 | |||
let token = uni.getStorageSync('token'); | |||
if (token) { | |||
config.header['Authorization'] = 'Bearer ' + token; | |||
const jwt = decryptJWT(token); | |||
const exp = getJWTDate(jwt.exp); | |||
// token 已经过期 | |||
if (new Date() <= exp) { | |||
let refreshToken = store.state.refreshToken; | |||
// 携带刷新 token | |||
if (refreshToken) { | |||
config.header["X-" + 'Authorization'] = 'Bearer ' + refreshToken; | |||
} | |||
} | |||
} | |||
// #ifndef H5 | |||
@@ -22,10 +34,7 @@ http.interceptors.request.use((config) => { // 可使用async await 做异步操 | |||
return Promise.reject(config) | |||
}) | |||
http.interceptors.response.use((response) => { | |||
// 若有数据返回则通过 | |||
if (response.data.access_token || response.data.key) { | |||
return response.data | |||
} | |||
checkAndStoreAuthentication(response) | |||
// 服务端返回的状态码不等于200,则reject() | |||
if (response.data.code && response.data.code !== 200) { | |||
setTimeout(() => { | |||
@@ -49,6 +58,7 @@ http.interceptors.response.use((response) => { | |||
if (response.statusCode == 401) { | |||
const pages = getCurrentPages() | |||
const currentPage = pages[pages.length - 1] | |||
this.$store.dispatch('clearToken') | |||
uni.redirectTo({ | |||
url: `/pages/login?redirect=/${currentPage.route}` | |||
}) | |||
@@ -74,4 +84,21 @@ function decryptJWT(token) { | |||
function getJWTDate(timestamp) { | |||
return new Date(timestamp * 1000); | |||
} | |||
/** | |||
* 检查并存储授权信息 | |||
* @param res 响应对象 | |||
*/ | |||
const checkAndStoreAuthentication = (res) => { | |||
// 读取响应报文头 token 信息 | |||
let token = res.header['access-token']; | |||
let refreshToken = res.header['x-access-token']; | |||
// 判断是否是无效 token | |||
if (token === "invalid_token") { | |||
stroe.dispatch('clearToken') | |||
} | |||
// 判断是否存在刷新 token,如果存在则存储在本地 | |||
else if (refreshToken && token && token !== "invalid_token") { | |||
stroe.dispatch('setToken',{token,refreshToken}) | |||
} | |||
} | |||
export default http; |
@@ -57,7 +57,8 @@ | |||
{ | |||
"path": "pages/work/returnBed/index", | |||
"style": { | |||
"navigationBarTitleText": "归寝" | |||
"navigationBarTitleText": "归寝", | |||
"enablePullDownRefresh": true | |||
} | |||
}, | |||
{ | |||
@@ -71,7 +72,8 @@ | |||
{ | |||
"path": "pages/work/passengerFlow/index", | |||
"style": { | |||
"navigationBarTitleText": "客流" | |||
"navigationBarTitleText": "客流", | |||
"enablePullDownRefresh": true | |||
} | |||
}, | |||
{ | |||
@@ -85,7 +87,8 @@ | |||
{ | |||
"path": "pages/work/rollCall/index", | |||
"style": { | |||
"navigationBarTitleText": "点名" | |||
"navigationBarTitleText": "点名", | |||
"enablePullDownRefresh": true | |||
} | |||
}, | |||
{ | |||
@@ -89,6 +89,7 @@ | |||
let data = res.data | |||
this.$store.dispatch('setToken',{token:data.token}) | |||
this.$store.dispatch('getUserInfo') | |||
this.$store.dispatch('getAllOptions') | |||
this.NAV_TO('/') | |||
}).finally(() => { | |||
this.isLoading = false | |||
@@ -125,7 +125,7 @@ | |||
}, | |||
loginOut() { | |||
this.CONFIRM('您是否确认退出登录?').then(()=>{ | |||
this.$store.dispatch('clearToken') | |||
this.CLEAR_STORAGE() | |||
this.JUMP_TO('/pages/login') | |||
}) | |||
} | |||
@@ -37,7 +37,6 @@ | |||
</u-list> | |||
</view> | |||
</template> | |||
<script> | |||
export default { | |||
data() { | |||
@@ -46,8 +45,8 @@ | |||
isLoading: false, | |||
status: 'loadmore', //loading正在加载 loadmore加载更多 nomore没有更多了 | |||
page: { | |||
size: 8, | |||
page: 8, | |||
pageNum: 1, | |||
pageSize: 10, | |||
} | |||
} | |||
}, | |||
@@ -1,63 +1,66 @@ | |||
<template> | |||
<view style="height: 100%;"> | |||
<view class="title"> | |||
应到:8人 | |||
应到:{{list.length}}人 | |||
</view> | |||
<view style="padding: 26rpx;overflow: hidden;"> | |||
<view class="cate"> | |||
<view class="cateList"> | |||
<view v-for="item in cateList" :class="{active:item.value == cate}" :key="item.value" class="item" | |||
@click="cateItemClick(item)"> | |||
{{item.label}}({{item.value}}) | |||
{{item.label}}({{item.num}}) | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
<view style="height:calc(100% - 200rpx);overflow: auto;"> | |||
<view class="list"> | |||
<view v-for="(item, index) in list" :key="index" class="item" @click="popupShow(item)"> | |||
<view class="right"> | |||
<image src="@/static/image/test/image2.png" mode="aspectFill" | |||
style="width: 160rpx;height: 160rpx;border-radius:10rpx"></image> | |||
<view class="des"> | |||
<view class="top"> | |||
<view class="status" | |||
:style="{backgroundColor:cate == 1?'#0FAF76':cate == 2?'#EF2D2D':'#F49D19' }"> | |||
{{cateList.find(e=>e.value == cate).label}} | |||
<template v-for="(item, index) in list"> | |||
<view class="item" @click="popupShow(item)" :key="index" v-show="cateList.length&&cateList[cate].show(item)"> | |||
<view class="right"> | |||
<image src="@/static/image/test/image2.png" mode="aspectFill" | |||
style="width: 160rpx;height: 160rpx;border-radius:10rpx"></image> | |||
<view class="des"> | |||
<view class="top"> | |||
<view class="status" | |||
:style="{backgroundColor:cate == 0?'#0FAF76':cate == 1?'#EF2D2D':'#F49D19' }"> | |||
{{cateList.find(e=>e.value == cate)?cateList.find(e=>e.value == cate).label:''}} | |||
</view> | |||
<view class="name"> | |||
{{item.name}} | |||
</view> | |||
</view> | |||
<view class="name"> | |||
杨云 | |||
<view class="depart"> | |||
系部:{{item.majorName}} | |||
</view> | |||
<view class="bottom"> | |||
<view class="major">专业:{{item.depName}}</view> | |||
<view class="class">班级:{{item.personSetName}}</view> | |||
</view> | |||
</view> | |||
<view class="depart"> | |||
系部:安环部 | |||
</view> | |||
<view class="bottom"> | |||
<view class="major">专业:环艺</view> | |||
<view class="class">班级:第二班</view> | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
</template> | |||
</view> | |||
</view> | |||
<u-popup :show="show" mode="center" :round="10"> | |||
<view class="popupInfo"> | |||
<image src="/static/image/test/image.png" mode="widthFix" style="width: 480rpx;border-radius:10rpx"></image> | |||
<image src="/static/image/test/image.png" mode="widthFix" style="width: 480rpx;border-radius:10rpx"> | |||
</image> | |||
<view class="top"> | |||
<view class="status" :style="{backgroundColor:cate == 1?'#0FAF76':cate == 2?'#EF2D2D':'#F49D19' }"> | |||
{{cateList.find(e=>e.value == cate).label}} | |||
<view class="status" :style="{backgroundColor:cate == 0?'#0FAF76':cate == 1?'#EF2D2D':'#F49D19' }"> | |||
{{cateList.find(e=>e.value == cate)?cateList.find(e=>e.value == cate).label:''}} | |||
</view> | |||
<view class="name"> | |||
杨云 | |||
{{popupInfo.name}} | |||
</view> | |||
</view> | |||
<view class="depart"> | |||
系部:安环部 | |||
系部:{{popupInfo.majorName}} | |||
</view> | |||
<view class="bottom"> | |||
<view class="major">专业:环艺</view> | |||
<view class="class">班级:第二班</view> | |||
<view class="major">专业:{{popupInfo.depName}}</view> | |||
<view class="class">班级:{{popupInfo.personSetName}}</view> | |||
</view> | |||
<view class="closeBox"> | |||
<image @click="show = false" src="/static/image/close.png" class="close"></image> | |||
@@ -68,44 +71,77 @@ | |||
</template> | |||
<script> | |||
import { | |||
detail, | |||
page | |||
} from '@/api/work/rollCall.js' | |||
export default { | |||
data() { | |||
return { | |||
list: [{ | |||
name: 1 | |||
}, { | |||
name: 2 | |||
}, { | |||
name: 3 | |||
}], | |||
cateList: [{ | |||
value: 1, | |||
label: '实到' | |||
}, | |||
{ | |||
value: 2, | |||
label: '未到' | |||
}, | |||
{ | |||
value: 3, | |||
label: '趴桌子' | |||
}, | |||
{ | |||
value: 4, | |||
label: '玩手机' | |||
}, | |||
{ | |||
value: 5, | |||
label: '睡觉' | |||
}, | |||
], | |||
cate: 1, | |||
list: [], | |||
alarmList: [], | |||
cateList: [], | |||
cate: 0, | |||
checkboxValue: [], | |||
show: false, | |||
popupInfo: {}, | |||
} | |||
}, | |||
async mounted() { | |||
const { | |||
personSetId, | |||
taskId | |||
} = this.options | |||
// 班级人员 | |||
if (personSetId) { | |||
await detail({ | |||
personSetId, | |||
pageSize: 1000, | |||
pageNum: 1 | |||
}).then(res => { | |||
if (res.code != 200) return | |||
this.list = res.data.list | |||
}) | |||
} | |||
// 监控行为列表 | |||
if (taskId) { | |||
page({ | |||
taskId, | |||
pageSize: 1000, | |||
pageNum: 1 | |||
}).then(res => { | |||
if (res.code != 200) return | |||
this.alarmList = res.data.list | |||
this.list = this.list.map(e => { | |||
e.alarmTypes = [] | |||
// alarmType:lie_on_table 趴桌子 class_room_call 点名(实到) | |||
let arr = this.alarmList.filter(e1 => e1.personId == e.personId) | |||
e.alarmTypes = arr.map(e=>e.alarmType) | |||
return e | |||
}) | |||
this.cateList = [{ | |||
value: 0, | |||
label: '实到', | |||
show:(item)=>item.alarmTypes.includes('class_room_call'), | |||
num: this.list.filter(e=>e.alarmTypes.includes('class_room_call')).length, | |||
}, | |||
{ | |||
value: 1, | |||
label: '未到', | |||
show:(item)=>!item.alarmTypes.includes('class_room_call'), | |||
num: this.list.filter(e=>!e.alarmTypes.includes('class_room_call')).length | |||
}, | |||
{ | |||
value: 2, | |||
label: '趴桌子', | |||
show:(item)=>item.alarmTypes.includes('lie_on_table'), | |||
num: this.list.filter(e=>e.alarmTypes.includes('lie_on_table')).length, | |||
}, | |||
] | |||
}) | |||
} | |||
}, | |||
methods: { | |||
cateItemClick(item) { | |||
this.cate = item.value | |||
@@ -166,8 +202,9 @@ | |||
width: 100%; | |||
position: absolute; | |||
left: 0; | |||
bottom:-98rpx; | |||
bottom: -98rpx; | |||
text-align: center; | |||
.close { | |||
width: 60rpx; | |||
height: 60rpx; | |||
@@ -1,7 +1,8 @@ | |||
<template> | |||
<view style="height: 100%;"> | |||
<view class="topBox"> | |||
<SelectRadio v-model="search.departCalss" align="center" placeholder="请选择摄像头" :options="[{value:1,label:'走廊尽头(海康)'},{value:2,label:'厨房(海康)'},{value:3,label:'走廊尽头(海康)'},{value:4,label:'厨房(海康)'}]" /> | |||
<SelectRadio v-model="search.CameraId" align="center" placeholder="请选择摄像头" @change="refresh" | |||
:options="allOptions.monitorList" /> | |||
</view> | |||
<u-empty marginTop="100rpx" :show="false" mode="list" text="暂无数据"></u-empty> | |||
<u-list @scrolltolower="scrolltolower" style="height: calc(100% - 150rpx);"> | |||
@@ -10,19 +11,19 @@ | |||
<view :class="{whiteCard:true,active:checkboxValue.includes(item.name)}"> | |||
<view class="left"> | |||
<view class="row1"> | |||
教室(海康) | |||
{{item.cameraName}} | |||
</view> | |||
<view class="row2"> | |||
<view class=""> | |||
持续时间:20 | |||
持续时间:{{item.continueTime}} | |||
</view> | |||
<view class=""> | |||
相似度:0.5 | |||
相似度:{{item.similarity}} | |||
</view> | |||
</view> | |||
</view> | |||
<view class="right" v-show="!isManage"> | |||
<view class="btn" @click="NAV_TO('./detail')"> | |||
<view class="btn" @click="NAV_TO('./detail',{personSetId:item.personSetId,taskId:item.taskId})"> | |||
<view> | |||
<image src="@/static/image/seeBlue.png" mode="aspectFill"></image> | |||
</view> | |||
@@ -38,39 +39,24 @@ | |||
<script> | |||
import SelectRadio from "@/components/selectRadio.vue" | |||
import { | |||
taskPage | |||
} from '@/api/work/rollCall.js' | |||
export default { | |||
components: { | |||
SelectRadio | |||
}, | |||
data() { | |||
return { | |||
list: [{ | |||
name: 1 | |||
}, { | |||
name: 2 | |||
}, { | |||
name: 3 | |||
}, { | |||
name: 4 | |||
}, { | |||
name: 5 | |||
}, { | |||
name: 6 | |||
}, { | |||
name: 7 | |||
}, { | |||
name: 8 | |||
}, { | |||
name: 9 | |||
}], | |||
list: [], | |||
isLoading: false, | |||
status: 'loadmore', //loading正在加载 loadmore加载更多 nomore没有更多了 | |||
search: { | |||
class: '', | |||
CameraId: '', | |||
}, | |||
page: { | |||
size: 8, | |||
page: 8, | |||
pageNum: 1, | |||
pageSize: 10, | |||
}, | |||
checkboxValue: [], | |||
isManage: false, | |||
@@ -83,48 +69,41 @@ | |||
loadmore() { | |||
if (this.status != 'loadmore') return | |||
this.status = 'loading' | |||
setTimeout(() => { | |||
for (let i = 0; i < 1; i++) { | |||
this.list.push({}, {}, {}, {}, {}, {}) | |||
} | |||
// 获取到的总条数>=接口总条数 || 接口总条数为0 | |||
if (this.list.length >= 14) { | |||
taskPage({ | |||
...this.page, | |||
...this.search | |||
}).then(res => { | |||
if(res.code != 200)return | |||
res.data.list.forEach(e=>{ | |||
this.list.push(e) | |||
}) | |||
// 获取到的总条数>=接口总条数 | |||
if (this.list.length >= res.data.total) { | |||
this.status = 'nomore' | |||
} else { | |||
this.status = 'loadmore' | |||
} | |||
}, 2000) | |||
}) | |||
}, | |||
refresh() { | |||
this.status = 'loadmore' | |||
this.list = [] | |||
this.page.page = 1 | |||
this.page.pageNum = 1 | |||
this.loadmore() | |||
}, | |||
manageClick() { | |||
this.checkboxValue = [] | |||
this.isManage = this.isManage ? false : true | |||
}, | |||
radioChange(e) { | |||
if (e) { | |||
let ids = this.list.map(e => e.name) | |||
this.checkboxValue = ids | |||
} else { | |||
this.checkboxValue = [] | |||
} | |||
}, | |||
pullDownRefresh() { | |||
this.refresh() | |||
} | |||
}, | |||
onLoad() { | |||
console.log(this.allOptions) | |||
this.loadmore() | |||
}, | |||
onPullDownRefresh(){ | |||
uni.stopPullDownRefresh() | |||
this.refresh() | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.topBox { | |||
padding: 26rpx 30rpx; | |||
} | |||
@@ -159,9 +138,11 @@ | |||
padding: 30rpx; | |||
color: #333333; | |||
display: flex; | |||
.left{ | |||
.left { | |||
width: 76%; | |||
} | |||
.row1 { | |||
font-size: 32rpx; | |||
font-weight: 700; | |||
@@ -179,6 +160,7 @@ | |||
justify-content: right; | |||
flex: 1; | |||
padding-top: 19rpx; | |||
.btn { | |||
text-align: center; | |||
@@ -4,6 +4,9 @@ Vue.use(Vuex) | |||
import { | |||
getLoginUser | |||
} from '@/api/user.js' | |||
import { | |||
list as monitorList | |||
} from '@/api/monitor/index.js' | |||
let lifeData = {}; | |||
@@ -15,7 +18,7 @@ try { | |||
} | |||
// 需要永久存储,且下次APP启动需要取出的,在state中的变量名 | |||
let saveStateKeys = ['token', 'userInfo']; | |||
let saveStateKeys = ['token', 'userInfo', 'allOptions', 'refreshToken']; | |||
// 保存变量到本地存储中 | |||
const saveLifeData = function(key, value) { | |||
@@ -42,14 +45,17 @@ const store = new Vuex.Store({ | |||
// 如果上面从本地获取的lifeData对象下有对应的属性,就赋值给state中对应的变量 | |||
userInfo: lifeData.userInfo || null, | |||
token: lifeData.accessToken || '', | |||
refreshToken: '', | |||
refreshToken: lifeData.refreshToken || '', | |||
allOptions: lifeData.allOptions || { | |||
monitorList: [], //摄像头列表 | |||
}, | |||
}, | |||
actions: { | |||
async getUserInfo(context) { | |||
const { | |||
data | |||
} = await getLoginUser(); | |||
if(data){ | |||
if (data) { | |||
context.commit('$uStore', { | |||
name: 'userInfo', | |||
value: data | |||
@@ -57,17 +63,43 @@ const store = new Vuex.Store({ | |||
} | |||
return data; | |||
}, | |||
setToken(context,payload){ | |||
setToken(context, payload) { | |||
context.commit('$uStore', { | |||
name: 'token', | |||
value: payload.token | |||
}) | |||
context.commit('$uStore', { | |||
name: 'token', | |||
value: payload.refreshToken || '' | |||
}) | |||
}, | |||
clearToken(context){ | |||
clearToken(context) { | |||
context.commit('$uStore', { | |||
name: 'token', | |||
value: '' | |||
}) | |||
context.commit('refreshToken', { | |||
name: 'token', | |||
value: '' | |||
}) | |||
}, | |||
async getAllOptions(context) { | |||
let allOptions = {} | |||
// 摄像头列表 | |||
await monitorList({ | |||
pageSize: 1000 | |||
}).then(res => { | |||
if (res.code != 200) return | |||
allOptions.monitorList = res.data.list.map(e => { | |||
e.value = e.sensorId | |||
e.label = e.sensorName | |||
return e | |||
}) | |||
}) | |||
context.commit('$uStore', { | |||
name: 'allOptions', | |||
value: allOptions | |||
}) | |||
} | |||
}, | |||
mutations: { | |||