0 前言
前两天去参加公司组织的 60 公里徒步,回来之后就发烧了,差点以为自己得了新冠,然后跑到医院去做了核酸检测,睡了 1 天半。还好,结果为阴性。最后托了发烧的福,足够的睡眠和较高的提问倒是把 60 公里徒步的肌肉酸痛给缓解了,哈哈哈。即便如此,但这个项目还是要继续的,关乎年后的项目管理。
1 确认需求
1.1 客户端
- 查看进度。公司内部当天最多 200 人访问。主要是查看 Yogurt 的需求受理情况,开发进度,完成反馈等相关信息。
- 表单提交。主要是向 Yogurt 提交各种形式的需求单
1.2 管理端
- 表单模板。因为客户端需要提交各种形式的需求单,但不能每次都临时写一个表单页面来提交,因此需要做一个动态的,可以随时修改的表单模板。
- 表单管理。客户端查看的受理情况、开发进度等等都是需要 Yogurt 来审核受理的。
- 权限管理。这是一个多对一的工具,因此不需要太复杂的权限,也没有太多层级的流程控制,这里只需要区分一下管理员权限和访问者权限就好了。
2 绘制原型图
原型图工具有很多,收费的免费的都有,看个人喜好,手绘也是可以的,哈哈哈哈(手动狗头)。Yogurt 这里用的是 即时设计
(传送门:https://js.design/),xiaopiu
的另一款产品。本来是想用 xiaopiu
的,但由于 Yogurt 的 xiaopiu
账号给公司在用,为了避免不必要的麻烦,所以换一个平台来做原型图的设计。
需要注意的是,即时设计
的内置默认字体是 思源黑体
,同时服务器并没有发送字体包过来,所以需要在本机提前安装。传送门:http://big.xzfile.com/bigfile/100/siyuanheiti_downcc.com.zip。
注册登录后进来就是这个样子的。点击 创建新文件
由于是桌面的 Web 项目,所以直接选择下面的 Desktop 即可。Yogurt 公司内部的办公电脑基本都是 1920x1080 的屏幕,所以稍微考虑一下布局,1440的宽也是OK的。
进来之后,默认文件名是 无标题
(左上角)。我们点击进去修改一下就好了。
剩下的事情就看自己的发挥了。这里为了后期编写前端代码更直观,而且这个项目很小,所以准备采用高保真的标准来进行设计。前面我们是用的 Element UI 的组件,因此,在原型图设计过程中也要尽可能的贴近 Element UI 的组件风格。
2000 Years Later…
后面突然想想,以前画原型图是跟需求方说(chui)明(shui)的。。。现在自己做这个项目要什么原型图!干就完事了!
3 Web 前端搭建
3.1 页面目录搭建
pages
目录是默认没有的,因此需要自己创建一下,如果嫌麻烦的,也可以直接在 components
目录下创建,毕竟 Demo 就是这样做的。
.vue
文件是支持 html
、css
、 js
三种语言共存的。但以 Yogurt 之前的开发情况来看,当 .vue
文件的代码越来越多的时候,排错的时候就十分困难,所以就参考微信小程序的目录形式来创建 vue 的文件目录了。
这里的命名风格是为了保证页面文件始终保持一定的顺序,后期方便查找文件。
3.2 创建路由
找到 web-project\src\router\index.js
,打开
引用我们刚刚创建好的 vue 文件
/* 页面访问路由 - 引用页面 */
import Login from '@/pages/01_Login/Login.vue' // 登录页
import Index from '@/pages/02_Index/Index.vue' // 首页
原先的 HelloWorld
咱们先不动,学着它的形式先创建我们自己的访问路由
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/index',
name: 'Index',
component: Index
}
]
})
创建好了保存一下。然后咱们通过浏览器再访问一下
http://localhost:8080/#/login
http://localhost:8080/#/index
会发现这个 vue 的 logo 一直都在,咱们先给它请出去。在这个项目里,web-project\index.html
是入口文件,然后首先访问的资源是 web-project\src\App.vue
。那么我们到这里面去看看就知道了。
这里有一行:
<img src="./assets/logo.png">
把这一行删掉就没有了。
4 页面设计
4.1 登录页
登录页尽可能越简单越好。登录和注册的功能都放一起,这样可以节约一下前端的开发时间,同时对于使用者来说也比较方便。为后期方便通过邮箱发送需求执行情况等信息,因此在这个项目里,使用邮箱作为登录账号是比较合理的。
秉承越简单越好的原则,登录和注册的两个功能直接在一个页面搞定,到时做一下判断即可。
<template>
<div style = "width: 100%;">
<div style = "width: 300px; height: 300px; position: fixed; top: 50%; margin: calc(-300px / 2) calc(-300px / 2); left: 50%;">
<div style = "line-height: 50px; font-size: 18px; font-weight: 900;">Yogurt_cry 的需求管理工具</div>
<el-input v-model = "loginInfo.email">
<template slot = "prepend">
<div style = "width: 40px;">邮箱</div>
</template>
</el-input>
<el-input style = "margin-top: 5px;"
v-model = "loginInfo.password">
<template slot = "prepend">
<div style = "width: 40px;">密码</div>
</template>
</el-input>
<div style = "margin-top: 5px;">
<el-input style = "width: calc(100% - 108px - 10px);"
v-model = "loginInfo.verifyCode">
<template slot = "prepend">
<div style = "width: 40px;">验证码</div>
</template>
</el-input>
<el-button style = "width: 110px;">
<span v-if = "loginInfo.verifyCodeSendState === 0">获取验证码</span>
<span v-else-if = "loginInfo.verifyCodeSendState === 1">重新获取</span>
</el-button>
</div>
<el-button style = "width: 100%; margin-top: 10px;"
type = "primary">
<span v-if = "loginInfo.loginState === 0">注册</span>
<span v-else-if = "loginInfo.loginState === 1">登录</span>
</el-button>
</div>
</div>
</template>
<script>
export default {
data () {
return {
'loginInfo': {
'email': '',
'password': '',
'verifyCode': '',
'verifyCodeSendState': 0,
'loginState': 1
}
}
}
}
</script>
<style>
</style>
4.2 首页
用 el-menu
导航栏将页面划分成上述样式。依旧是为了节约开发时间,这一套前端需要提供给 访问者
和 管理者
同时使用,因此导航栏做成动态加载的,权限后期由后端来进行判断。
<template>
<div style = "width: 100%;">
<!-- 左侧 -->
<div style = "position: fixed; width: 180px; height: 100%;">
<div style = "font-size: 18px; padding: 24px 0; font-weight: 900; background-color: #ecf5ff; color: #409eff; height: 22px; line-height: 22px; border-right: solid 1px #e6e6e6;">
<span>YGCYRMT v1.0</span>
</div>
<el-menu style = "text-align: left; height: 100%;"
:router = "true">
<el-menu-item v-for = "(item, index) in menuList"
:key = "index"
:index = "item.index">
<i :class = "item.iconClass"></i>
<span slot = "title">{
{ item.name }}</span>
</el-menu-item>
</el-menu>
</div>
<!-- 右侧 -->
<div style = "position: fixed; width: calc(100% - 180px); min-width: 1020px; height: 100%; left: 180px;">
<el-menu style = "width: 100%; height: 70px;"
mode = "horizontal">
<el-menu-item style = "height: 70px; line-height: 70px; float: right;">退出</el-menu-item>
<el-menu-item style = "height: 70px; line-height: 70px; float: right;">欢迎您, {
{ email }}</el-menu-item>
<div style = "clear: both;"></div>
</el-menu>
<!-- 正式内容 -->
<div style = "height: calc(100% - 74px);">
<router-view/>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
/* 左侧导航栏 */
'menuList': [
{
'name': '需求看板',
'index': '/index/databoard',
'iconClass': 'el-icon-s-data'
},
{
'name': '需求列表',
'index': '/index/formList',
'iconClass': 'el-icon-s-grid'
},
{
'name': '表单模板',
'index': '/index/formTemplate',
'iconClass': 'el-icon-s-claim'
},
{
'name': '用户管理',
'index': '/index/userMangement',
'iconClass': 'el-icon-user-solid'
}
],
/* 顶部导航栏信息 */
'email': '[email protected]'
}
}
}
</script>
<style>
</style>
4.3 功能页
大家可以看到,除一开始创建的 Login
和 Index
两个页面外,现在还新增了 Databoard
(需求看板), FormList
(需求列表), FormTemplate
(表单模板), userMangement
(用户管理), LimitError
(授权失败) 等页面。这些是子功能页,是提供具体功能操作的。
同样的,我们也需要对他们进行路由管理。
import Vue from 'vue'
import Router from 'vue-router'
/* --- create: 2021-01-11 23:09 Yogurt_cry --- */
import VueResource from 'vue-resource'
import HelloWorld from '@/components/HelloWorld'
/* 页面访问路由 - 引用页面 */
import Login from '@/pages/01_Login/Login.vue' // 登录页
import Index from '@/pages/02_Index/Index.vue' // 首页
import Databoard from '@/pages/0201_Databoard/Databoard.vue' // 需求看板
import FormList from '@/pages/0202_FormList/FormList.vue' // 需求列表
import FormTemplate from '@/pages/0203_FormTemplate/FormTemplate.vue' // 需求模板
import UserMangement from '@/pages/0204_UserMangement/UserMangement.vue' // 系统设置
import LimitError from '@/pages/9901_LimitError/LimitError.vue' // 权限错误
Vue.use(Router)
/* 2021-01-11 23:09 Yogurt_cry 解决相同路径跳转报错的异常 */
const routerPush = Router.prototype.push
Router.prototype.push = function push (location) {
return routerPush.call(this, location).catch(error => error)
}
/* --- create: 2021-01-11 23:09 Yogurt_cry --- */
Vue.use(VueResource)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/index',
name: 'Index',
component: Index,
children: [
{
path: '/index/databoard',
name: 'Databoard',
component: Databoard
},
{
path: '/index/formList',
name: 'FormList',
component: FormList
},
{
path: '/index/formTemplate',
name: 'FormTemplate',
component: FormTemplate
},
{
path: '/index/userMangement',
name: 'UserMangement',
component: UserMangement
},
{
path: '/index/limitError',
name: 'LimitError',
component: LimitError
}
]
}
]
})
4.3.1 需求列表
本来应该是先做 需求看板
的,但是其他功能没完成之前,也不知道需要在看板里看些啥,就先往下做。先把 需求列表
做了再说。交互相关的暂时不着急,先把面上的布局先做完。表单管理是相对便捷和直观的,开发速度也比较快。
<template>
<div style = "padding: 10px; witdh: calc(100% - 20px); height: calc(100% - 20px);">
<div style = "background-color: #ffffff; width: 100%; height: 100%; border-radius: 4px; border: solid 1px #e5e5e5; overflow: hidden;">
<el-table :data = "tableData"
stripe
height = "590"
style = "width: calc(100% - 20px); margin: 10px;">
<el-table-column label = "序号"
prop = "indexNumber"
width = "70"
align = "center"></el-table-column>
<el-table-column label = "需求名称"
prop = "missionName"
min-width = "200"
show-overflow-tooltip
align = "center"></el-table-column>
<el-table-column label = "提交时间"
prop = "createDateTime"
min-width = "140"
show-overflow-tooltip
align = "center"></el-table-column>
<el-table-column label = "交付时间"
prop = "finishDateTime"
min-width = "140"
show-overflow-tooltip
align = "center"></el-table-column>
<el-table-column label = "级别"
prop = "missionLevel"
min-width = "70"
align = "center">
<template slot-scope = "scope">
<el-tag v-if = "scope.row.missionLevel === 0"
type = "info"
disable-transitions>普通</el-tag>
<el-tag v-else-if = "scope.row.missionLevel === 1"
type = "primary"
disable-transitions>一般</el-tag>
<el-tag v-else-if = "scope.row.missionLevel === 2"
type = "warning"
disable-transitions>重要</el-tag>
<el-tag v-else-if = "scope.row.missionLevel === 3"
type = "danger"
disable-transitions>紧急</el-tag>
</template>
</el-table-column>
<el-table-column label = "需求进度"
prop = "missionStep"
min-width = "80"
align = "center">
<template slot-scope = "scope">
<el-tag v-if = "scope.row.missionStep === 0"
type = "primary"
disable-transitions>待受理</el-tag>
<el-tag v-else-if = "scope.row.missionStep === 1"
type = "success"
disable-transitions>受理中</el-tag>
<el-tag v-else-if = "scope.row.missionStep === 2"
type = "info"
disable-transitions>已完成</el-tag>
<el-tag v-else-if = "scope.row.missionStep === 3"
type = "danger"
disable-transitions>已退回</el-tag>
</template>
</el-table-column>
<el-table-column label = "负责人"
prop = "taskLeader"
min-width = "100"
show-overflow-tooltip
align = "center"></el-table-column>
<el-table-column label = "操作"
min-width = "100"
align = "center">
<template slot = "header">
<el-button size = "medium"
type = "primary">提交需求</el-button>
</template>
<template>
<el-button size = "small">
<span v-if = "limitState === 0">详情</span>
<span v-else-if = "limitState === 1">编辑</span>
</el-button>
</template>
</el-table-column>
</el-table>
<div style = "height: 32px; margin-top: 5px;">
<el-pagination style = "float: right;"
background
layout = "total, sizes, prev, pager, next"
:page-sizes = "[100, 200, 300, 400]"
:total = "1000">
</el-pagination>
<div style = "clear: both;"></div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
'limitState': 0,
'tableData': [
{
'indexNumber': 1,
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务测试任务测试任务测试任务测试任务测试任务测试任务',
'createDateTime': '2021-01-19 00:00:00',
'finishDateTime': '2021-01-19 00:00:00',
'missionLevel': 0,
'missionStep': 0,
'taskLeader': 'Yogurt_cry'
},
{
'indexNumber': 2,
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务',
'createDateTime': '2021-01-19 00:00:00',
'finishDateTime': '2021-01-19 00:00:00',
'missionLevel': 1,
'missionStep': 1,
'taskLeader': 'Yogurt_cry'
},
{
'indexNumber': 3,
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务',
'createDateTime': '2021-01-19 00:00:00',
'finishDateTime': '2021-01-19 00:00:00',
'missionLevel': 2,
'missionStep': 2,
'taskLeader': 'Yogurt_cry'
},
{
'indexNumber': 4,
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务',
'createDateTime': '2021-01-19 00:00:00',
'finishDateTime': '2021-01-19 00:00:00',
'missionLevel': 3,
'missionStep': 3,
'taskLeader': 'Yogurt_cry'
},
{
'indexNumber': 5,
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务',
'createDateTime': '2021-01-19 00:00:00',
'finishDateTime': '2021-01-19 00:00:00',
'missionLevel': 0,
'missionStep': 0,
'taskLeader': 'Yogurt_cry'
},
{
'indexNumber': 6,
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务',
'createDateTime': '2021-01-19 00:00:00',
'finishDateTime': '2021-01-19 00:00:00',
'missionLevel': 0,
'missionStep': 1,
'taskLeader': 'Yogurt_cry'
},
{
'indexNumber': 7,
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务',
'createDateTime': '2021-01-19 00:00:00',
'finishDateTime': '2021-01-19 00:00:00',
'missionLevel': 0,
'missionStep': 2,
'taskLeader': 'Yogurt_cry'
},
{
'indexNumber': 8,
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务',
'createDateTime': '2021-01-19 00:00:00',
'finishDateTime': '2021-01-19 00:00:00',
'missionLevel': 0,
'missionStep': 3,
'taskLeader': 'Yogurt_cry'
},
{
'indexNumber': 9,
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务',
'createDateTime': '2021-01-19 00:00:00',
'finishDateTime': '2021-01-19 00:00:00',
'missionLevel': 0,
'missionStep': 0,
'taskLeader': 'Yogurt_cry'
},
{
'indexNumber': 10,
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务',
'createDateTime': '2021-01-19 00:00:00',
'finishDateTime': '2021-01-19 00:00:00',
'missionLevel': 0,
'missionStep': 1,
'taskLeader': 'Yogurt_cry'
},
{
'indexNumber': 11,
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务',
'createDateTime': '2021-01-19 00:00:00',
'finishDateTime': '2021-01-19 00:00:00',
'missionLevel': 0,
'missionStep': 2,
'taskLeader': 'Yogurt_cry'
},
{
'indexNumber': 12,
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务',
'createDateTime': '2021-01-19 00:00:00',
'finishDateTime': '2021-01-19 00:00:00',
'missionLevel': 0,
'missionStep': 3,
'taskLeader': 'Yogurt_cry'
},
{
'indexNumber': 13,
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务',
'createDateTime': '2021-01-19 00:00:00',
'finishDateTime': '2021-01-19 00:00:00',
'missionLevel': 0,
'missionStep': 0,
'taskLeader': 'Yogurt_cry'
}
]
}
}
}
</script>
<style>
</style>
4.3.2 表单模板
表单模板
的呈现形式和 需求列表
是一致的,直接复制过来修改一下就好了。
<template>
<div style = "padding: 10px; witdh: calc(100% - 20px); height: calc(100% - 20px);">
<div style = "background-color: #ffffff; width: 100%; height: 100%; border-radius: 4px; border: solid 1px #e5e5e5; overflow: hidden;">
<el-table :data = "tableData"
stripe
height = "590"
style = "width: calc(100% - 20px); margin: 10px;">
<el-table-column label = "序号"
prop = "indexNumber"
width = "80"
align = "center"></el-table-column>
<el-table-column label = "模板名称"
prop = "templateName"
min-width = "200"
show-overflow-tooltip
align = "center"></el-table-column>
<el-table-column label = "创建时间"
prop = "createDateTime"
min-width = "180"
align = "center"></el-table-column>
<el-table-column label = "使用次数"
prop = "executeCount"
min-width = "80"
align = "center"></el-table-column>
<el-table-column label = "表单状态"
prop = "templateState"
min-width = "80"
align = "center">
<template slot-scope = "scope">
<el-tag v-if = "scope.row.templateState === 0"
type = "info"
disable-transitions>未启用</el-tag>
<el-tag v-else-if = "scope.row.templateState === 1"
type = "success"
disable-transitions>启用</el-tag>
<el-tag v-else-if = "scope.row.templateState === 2"
type = "danger"
disable-transitions>禁用</el-tag>
</template>
</el-table-column>
<el-table-column label = "创建人"
prop = "createPerson"
min-width = "180"
align = "center"></el-table-column>
<el-table-column label = "操作"
min-width = "100"
align = "center">
<template slot = "header">
<el-button size = "medium"
type = "primary">创建表单</el-button>
</template>
<template>
<el-button size = "small">
<span>编辑</span>
</el-button>
</template>
</el-table-column>
</el-table>
<div style = "height: 32px; margin-top: 5px;">
<el-pagination style = "float: right;"
background
layout = "total, sizes, prev, pager, next"
:page-sizes = "[100, 200, 300, 400]"
:total = "1000">
</el-pagination>
<div style = "clear: both;"></div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
'tableData': [
{
'indexNumber': 1,
'templateName': '测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单',
'createDateTime': '2021-01-19 00:00:00',
'executeCount': 1,
'templateState': 0,
'createPerson': 'Yogurt_cry'
},
{
'indexNumber': 2,
'templateName': '测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单',
'createDateTime': '2021-01-19 00:00:00',
'executeCount': 1,
'templateState': 1,
'createPerson': 'Yogurt_cry'
},
{
'indexNumber': 3,
'templateName': '测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单',
'createDateTime': '2021-01-19 00:00:00',
'executeCount': 1,
'templateState': 2,
'createPerson': 'Yogurt_cry'
},
{
'indexNumber': 4,
'templateName': '测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单',
'createDateTime': '2021-01-19 00:00:00',
'executeCount': 1,
'templateState': 0,
'createPerson': 'Yogurt_cry'
},
{
'indexNumber': 5,
'templateName': '测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单',
'createDateTime': '2021-01-19 00:00:00',
'executeCount': 1,
'templateState': 1,
'createPerson': 'Yogurt_cry'
},
{
'indexNumber': 6,
'templateName': '测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单',
'createDateTime': '2021-01-19 00:00:00',
'executeCount': 1,
'templateState': 2,
'createPerson': 'Yogurt_cry'
},
{
'indexNumber': 7,
'templateName': '测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单',
'createDateTime': '2021-01-19 00:00:00',
'executeCount': 1,
'templateState': 0,
'createPerson': 'Yogurt_cry'
},
{
'indexNumber': 8,
'templateName': '测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单',
'createDateTime': '2021-01-19 00:00:00',
'executeCount': 1,
'templateState': 1,
'createPerson': 'Yogurt_cry'
},
{
'indexNumber': 9,
'templateName': '测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单',
'createDateTime': '2021-01-19 00:00:00',
'executeCount': 1,
'templateState': 2,
'createPerson': 'Yogurt_cry'
},
{
'indexNumber': 10,
'templateName': '测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单测试表单',
'createDateTime': '2021-01-19 00:00:00',
'executeCount': 1,
'templateState': 0,
'createPerson': 'Yogurt_cry'
}
]
}
}
}
</script>
<style>
</style>
4.3.3 用户管理
<template>
<div style = "padding: 10px; witdh: calc(100% - 20px); height: calc(100% - 20px);">
<div style = "background-color: #ffffff; width: 100%; height: 100%; border-radius: 4px; border: solid 1px #e5e5e5; overflow: hidden;">
<el-table :data = "tableData"
stripe
height = "590"
style = "width: calc(100% - 20px); margin: 10px;">
<el-table-column label = "序号"
prop = "indexNumber"
width = "80"
align = "center"></el-table-column>
<el-table-column label = "用户邮箱"
prop = "email"
min-width = "200"
align = "center"></el-table-column>
<el-table-column label = "用户备注"
prop = "remark"
width = "180"
align = "center"></el-table-column>
<el-table-column label = "提交次数"
prop = "missionCount"
min-width = "80"
align = "center"></el-table-column>
<el-table-column label = "当前状态"
prop = "userState"
min-width = "80"
align = "center">
<template slot-scope = "scope">
<el-tag v-if = "scope.row.userState === 0"
type = "info"
disable-transitions>未启用</el-tag>
<el-tag v-else-if = "scope.row.userState === 1"
type = "success"
disable-transitions>正常</el-tag>
<el-tag v-else-if = "scope.row.userState === 2"
type = "danger"
disable-transitions>已禁用</el-tag>
</template>
</el-table-column>
<el-table-column label = "创建时间"
prop = "createDateTime"
width = "180"
align = "center"></el-table-column>
<el-table-column label = "操作"
min-width = "100"
align = "center">
<template>
<el-button size = "small">
<span>编辑</span>
</el-button>
</template>
</el-table-column>
</el-table>
<div style = "height: 32px; margin-top: 5px;">
<el-pagination style = "float: right;"
background
layout = "total, sizes, prev, pager, next"
:page-sizes = "[100, 200, 300, 400]"
:total = "1000">
</el-pagination>
<div style = "clear: both;"></div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
'tableData': [
{
'indexNumber': 1,
'email': '[email protected]',
'missionCount': 99,
'userState': 0,
'createDateTime': '2021-01-19 21:53:00',
'remark': 'Yogurt_cry'
},
{
'indexNumber': 2,
'email': '[email protected]',
'missionCount': 99,
'userState': 1,
'createDateTime': '2021-01-19 21:53:00',
'remark': 'Yogurt_cry'
},
{
'indexNumber': 3,
'email': '[email protected]',
'missionCount': 99,
'userState': 2,
'createDateTime': '2021-01-19 21:53:00',
'remark': 'Yogurt_cry'
},
{
'indexNumber': 4,
'email': '[email protected]',
'missionCount': 99,
'userState': 0,
'createDateTime': '2021-01-19 21:53:00',
'remark': 'Yogurt_cry'
},
{
'indexNumber': 5,
'email': '[email protected]',
'missionCount': 99,
'userState': 1,
'createDateTime': '2021-01-19 21:53:00',
'remark': 'Yogurt_cry'
},
{
'indexNumber': 6,
'email': '[email protected]',
'missionCount': 99,
'userState': 2,
'createDateTime': '2021-01-19 21:53:00',
'remark': 'Yogurt_cry'
},
{
'indexNumber': 7,
'email': '[email protected]',
'missionCount': 99,
'userState': 0,
'createDateTime': '2021-01-19 21:53:00',
'remark': 'Yogurt_cry'
},
{
'indexNumber': 8,
'email': '[email protected]',
'missionCount': 99,
'userState': 1,
'createDateTime': '2021-01-19 21:53:00',
'remark': 'Yogurt_cry'
},
{
'indexNumber': 9,
'email': '[email protected]',
'missionCount': 99,
'userState': 2,
'createDateTime': '2021-01-19 21:53:00',
'remark': 'Yogurt_cry'
}
]
}
}
}
</script>
<style>
</style>
4.3.4 需求看板
把前面那些页面布局做好了,大概也知道自己要在看板上关注什么了。这个看板除了自己要看以外,提交需求的人也需要看到,了解 Yogurt 的需求列表之后他就会知道为什么有些需求交过来没有那么快能够完成的原因。
<template>
<div style = "padding: 10px; witdh: calc(100% - 20px); height: calc(100% - 20px);">
<div style = "background-color: #ffffff; width: calc(100% - 40px); height: calc(100% - 40px); border-radius: 4px; border: solid 1px #e5e5e5; overflow: hidden; padding: 20px;">
<!-- 系统数据 -->
<el-row :gutter = "12">
<el-col :span = "4">
<el-card style = "height: 130px;"
shadow = "hover">
<div>
<div style = "font-size: 32px; font-weight: 900; line-height: 58px; color: #409eff;">{
{ keyData.waitFor }}</div>
<div>待受理</div>
</div>
</el-card>
</el-col>
<el-col :span = "4">
<el-card style = "height: 130px;"
shadow = "hover">
<div>
<div style = "font-size: 32px; font-weight: 900; line-height: 58px; color: #409eff;">{
{ keyData.processing }}</div>
<div>已受理</div>
</div>
</el-card>
</el-col>
<el-col :span = "4">
<el-card style = "height: 130px;"
shadow = "hover">
<div>
<div style = "font-size: 32px; font-weight: 900; line-height: 58px; color: #409eff;">{
{ keyData.finish }}</div>
<div>已完成</div>
</div>
</el-card>
</el-col>
<el-col :span = "4">
<el-card style = "height: 130px;"
shadow = "hover">
<div>
<div style = "font-size: 32px; font-weight: 900; line-height: 58px; color: #409eff;">{
{ keyData.responseSpeed }}</div>
<div>响应时效</div>
</div>
</el-card>
</el-col>
<el-col :span = "4">
<el-card style = "height: 130px;"
shadow = "hover">
<div>
<div style = "font-size: 32px; font-weight: 900; line-height: 58px; color: #409eff;">{
{ keyData.finishSpeed }}</div>
<div>结案时效</div>
</div>
</el-card>
</el-col>
<el-col :span = "4">
<el-card style = "height: 130px;"
shadow = "hover">
<div>
<div style = "font-size: 32px; font-weight: 900; line-height: 58px; color: #409eff;">{
{ keyData.user }}</div>
<div>用户数</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 待办事项 -->
<div style = "width: 100%; text-align: left; padding-top: 25px; padding-bottom: 10px; font-weight: 900;">
<span style = "padding: 0 2.5px; background-color: #409eff; margin-right: 10px;"></span>当前受理任务队列 - Top 10
</div>
<div v-for = "(item, index) in missionList"
:key = "index"
style = "width: 100%; text-align: left; margin: 15px 0;">
<div style = "width: 700px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; float: left; line-height: 26px;">
<el-tag effect = "plain"
style = "height: 26px; line-height: 26px;">
<span>{
{ item.finishDateTime }}</span>
</el-tag>
<el-tag :type = "item.missionLevel === 0 ? 'info' : item.missionLevel === 1 ? 'primary' : item.missionLevel === 2 ? 'warning' : 'danger'"
style = "height: 26px; line-height: 26px;">
<span>{
{ item.missionLevel === 0 ? '普通' : item.missionLevel === 1 ? '一般' : item.missionLevel === 2 ? '重要' : '紧急' }}</span>
</el-tag>
<el-tag :type = "item.timeBalance >= 72 ? 'info' : item.timeBalance >= 48 ? 'primary' : item.timeBalance >= 24 ? 'warning' : 'danger'"
style = "height: 26px; line-height: 26px;">
<span>剩余 {
{ item.timeBalance }} 小时</span>
</el-tag>
<span style = "margin-left: 10px;">{
{ item.missionName }}</span>
</div>
<el-progress style = "width: calc(100% - 700px - 20px); float: left; margin-left: 20px; margin-top: 2px;"
:text-inside = "true"
:stroke-width = "22"
:percentage = "item.missionProgress"></el-progress>
<div style = "clear: both;"></div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
'keyData': {
'waitFor': '999',
'processing': '999',
'finish': '999',
'responseSpeed': '1.0',
'finishSpeed': '1.0',
'user': '999'
},
'missionList': [
{
'missionLevel': 0,
'timeBalance': 10,
'finishDateTime': '2021-01-20 10:29:00',
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务测试任务',
'missionProgress': 50
},
{
'missionLevel': 1,
'timeBalance': 1,
'finishDateTime': '2021-01-20 10:29:00',
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务测试任务',
'missionProgress': 70
},
{
'missionLevel': 2,
'timeBalance': 25,
'finishDateTime': '2021-01-20 10:29:00',
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务测试任务',
'missionProgress': 10
},
{
'missionLevel': 3,
'timeBalance': 49,
'finishDateTime': '2021-01-20 10:29:00',
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务测试任务',
'missionProgress': 1
},
{
'missionLevel': 3,
'timeBalance': 9999,
'finishDateTime': '2021-01-20 10:29:00',
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务测试任务',
'missionProgress': 1
},
{
'missionLevel': 1,
'timeBalance': 1,
'finishDateTime': '2021-01-20 10:29:00',
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务测试任务',
'missionProgress': 70
},
{
'missionLevel': 2,
'timeBalance': 25,
'finishDateTime': '2021-01-20 10:29:00',
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务测试任务',
'missionProgress': 10
},
{
'missionLevel': 3,
'timeBalance': 49,
'finishDateTime': '2021-01-20 10:29:00',
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务测试任务',
'missionProgress': 1
},
{
'missionLevel': 3,
'timeBalance': 9999,
'finishDateTime': '2021-01-20 10:29:00',
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务测试任务',
'missionProgress': 1
},
{
'missionLevel': 1,
'timeBalance': 1,
'finishDateTime': '2021-01-20 10:29:00',
'missionName': '测试任务测试任务测试任务测试任务测试任务测试任务测试任务',
'missionProgress': 70
}
]
}
}
}
</script>
<style>
</style>
4.3.5 授权失败页
既然做了权限划分,那么自然就会有误入的情况,那么就需要每次进入页面时检查一下用户授权的情况,无授权的用户误入页面时提示用户授权失败,然后中断功能的请求。
<template>
<div style = "padding: 200px; color: #a6a6a6;">
<div style = "margin: 20px 0;">
<i class = "el-icon-warning"
style = "font-size: 100px;"></i>
</div>
<div style = "font-weight: 900; font-size: 24px;">当前功能未授权</div>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
5 小结
功能结构差不多确定了,需求看板
和 需求列表
不管是 管理员
还是 访客
看到的都是同一个页面,只不过有所区别的是 管理员
是可以对 需求列表
进行操作的,而 访客
只能是 提交需求
和查看需求的执行情况。表单模板
和 用户管理
是仅 管理员
可见的。
后面的开发就根据当前功能的定义进行细节的开发就好了。