Commit ddbc4ea3 authored by zhangzhaohui's avatar zhangzhaohui

页面开发

parent 790435ee
This diff is collapsed.
...@@ -7,7 +7,12 @@ ...@@ -7,7 +7,12 @@
"build": "vue-cli-service build" "build": "vue-cli-service build"
}, },
"dependencies": { "dependencies": {
"axios": "^1.4.0",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"echarts": "^5.1.1",
"js-cookie": "^3.0.5",
"vant": "^2.12.54",
"vconsole": "^3.15.1",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-router": "^3.2.0", "vue-router": "^3.2.0",
"vuex": "^3.4.0" "vuex": "^3.4.0"
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title> <title><%= htmlWebpackPlugin.options.title %></title>
</head> </head>
......
<template> <template>
<div id="app"> <div id="app">
<div id="nav"> <keep-alive :include="include">
<router-link to="/">Home</router-link> | <router-view/>
<router-link to="/about">About</router-link> </keep-alive>
</div>
<router-view/>
</div> </div>
</template> </template>
<script>
<style lang="scss"> export default {
#app { computed: {
font-family: Avenir, Helvetica, Arial, sans-serif; include() {
-webkit-font-smoothing: antialiased; return ['Home', 'Search']
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
} }
} }
} }
</script>
<style lang="scss">
</style> </style>
import request from '../utils/request'
// 获取验证码
export function getCodeImg() {
return request({
url: '/code',
headers: {
isToken: false
},
method: 'get'
})
}
// 登录
export function login(data) {
return request({
url: '/auth/login',
method: 'post',
data
})
}
// 获取用户信息
export function getUserInfo() {
return request({
url: '/system/user/getInfo',
method: 'get'
})
}
// 获取处理列表
export function getEventList(params) {
return request({
url: 'wcwy/api/event',
params
})
}
// 获取处理详情
export function getEventDetail(id) {
return request({
url: '/wcwy/eventInfo/' + id
})
}
// 获取处理后详情
export function getEventInfo(id) {
return request({
url: '/wcwy/eventInfo/get/' + id
})
}
// 获取消息列表
export function getMessageList(params) {
return request({
url: '/wcwy/api/eventMessage',
params
})
}
// 获取消息详情
export function getMessageDetail(id) {
return request({
url: '/wcwy/api/eventMessage/getInfo/' + id
})
}
// 上传图片
export function uploadImg(data) {
return request({
url: '/file/upload',
method: 'post',
data,
})
}
// 获取未读消息数量
export function getMessageCount() {
return request({
url: '/wcwy/api/eventMessage/getUnreadCnt'
})
}
// 处理事件
export function handleEvent(data) {
return request({
url: '/wcwy/api/event/process',
method: 'post',
data,
})
}
// 获取事件类型统计
export function getTypeChartData() {
return request({
url: '/wcwy/api/event/getEventCategoryStatics'
})
}
// 获取事件处理量统计
export function getCountChartData() {
return request({
url: '/wcwy/api/event/getEventProcessCntStatics'
})
}
\ No newline at end of file
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
...@@ -2,9 +2,18 @@ import Vue from 'vue' ...@@ -2,9 +2,18 @@ import Vue from 'vue'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import store from './store' import store from './store'
import VConsole from 'vconsole'
import './utils/rem'
import './static/reset.css'
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);
Vue.config.productionTip = false Vue.config.productionTip = false
router.afterEach((to, from) => {
document.title = to.meta.title
})
const vConsole = new VConsole()
new Vue({ new Vue({
router, router,
store, store,
......
import Vue from 'vue' import Vue from 'vue'
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter) Vue.use(VueRouter)
const routes = [ const routes = [
{ {
path: '/', path: '/home',
name: 'Home', name: 'Home',
component: Home component: () => import('../views/Home.vue'),
children:[
{
path: '',
name: 'EventList',
component: () => import('../views/EventList.vue'),
meta: {
title: '事件'
}
},
{
path: '/message',
name: 'Message',
component: () => import('../views/Message.vue'),
meta: {
title: '消息'
}
},
{
path: '/statistics',
name: 'Statistics',
component: () => import('../views/Statistics.vue'),
meta: {
title: '统计'
}
}
]
},
{
path: '/',
name: 'Login',
component: () => import('../views/Login.vue'),
meta: {
title: '登录'
}
},
{
path: '/event-detail',
name: 'EventDetail',
component: () => import('../views/EventDetail.vue'),
meta: {
title: '事件详情'
}
},
{
path: '/search',
name: 'Search',
component: () => import('../views/Search.vue'),
meta: {
title: '搜索'
}
}, },
{ {
path: '/about', path: '/handle',
name: 'About', name: 'Handle',
// route level code-splitting component: () => import('../views/Handle.vue'),
// this generates a separate chunk (about.[hash].js) for this route meta: {
// which is lazy-loaded when the route is visited. title: '处理'
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') }
} }
] ]
......
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
box-sizing: border-box;
margin: 0;
padding: 0;
border: 0;
font-size: .28rem;
vertical-align: baseline;
font-family: Source Han Sans CN;
}
ul {
list-style: none;
margin: 0px;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
a {
color: inherit;
text-decoration: none;
display: block;
border: none;
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
-webkit-user-select: none;
-moz-user-focus: none;
-moz-user-select: none;
}
a,
a:hover,
a:active,
a:visited,
a:link,
a:focus {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-tap-highlight-color: transparent;
outline: none;
background: none;
text-decoration: none;
}
::selection {
background: #FFF;
color: #333;
}
::-moz-selection {
background: #FFF;
color: #333;
}
::-webkit-selection {
background: #FFF;
color: #333;
}
input,
textarea {
background-color: #fff;
border: none;
padding: 0;
outline: none;
}
textarea {
resize: none;
}
\ No newline at end of file
...@@ -5,8 +5,16 @@ Vue.use(Vuex) ...@@ -5,8 +5,16 @@ Vue.use(Vuex)
export default new Vuex.Store({ export default new Vuex.Store({
state: { state: {
userInfo:{},
unReadCount: 0
}, },
mutations: { mutations: {
SET_USERINFO(state,userInfo){
state.userInfo = userInfo
},
SET_READCOUNT(state, value) {
state.unReadCount = value
}
}, },
actions: { actions: {
}, },
......
import Cookies from "js-cookie";
const TokenKey = "Admin-Token";
export function getToken() {
// return sessionStorage.getItem(TokenKey);
return Cookies.get(TokenKey)
}
export function setToken(token) {
// return sessionStorage.setItem(TokenKey, token);
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
(function (doc, win) {
var docEl = doc.documentElement,
resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
recalc = function () {
var clientWidth = docEl.clientWidth;
if (!clientWidth) return;
if (clientWidth >= 750) {
docEl.style.fontSize = '100px';
} else {
docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
}
var html = document.getElementsByTagName('html')[0]
var settingFs = parseInt(100 * (clientWidth / 750))
var settedFs = settingFs
var whileCount = 0
while (true) {
var realFs = parseInt(window.getComputedStyle(html).fontSize)
var delta = realFs - settedFs
// 不相等
if (Math.abs(delta) >= 1) {
if (delta > 0) {
settingFs--
} else {
settingFs++
}
html.setAttribute('style', 'font-size:' + settingFs + 'px!important')
} else {
break
}
if (whileCount++ > 100) {
break
}
}
};
if (!doc.addEventListener) return;
win.addEventListener(resizeEvt, recalc, false);
doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);
import axios from 'axios'
import { getToken, removeToken } from "@/utils/auth";
import router from '@/router'
import { Toast } from 'vant'
const baseURL = process.env.NODE_ENV === "production" ? "/hmit-api" : "/proxyApi"
const request = axios.create({
baseURL,
timeout: 10000,
withCredentials: true
});
request.interceptors.request.use(
config => {
config.headers["Authorization"] = "Bearer " + getToken();
return config
},
error => {
return Promise.reject(error)
}
)
request.interceptors.response.use((response) => {
const res = response.data;
if (res.code === 200) {
return res
} else if (res.code === 401) {
Toast.fail(res.msg)
router.replace('/login')
} else {
Toast.fail(res.msg)
return res
}
}, (err) => {
Toast('网络请求异常,请稍后重试!');
return err
});
export default request;
\ No newline at end of file
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<template>
<div class="event-detail">
<div class="detail-container">
<div class="detail-item">
<div class="label">事件编号</div>
<div class="val">{{ info.eventNo }}</div>
</div>
<div class="detail-item">
<div class="label">事件类型</div>
<div class="val">{{ info.category }}</div>
</div>
<div class="detail-item">
<div class="label">事发地点</div>
<div class="val">{{ info.address }}</div>
</div>
<div class="detail-item">
<div class="label">上报网络</div>
<div class="val">{{ info.gridName }}</div>
</div>
<div class="detail-item">
<div class="label">事件描述</div>
<div class="val" style="line-height: .42rem;">{{ info.description }}</div>
</div>
<div class="detail-item">
<div class="label">涉及人数</div>
<div class="val">{{ info.involvedNumber }}</div>
</div>
<div class="detail-item">
<div class="label">上报人</div>
<div class="val">{{ info.reportName }}</div>
</div>
<div class="detail-item">
<div class="label">上报人电话</div>
<div class="val">{{ info.reflectorMobile }}</div>
</div>
<div class="detail-item">
<div class="label">上报时间</div>
<div class="val">{{ info.reportDate }}</div>
</div>
<div class="detail-item">
<div class="label">事件等级</div>
<div class="val">{{ info.rankNo }}</div>
</div>
<div class="detail-item">
<div class="label">处理天数</div>
<div class="val">{{ info.expectedDays }}</div>
</div>
<div class="detail-item">
<div class="label">处理状态</div>
<div class="val">{{ info.status }}</div>
</div>
<div class="detail-item">
<div class="label">处理时间</div>
<div class="val">{{ info.registerDate }}</div>
</div>
<div class="detail-item" v-if="hasProcess">
<div class="label">处理内容</div>
<div class="val">{{ info.processContent }}</div>
</div>
<div class="detail-item" v-if="hasProcess">
<div class="label">处理前</div>
<div class="val">
<div class="img-box" >
<img :src="item" @click="preview(beforeHandleImgs, index)" v-for="(item, index) in beforeHandleImgs" />
</div>
</div>
</div>
<div class="detail-item" v-if="hasProcess">
<div class="label">处理后</div>
<div class="val">
<div class="img-box" >
<img :src="item" @click="preview(afterHandleImgs, index)" v-for="(item, index) in afterHandleImgs" />
</div>
</div>
</div>
<div class="detail-item">
<div class="label">附件</div>
<div class="val">
<div class="file-list">
<div class="file-item" v-for="item in 1">
<div class="file-name">附件名称.doc</div>
<div class="check-btn">查看附件</div>
</div>
</div>
</div>
</div>
</div>
<div class="handle-btn-box" v-if="Object.keys(info).length > 0 && !hasProcess">
<van-button type="info" style="height: .86rem; background-color: #3978F8;color: #fff;" round size="large" @click="toHandle">去处理</van-button>
</div>
</div>
</template>
<script>
import { ImagePreview } from 'vant';
import { getEventDetail, getEventInfo } from '../api/index'
export default {
data() {
return {
id: '',
info: {},
afterHandleImgs: [],
beforeHandleImgs: []
};
},
computed: {
hasProcess() {
return this.info.status === '已处理'
}
},
methods: {
getInfo() {
getEventDetail(this.id).then(res => {
this.info = res.data;
if (res.data.status === '已处理') {
this.getPrecessInfo()
}
})
},
getPrecessInfo() {
getEventInfo(this.id).then(res => {
this.info.processContent = res.data.processContent;
if (res.data.beforeProcessImgList) {
this.beforeHandleImgs = res.data.beforeProcessImgList.split(',').map(i => {
return 'http://192.168.20.11:10008/file/' + i
});
}
if (res.data.afterProcessImgList) {
this.afterHandleImgs = res.data.afterProcessImgList.split(',').map(i => {
return 'http://192.168.20.11:10008/file/' + i
});
}
if (res.data.fileUrlList) {
this.info.fileList = res.data.fileUrlList.split(',');
}
})
},
toHandle() {
this.$router.push('/handle?id=' + this.id);
},
preview(images, startPosition) {
ImagePreview({
images, startPosition
});
},
},
mounted() {
this.id = this.$route.query.id;
this.getInfo();
}
};
</script>
<style lang="scss" scoped>
.event-detail {
.detail-container{
border-top: 15px solid #F7F7F7;
border-bottom: 15px solid #f7f7f7;
padding: .3rem .35rem 1.28rem;
.detail-item {
.label{
color: #999;
font-size: .25rem;
}
.val{
color: #111;
margin-top: .2rem;
font-size: .31rem;
.img-box{
display: flex;
flex-wrap: wrap;
img{
width: 1.6rem;
height: 1.06rem;
margin-right: .14rem;
margin-bottom: .14rem;
}
}
.file-list{
.file-item{
margin-bottom: .2rem;
display: flex;
align-items: center;
.file-name{
color: #111;
font-size: .31rem;
}
.check-btn{
margin-left: .5rem;
border-radius: 0.21rem;
border: 1px solid #3978F8;
color: #3978F8;
font-size: .25rem;
padding: .1rem .2rem;
}
}
}
}
&:not(:last-child) {
margin-bottom: .35rem;
}
}
}
.handle-btn-box{
width: 100%;
position: fixed;
left: 0;
bottom: 0;
height: 1.28rem;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
padding: 0 .62rem;
}
}
</style>
\ No newline at end of file
<template>
<div class="event-list-container">
<div class="banner">
<div class="user-box">
<img src="@/assets/imgs/def_photo.png" alt="头像" />
<div class="name">{{ $store.state.userInfo.userName }}</div>
<div class="dept">{{ $store.state.userInfo.nickName }}</div>
<div class="logout" @click="logout">退出登录</div>
</div>
</div>
<div class="event-list-box">
<div class="tab-box">
<div class="tab-item" @click="changeTab(0)" :class="{'active': tabIndex === 0}">待处理</div>
<div class="tab-item" @click="changeTab(1)" :class="{'active': tabIndex === 1}">已处理</div>
</div>
<div class="search-container" @click="toSearch">
<div class="search-box">
<img src="@/assets/imgs/search_icon.png" alt="搜索" />
<span>搜索事件</span>
</div>
</div>
<div class="event-list">
<van-list
v-model="loading"
:finished="finished"
finished-text="没有更多了"
@load="getList"
>
<div
class="item"
:key="item.id"
v-for="(item, index) in list"
@click="toDetail(item)"
>
<div class="item-name">{{ item.eventName }}</div>
<div class="_bottom">
<div class="item-info">
<div class="item-type">
<div class="label">事件类型:</div>
<div class="val">{{ item.category }}</div>
</div>
<div class="item-deadline" v-if="tabIndex === 0">
<div class="label">处理期限:</div>
<div class="val" :class="getClass(item.expectedDays)">{{ item.expectedDays }}</div>
</div>
</div>
<div class="handle-btn">{{ tabIndex === 0 ? '去处理' : '查看' }}</div>
</div>
</div>
</van-list>
</div>
</div>
</div>
</template>
<script>
import { Toast } from 'vant'
import { getEventList } from '../api/index'
import { removeToken } from '../utils/auth'
export default {
data() {
return {
tabIndex: 0,
loading: false,
finished: false,
list: [],
pageNum: 1,
pageSize: 10,
total: 0,
}
},
methods: {
getClass(day) {
if (day > 2) {
return 'green'
} else if (day === 2) {
return 'yellow'
} else {
return 'red'
}
},
logout() {
removeToken()
Toast.success('退出成功')
this.$router.replace('/login')
},
toSearch() {
this.$router.push({
name: 'Search',
query: {
type: this.tabIndex
}
})
},
changeTab(index) {
if (this.tabIndex === index) return;
this.tabIndex = index
this.pageNum = 1
this.finished = false
this.list = []
this.getList()
},
getList() {
this.loading = true;
let params = {
processed: this.tabIndex === 1 ? true : false,
pageNum: this.pageNum,
pageSize: this.pageSize,
};
getEventList(params).then(res => {
if (res.code === 200) {
this.loading = false;
this.total = res.total;
if (this.total === 0) {
this.finished = true;
return;
}
if (this.pageNum === 1) {
this.list = res.rows;
} else {
this.list.push(...res.rows);
}
if (this.pageNum * this.pageSize >= this.total) {
this.finished = true;
} else {
this.pageNum++;
}
}
})
},
toDetail(item) {
this.$router.push({
name: 'EventDetail',
query: {
id: item.id
}
})
}
},
mounted() {
this.getList()
}
}
</script>
<style lang="scss">
.event-list-container{
.banner{
background: url('../assets/imgs/index_top_bg.png') no-repeat;
background-size: 100% 100%;
width: 100%;
height: 3.4rem;
padding: 1.1rem .28rem 1.28rem;
.user-box{
height: .74rem;
display: flex;
align-items: center;
img{
width: .74rem;
height: .74rem;
border-radius: 50%;
}
.name{
color: #fff;
font-size: .32rem;
font-weight: bold;
margin: 0 .3rem;
}
.dept{
color: #fff;
font-size: .26rem;
border-left: 1px solid #fff;
padding-left: .2rem;
}
.logout{
color: #fff;
font-size: .26rem;
margin-left: auto;
font-weight: bold;
}
}
}
.event-list-box{
height: calc(100vh - 2.4rem);
width: 100%;
background: #FFFFFF;
border-radius: 0.42rem 0.42rem 0rem 0rem;
margin-top: -1rem;
.tab-box{
height: 1rem;
width: 100%;
display: flex;
align-items: center;
justify-content: space-around;
border-bottom: 1px solid #DCDCDC;
.tab-item{
font-size: .28rem;
color: #666666;
}
.active{
color: #111;
font-weight: bold;
position: relative;
&::after{
content: '';
position: absolute;
background-color: #111;
height: 0.06rem;
width: .35rem;
border-radius: .1rem;
bottom: -.15rem;
left: 50%;
margin-left: -.18rem;
}
}
}
.search-container{
padding: .2rem .35rem 0;
.search-box{
width: 100%;
background-color: #F1F1F1;
border-radius: 0.33rem;
height: .68rem;
display: flex;
align-items: center;
padding-left: .28rem;
img{
width: .25rem;
height: .25rem;
margin-right: .13rem;
}
span{
color: #A6A9B0;
font-size: .29rem;
}
}
}
.event-list{
padding: 0 .35rem 1.28rem;
.item{
margin-top: .42rem;
.item-name{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
color: #111;
font-size: .31rem;
}
._bottom{
margin-top: .34rem;
display: flex;
justify-content: space-between;
align-items: center;
.item-info{
font-size: .25rem;
.item-type{
display: flex;
margin-bottom: .28rem;
}
.item-deadline{
display: flex;
.val{
position: relative;
padding-left: .32rem;
&::after{
position: absolute;
width: .13rem;
height: .13rem;
content: '';
left: .05rem;
top: .05rem;
border-radius: 50%;
}
}
.red{
&::after{
background-color: #E9691F;
}
}
.yellow{
&::after{
background-color: #FEBC2F;
}
}
.green{
&::after{
background-color: #1CC291;
}
}
}
.label{
color: #999;
}
.val{
color: #111;
}
}
.handle-btn{
width: 1.3rem;
height: .54rem;
border-radius: .26rem;
border: 1px solid #3978F8;
color: #3978F8;
text-align: center;
line-height: .54rem;
}
}
}
}
}
}
</style>
\ No newline at end of file
<template>
<div class="handle-container">
<div class="handle-form">
<div class="handle-item content">
<div class="label">处理内容</div>
<div class="val">
<textarea v-model="content" placeholder="请填写内容"></textarea>
</div>
</div>
<div class="handle-item">
<div class="label">上传处理前图片</div>
<div class="val">
<van-uploader v-model="beforeHandleImgs" :after-read="beforeHandleUpload" multiple />
</div>
</div>
<div class="handle-item">
<div class="label">上传处理后图片</div>
<div class="val">
<van-uploader v-model="afterHandleImgs" :after-read="afterHandleUpload" multiple />
</div>
</div>
<div class="handle-item">
<div class="label">附件上传</div>
<div class="val">
<van-uploader v-model="fileList" multiple />
</div>
</div>
<div class="btn-box">
<van-button type="info" style="height: .86rem; background-color: #3978F8;" round size="large" @click="submit">提交</van-button>
</div>
</div>
</div>
</template>
<script>
import { Toast } from 'vant'
import { handleEvent, uploadImg } from '../api/index'
export default {
data() {
return {
content: '',
beforeHandleImgs: [],
afterHandleImgs: [],
fileList: []
}
},
methods: {
submit() {
let data = {
beforeProcessImgList: this.beforeHandleImgs.map(item => item.url).join(','),
afterProcessImgList: this.afterHandleImgs.map(item => item.url).join(','),
eventId: this.$route.query.id,
processContent: this.content,
fileUrlList: this.fileList.map(item => item.url).join(',')
}
handleEvent(data).then(res => {
if (res.code === 200) {
Toast.success('提交成功')
this.$router.go(-1)
}
})
},
beforeHandleUpload(files) {
let fileArr = []
if (!Array.isArray(files)) {
fileArr.push(files.file)
} else {
fileArr = files
}
let startIndex = this.beforeHandleImgs.length - fileArr.length
for(let i = startIndex; i < this.beforeHandleImgs.length; i ++) {
this.beforeHandleImgs[i].status = 'uploading'
let formData = new FormData();
formData.append("file", this.beforeHandleImgs[i].file);
uploadImg(formData).then(res => {
if (res.code === 200) {
this.beforeHandleImgs[i].status = 'done'
this.beforeHandleImgs[i].url = res.data.url
} else {
this.beforeHandleImgs[i].status = 'failed'
}
})
}
},
afterHandleUpload(files) {
let fileArr = []
if (!Array.isArray(files)) {
fileArr.push(files.file)
} else {
fileArr = files
}
let startIndex = this.afterHandleImgs.length - fileArr.length
for(let i = startIndex; i < this.afterHandleImgs.length; i ++) {
this.afterHandleImgs[i].status = 'uploading'
let formData = new FormData();
formData.append("file", this.afterHandleImgs[i].file);
uploadImg(formData).then(res => {
if (res.code === 200) {
this.afterHandleImgs[i].status = 'done'
this.afterHandleImgs[i].url = res.data.url
} else {
this.afterHandleImgs[i].status = 'failed'
}
})
}
}
}
}
</script>
<style lang="scss" scoped>
.handle-container{
.handle-form{
border-top: 14px solid #F7F7F7;
.handle-item{
padding: .24rem .46rem .2rem;
.label{
font-size: .31rem;
}
.val{
margin-top: .2rem;
textarea{
width: 100%;
height: 1.8rem;
font-size: .3rem;
}
}
}
.content{
border-bottom: 14px solid #F7F7F7;
.label{
position: relative;
&::before{
content: '*';
color: #F74C31;
position: absolute;
left: -.2rem;
top: .05rem;
font-size: .3rem;
}
}
}
}
.btn-box{
padding: .6rem .62rem 0;
}
}
</style>
\ No newline at end of file
<template> <template>
<div class="home"> <div class="home">
<img alt="Vue logo" src="../assets/logo.png"> <keep-alive>
<HelloWorld msg="Welcome to Your Vue.js App"/> <router-view></router-view>
</keep-alive>
<div class="tab-container">
<div class="tab-item" @click="changeTab(item, index)" :class="{'active': index === tabIndex}" :key="index" v-for="(item, index) in tabs">
<img class="icon" :src="require(`../assets/imgs/bottom_${item.iconName}_${index === tabIndex ? 'hover' : 'def'}_icon.png`)" />
<div class="name">{{ item.name }}</div>
<div class="message-count" v-if="index === 1 && messageCount > 0">{{ messageCount }}</div>
</div>
</div>
</div> </div>
</template> </template>
<script> <script>
// @ is an alias to /src import { getUserInfo, getMessageCount } from '../api/index'
import HelloWorld from '@/components/HelloWorld.vue'
export default { export default {
name: 'Home', name: 'Home',
components: { computed: {
HelloWorld messageCount() {
return this.$store.state.unReadCount
},
tabIndex() {
if (this.$route.name === 'EventList') {
return 0
} else if (this.$route.name === 'Message') {
return 1
} else if (this.$route.name === 'Statistics') {
return 2
}
}
},
data() {
return {
tabs: [
{
name: '事件',
iconName: 'event',
routerName: 'EventList'
},
{
name: '消息',
iconName: 'notice',
routerName: 'Message'
},
{
name: '统计',
iconName: 'count',
routerName: 'Statistics'
}
]
}
},
methods: {
changeTab(item, index) {
if (this.tabIndex === index) return;
this.$router.push({
name: item.routerName
})
},
getInfo() {
getUserInfo().then(res => {
if (res.code === 200) {
this.$store.commit('SET_USERINFO', res.user)
}
})
getMessageCount().then(res => {
if (res.code === 200) {
this.$store.commit('SET_READCOUNT', res.data)
}
})
},
},
mounted() {
this.getInfo()
} }
} }
</script> </script>
<style lang="scss" scoped>
.home{
min-height: 100vh;
.tab-container{
height: 1.28rem;
width: 100%;
position: fixed;
z-index: 1000;
bottom: 0;
background: #F3F4F8;
display: flex;
justify-content: space-around;
.tab-item{
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
font-size: 0.21rem;
align-items: center;
color: #696C76;
img {
width: .5rem;
height: .5rem;
margin-bottom: .15rem;
}
.message-count{
border-radius: 50%;
position: absolute;
width: .4rem;
height: .4rem;
background-color: #FF5000;
color: #fff;
font-size: .25rem;
font-weight: bold;
top: -.05rem;
right: -.12rem;
border: 1px solid #fff;
text-align: center;
line-height: .4rem;
}
}
.active{
color: #3978F8;
}
}
}
</style>
\ No newline at end of file
<template>
<div class="login">
<div class="banner">
<p>Hello!</p>
<p style="margin-top: 0.3rem">欢迎登录XXXX物业管理端</p>
</div>
<div class="form-container">
<div class="form-item">
<input v-model="form.username" type="text" placeholder="请输入账号" />
</div>
<div class="form-item">
<input type="password" v-model="form.password" placeholder="请输入密码" />
</div>
<div class="code-box">
<div class="form-item code">
<input v-model="form.code" placeholder="请输入验证码" />
</div>
<img :src="codeUrl" @click="getCode" class="login-code-img" />
</div>
<van-button type="info" @click="submit" round size="large">立即登录</van-button>
</div>
</div>
</template>
<script>
import { login, getCodeImg, getUserInfo } from "../api";
import { setToken } from '../utils/auth'
export default {
data() {
return {
codeUrl: '',
form: {
username: 'tyjy',
password: 'xcsq1234,',
code: '',
uuid: ''
},
};
},
methods: {
getCode() {
getCodeImg().then((res) => {
this.captchaEnabled =
res.captchaEnabled === undefined ? true : res.captchaEnabled;
if (this.captchaEnabled) {
this.codeUrl = "data:image/gif;base64," + res.img;
this.form.uuid = res.uuid;
}
});
},
submit() {
login(this.form).then((res) => {
if( res.code === 200) {
setToken(res.data.access_token)
this.$router.replace('/home')
}
});
},
},
mounted() {
this.getCode();
}
};
</script>
<style lang="scss">
.login {
.banner {
height: 3.3rem;
width: 100%;
background: url("../assets/imgs/login_to.png") no-repeat;
background-size: 100% 100%;
padding-top: 1.32rem;
padding-left: 0.8rem;
p {
color: #fff;
font-size: 0.5rem;
}
}
.form-container {
min-height: 6rem;
background-color: #fff;
border-radius: 0.42rem 0.42rem 0rem 0rem;
margin-top: -0.4rem;
padding: 0.8rem 0.6rem;
.form-item {
height: 0.98rem;
background-color: #f1f2f6;
border-radius: 0.5rem;
width: 100%;
padding: 0 0.4rem;
display: flex;
align-items: center;
margin-bottom: 0.4rem;
input {
width: 100%;
font-size: 0.33rem;
background-color: #f1f2f6;
}
}
.code-box{
display: flex;
img{
margin-left: .2rem;
height: 1rem;
}
}
}
}
</style>
\ No newline at end of file
<template>
<div class="message-container">
<div class="message-list">
<van-list
v-model="loading"
:finished="finished"
finished-text="没有更多了"
@load="getList"
>
<div class="message-item" v-for="item in list" @click="toDetail(item)">
<div class="_top">
<div class="item-title">{{ item.title }}</div>
<div class="item-time">{{ item.publishTime }}</div>
</div>
<div class="item-desc" :class="{'notRead': item.status === 0}">{{ item.content }}啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊</div>
</div>
</van-list>
</div>
</div>
</template>
<script>
import { getMessageList, getMessageDetail, getMessageCount } from '../api'
export default {
data() {
return {
loading: false,
finished: false,
page: 1,
limit: 10,
total: 0,
list: []
}
},
methods: {
getList() {
this.loading = true
let params = {
page: this.page,
limit: this.limit
}
getMessageList(params).then(res => {
if (res.code === 200) {
this.loading = false
this.list = this.list.concat(res.rows)
this.total = res.total
this.page++
if (this.list.length >= this.total) {
this.finished = true
}
} else {
this.finished = true
}
})
},
toDetail(item) {
getMessageDetail(item.id).then(res => {
item.status = 1
getMessageCount().then(res => {
if (res.code === 200) {
this.$store.commit('SET_READCOUNT', res.data)
}
})
})
this.$router.push({
name: 'EventDetail',
query: {
id: item.eventId
}
})
}
},
mounted() {
this.getList()
}
}
</script>
<style lang="scss" scoped>
.message-container{
.message-list{
padding: 0 .26rem;
.message-item{
border-bottom: 1px solid #E7E9F0;
padding: .26rem 0 .35rem;
._top{
display: flex;
justify-content: space-between;
.item-title{
font-weight: bold;
font-size: .31rem;
}
.item-time{
color: #999;
font-size: .23rem;
}
}
.item-desc{
padding-right: .3rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-top: .24rem;
color: #999;
font-size: .28rem;
}
.notRead{
position: relative;
&::after{
position: absolute;
width: .15rem;
height: .15rem;
content: '';
background-color: #FE5219;
border-radius: 50%;
right: 0;
top: .06rem;
}
}
}
}
}
</style>
\ No newline at end of file
<template>
<div class="search-container">
<van-search
v-model="searchVal"
show-action
placeholder="搜索事件"
shape="round"
@search="onSearch"
@cancel="onCancel"
/>
<div class="history-container">
<div class="history-title">
搜索历史
<van-icon name="delete-o" @click="deleteHistory" />
</div>
<div class="history-list">
<div class="history-item" v-for="item in searchHistory" :key="item" @click="historyClick(item)">
{{ item }}
</div>
</div>
</div>
<div class="result-list">
<van-list
v-model="loading"
:finished="finished"
finished-text="没有更多了"
@load="getList"
>
<div
class="item"
:key="item.id"
v-for="(item, index) in list"
@click="toDetail(item)"
>
<div class="item-name">{{ item.eventName }}</div>
<div class="_bottom">
<div class="item-info">
<div class="item-type">
<div class="label">事件类型:</div>
<div class="val">{{ item.category }}</div>
</div>
<div class="item-deadline" v-if="type === '0'">
<div class="label">处理期限:</div>
<div class="val" :class="getClass(item.expectedDays)">{{ item.expectedDays }}</div>
</div>
</div>
<div class="handle-btn">{{ type === '0' ? '去处理' : '查看' }}</div>
</div>
</div>
</van-list>
</div>
</div>
</template>
<script>
import { getEventList } from '../api'
export default {
data() {
return {
type: '',
loading: false,
finished: false,
list: [],
page: 1,
limit: 10,
total: 0,
searchVal: '',
searchHistory: []
}
},
methods: {
getClass(day) {
if (day > 2) {
return 'green'
} else if (day === 2) {
return 'yellow'
} else {
return 'red'
}
},
toDetail(item) {
this.$router.push({
name: 'EventDetail',
query: {
id: item.id
}
})
},
historyClick(item) {
this.searchVal = item
this.listReset()
this.getList()
},
listReset() {
this.page = 1
this.limit = 1
this.total = 0
this.loading = false
this.finished = false
},
getList() {
if (this.searchVal.trim() === '') {
this.finished = true
return
}
this.finished = false
this.loading = true
let params = {
pageNum: this.page,
pageSize: this.limit,
processed: this.type === '0' ? true : false,
keyword: this.searchVal
}
getEventList(params).then(res => {
if (res.code === 200) {
this.loading = false
this.total = res.total
this.list = this.list.concat(res.rows)
this.page ++
if (this.list.length >= this.total) {
this.finished = true
}
} else {
this.loading = false
this.finished = true
}
})
},
onSearch(val) {
this.getList()
},
onCancel() {
console.log('cancel')
},
deleteHistory() {
this.searchHistory = []
}
},
mounted() {
this.type = this.$route.query.type
}
}
</script>
<style lang="scss" scoped>
.search-container{
.history-container{
padding: .42rem .28rem;
.history-title{
display: flex;
font-size: .31rem;
justify-content: space-between;
}
.history-list{
margin-top: .28rem;
display: flex;
flex-wrap: wrap;
.history-item{
margin-right: .28rem;
background-color: #f7f7f7;
color: #666666;
border-radius: .3rem;
padding: .2rem .35rem;
margin-bottom: .18rem;
}
}
}
.result-list{
padding: 0 .35rem 1.28rem;
.item{
margin-top: .42rem;
.item-name{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
color: #111;
font-size: .31rem;
}
._bottom{
margin-top: .34rem;
display: flex;
justify-content: space-between;
align-items: center;
.item-info{
font-size: .25rem;
.item-type{
display: flex;
margin-bottom: .28rem;
}
.item-deadline{
display: flex;
.val{
position: relative;
padding-left: .32rem;
&::after{
position: absolute;
width: .13rem;
height: .13rem;
content: '';
left: .05rem;
top: .05rem;
border-radius: 50%;
}
}
.red{
&::after{
background-color: #E9691F;
}
}
.yellow{
&::after{
background-color: #FEBC2F;
}
}
.green{
&::after{
background-color: #1CC291;
}
}
}
.label{
color: #999;
}
.val{
color: #111;
}
}
.handle-btn{
width: 1.3rem;
height: .54rem;
border-radius: .26rem;
border: 1px solid #3978F8;
color: #3978F8;
text-align: center;
line-height: .54rem;
}
}
}
}
}
</style>
\ No newline at end of file
<template>
<div class="statistics-container">
<div class="chart-box type">
<div class="label">事件类型统计</div>
<div id="type-chart"></div>
</div>
<div class="chart-box chart">
<div class="label">事件处理量统计</div>
<div id="count-chart"></div>
</div>
</div>
</template>
<script>
import * as echarts from "echarts";
import { getCountChartData, getTypeChartData } from "../api";
export default {
data() {
return {
typeChart: null,
countChart: null,
};
},
methods: {
getData() {
getTypeChartData().then((res) => {
if (res.code === 200) {
let data = res.data.map(i => {
return {
name: i.category,
value: i.total
}
})
this.typeChartInit(data)
}
});
getCountChartData().then((res) => {
if (res.code === 200) {
let data = {
time: [],
value: []
}
res.data.forEach(i => {
data.time.push(i.month)
data.value.push(i.count)
})
this.countChartInit(data)
}
});
},
typeChartInit(data) {
this.typeChart = echarts.init(document.getElementById("type-chart"));
this.typeChart.setOption({
title: {
show: false,
},
tooltip: {
trigger: "item",
},
legend: {
show: false,
},
series: [
{
name: "事件类型",
type: "pie",
radius: ["40%", "70%"],
data,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)",
},
},
},
],
});
},
countChartInit(data) {
this.countChart = echarts.init(document.getElementById("count-chart"));
this.countChart.setOption({
xAxis: {
type: "category",
data: data.time,
axisTick: {
show: false,
alignWithLabel: true,
},
},
grid: {
top: "15%",
left: "5%",
right: "5%",
bottom: "10%",
},
yAxis: {
show: false,
},
series: [
{
data: data.value,
type: "bar",
barWidth: "40%",
showBackground: true,
itemStyle: {
color: "#05BE96",
},
backgroundStyle: {
color: "rgba(180, 180, 180, 0.2)",
},
label: {
show: true,
position: "top",
textStyle: {
color: "#9A9A9A",
},
},
},
],
});
},
},
mounted() {
this.getData()
},
};
</script>
<style lang="scss" scoped>
.statistics-container {
.chart-box {
border-top: 15px solid #f7f7f7;
.label {
font-size: 0.31rem;
padding-left: 0.3rem;
padding-top: 0.2rem;
font-size: 0.3rem;
color: #111;
}
#type-chart {
width: 100%;
height: 4.5rem;
}
#count-chart {
width: 100%;
height: 4.5rem;
}
}
}
</style>
\ No newline at end of file
module.exports = {
lintOnSave: false,
publicPath: process.env.NODE_ENV === "production" ? "./" : "/",
chainWebpack: (config) => {
const svgRule = config.module.rule("svg");
svgRule.uses.clear();
svgRule
.test(/\.svg$/)
.use("svg-sprite-loader")
.loader("svg-sprite-loader");
},
configureWebpack: {
resolve: { extensions: [".ts", ".tsx", ".js", ".json"] },
module: {
rules: [
{
test: /\.ts?$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
appendTsSuffixTo: [/\.vue$/],
}
}
]
}
},
productionSourceMap: false,
devServer: {
proxy: {
"/proxyApi": {
// target: 'http://192.168.10.12:18001/hmit-api',
target: 'http://192.168.10.4:10024/',
changeOrigin: true,
pathRewrite: {
"^/proxyApi": "/",
},
}
},
port: 8080
},
};
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment