上一章我们基本完成了的项目工程化,项目已经可以自动实时重载并完成日志映射。通过配置也为以后项目打包上线打下了基础,那么现在终于到了设计界面的时候了。

一. 分析&布局设计

当前我们项目的界面是这样的:

简单来说,看不出一点管理系统的样子,到Axure中文网找了一下,管理类网站的原型应该是下面这个样子的:

照着撸一个呗,管理系统界面一般由顶部导航&状态栏、左侧导航栏、右侧内容区域三个部分组成,也就是这样的:

我们网站内容较少所以顶部无需导航栏,也就是顶部只需保留左侧标题和右侧状态部分,接着再填充一下界面,顶部左侧加入系统名称和logo,顶部右侧加入用户名,左侧导航栏填充入导航项,右侧内容栏根据左侧选中的导航项显示对应的内容:

填充完布局之后好像变得能看了一些,那我们就根据效果图来完成这个界面。

二. 技术选型

借助框架能够快速实现整体样式,选用最通用的样式框架Bootstrap3来协助完成界面,但是如果想要引入Bootstrap3控件的话需要引入jQuery,这是我们不想要的。

这时候我们可以考虑使用和Angular项目的相性最佳的angular-ui-bootstrap来取代Bootstrap3的控件,angular-ui-bootstrap是AngularUI团队在Bootstrap基础上用AngularJS实现的一组UI控件,在达到和Bootstrap控件相同效果的情况下还无需引入jQuery简直棒。

于是我们简单选定了Bootstrap + angular-ui-bootstrap组合来加速界面开发。

三. 开发

3.1 安装

3.1.1 angular-ui-bootstrap安装

参考angular-ui-bootstrap文档-Dependencies,了解到其版本的选择与Angular的版本有对应关系,不过我们用的是AngularJS1.6.10版本所以可以直接安装最新的angular-ui-bootstrap:

yarn add angular-ui-bootstrap --save

由于上面的文档还提到angular-ui-bootstrap需要Angular-animate、Angular-touch、Bootstrap CSS,直接安装yarn安装顺便更新一下angular到1.7.0:

yarn add angular --save
yarn add angular-animate --save
yarn add angular-touch --save
yarn add bootstrap@3 --save

在app.js中加入引用并为'pokemon-app'模块加入依赖(暂时不加入Bootstrap3样式):

import ngAnimate from 'angular-animate';
import ngTouch from 'angular-touch';
import uibootstrap from 'angular-ui-bootstrap';
...
angular.module('pokemon-app', [...ngAnimate,ngTouch,ngUIBootstrap...
])

在index.tpl.html中加入一段文档中的测试代码:

<h4>Single toggle</h4>
<pre>{{singleModel}}</pre>
<button type="button" class="btn btn-primary" ng-model="singleModel" uib-btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">Single Toggle
</button>

接着在app.js中的AppController中加入:

$scope.singleModel = 1;

结果如下:

中间多出了一个button并且可以通过点击修改数字,这表示angular-ui-bootstrap已经安装成功了~

3.1.2 Bootstrap3配置

接下来我们为项目加入Bootstrap.css,CSS可以通过Webpack打包然后在项目入口文件app.js中加载,这里我们要用到css-loader、style-loader、file-loader(加载字体,如果没有这个loader字体会加载失败):

yarn add css-loader style-loader file-loader --save-dev

修改webpack.config.js的module如下:

  module: {rules: [{test: /\.html$/,loader: 'raw-loader'}, {        // 负责css模块加载test: /\.css$/,use: ['style-loader', 'css-loader']}, {test: /\.(woff|woff2|eot|ttf|svg)$/,use: ['file-loader']}]},

app.js中引入

import 'bootstrap/dist/css/bootstrap.min.css';

现在查看自动重载之后的页面,你会发现熟悉的Bootstrap页面样式终于出现了:

3.2 界面开发

首先去掉上面添加的angular-ui-bootstrap测试代码,然后开始界面开发:

3.2.1 顶部状态栏

顶部栏使用navbar样式编写,去掉原来的h1标签然后左边填充icon和系统名右边填充用户名,编写代码如下(图源来自神奇宝贝百科,承诺不用于商业用途):

<nav class="navbar navbar-inverse navbar-fixed-top"><div class="container-fluid"><div class="navbar-header"><a href="#" class="navbar-brand"><img width="20" height="20" src="http://s1.52poke.wiki/wiki/5/5e/Bag_%E7%B2%BE%E7%81%B5%E7%90%83_Sprite.png"></a><a href="#" class="navbar-brand">口袋妖怪管理系统v0.0.1</a></div><div id="navbar" class="navbar-collapse collapse"><ul class="nav navbar-nav navbar-right"><li><a href="#">Nodreame</a></li></ul></div></div>
</nav>

效果如下:

为了防止链接丢失导致图片加载失败,把图片下载下来放在本地assert/img文件夹下,命名为spriteball-common.png。按照Webpack模块化规则,图片也应该作为一个模块来加载,于是参考文档url-loader,在webpack.config.js文件的module中加入:

{test: /\.(png|svg|jpg|gif)$/,use: [{loader: 'url-loader',options: {limit: 8192,fallback: 'file-loader'}}]
}

用url-loader作为小于8192byte图片的加载器,如果符合条件图片链接将会转为一个DataURL,如果超过改限制,将会默认使用file-loader作为图片的加载器,修改后重新编译通过.

现在继续修改index.tpl.html中图片位置的a标签,加入id="icon"并屏蔽原来图片:

<a href="#" class="navbar-brand" id="icon"><!-- <img width="20" height="20" src="http://s1.52poke.wiki/wiki/5/5e/Bag_%E7%B2%BE%E7%81%B5%E7%90%83_Sprite.png" alt=""> --><!-- <img src="../assert/img/spriteball-common.png" alt=""> -->
</a>

在app.js中引入图片,并通过DOM操作把图片插入页面:

import icon from '../assert/img/spriteball-common.png'
...function AppController ($scope) {// $scope.singleModel = 1;var sysIcon = new Image();sysIcon.src = icon;sysIcon.width = 20;sysIcon.height = 20;document.getElementById('icon').appendChild(sysIcon);}

重新编译,在浏览器元素检测中看到图片已成功插入页面并以DataURL形式被引用:

顶部栏基本编写完成~

3.2.2 左侧导航栏 & 右侧内容区域

顶部栏完成之后,左右将分成两部分,这里的页面布局划分Bootstrap3似乎没有提供响应的样式,不过在Bootstrap的官网样例中我们找到了类似的Dashboard,他提供了一个现成的dashboard.css我们可以直接用起来,将dashboard.css放到assert/css文件夹下,并在app.js中引用:

import '../assert/css/dashboard.css'

然后开始跟随Demo简单布局:

<div class="container-fluid"><div class="row"><div class="col-sm-3 col-md-2 sidebar">sidebar</div><div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">main</div></div>
</div>

很简单就完成了页面布局划分(这里左侧导航栏在小于768px时将自动隐藏):

然后继续编写左侧导航栏:

<div class="col-sm-3 col-md-2 sidebar"><ul class="nav nav-sidebar"><li><a href="/#!/pokemons">图鉴</a></li><li><a href="/#!/skills">技能</a></li><li><a href="/#!/hagberrys">树果</a></li><li><a href="/#!/props">道具</a></li><li><a href="/#!/games">游戏</a></li></ul>
</div>

简单的左侧导航栏已经基本完成,并且点击能够看到内容切换,现在我们将被部分遮蔽的内容移动到右侧内容区域:

<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"><div ng-view></div>
</div>

简单移动完成页面:

基础页面完成之后,看看内容样式还是比较丑,跟随dashboard例子和Bootstrap修改其样式,对图鉴页(原来的口袋妖怪详情页)进行修改:

pm-list.html
<h1 class="page-header">图鉴</h1>
<div class="table-responsive"><table class="table table-striped"><thead><tr><th>NO.</th><th>名称</th><th>数量</th><th>重量</th><th>总计</th><th>操作</th></tr></thead><tbody><tr ng-repeat="pokemon in pokemons"><td>{{pokemon.no}}</td><td><a href="/#!/pokemon/{{pokemon.no}}">{{pokemon.name}}</a></td><td><input type="text" ng-model="pokemon.count"></td><td>{{pokemon.weight}}</td><td>{{pokemon.weight * pokemon.count}}</td><td><button class="btn btn-xs btn-primary" ng-click="remove($index)">删除</button></td></tr></tbody></table>
</div>
pm-detail.html
<div><a href="/#!/pokemons"><span class="glyphicon glyphicon-arrow-left"></span>返回图鉴列表</a>
</div>
<h2 class="sub-header"><b>{{pokemon.name}}</b></h2>
<img ng-src="{{pokemon.img}}" width="200" height="200">
<p><b>编号: </b>No.{{pokemon.no}}</p>
<p><b>体重: </b>{{pokemon.weight}}</p>
<p><b>属性: </b>{{pokemon.property}}</p>
<p><b>种类: </b>{{pokemon.type}}</p>
<div><b>特性: </b><ul><li><b>普通特性: </b>{{pokemon.character.common}}</li><li><b>隐藏特性: </b>{{pokemon.character.conceal}}</li></ul>
</div><div ng-show="pokemon.forms"><b style="float: left;">其他形象:</b><br/><div ng-repeat="form in pokemon.forms" style="float: left;"><img ng-src="{{form.src}}"><p style="text-align: center;">{{form.name}}</p></div><div style="clear: both;"></div>
</div>

上面代码中,pm-list修改了标题和表格样式,pm-detail修改了返回样式并稍微修缮了布局,修改结果如下:

其他界面也进行类似的修改,结果如下:

至此基本网站布局已完成。

3.3 操作体验升级

现在网站布局和样式得到了优化,但是一些细节暂时还没处理好,列出一些比较直观能看到的不足:

  • 左侧导航栏交互缺乏选中感
  • 删除按钮没有二次确认容易导致误删

现在我们就来完善这些细节。

3.3.1 左侧导航栏交互

dashboard.css已经帮我们写好了选中左侧导航栏某项之后变蓝底白字的样式,只需要简单在选中项的<li>元素上加上class="active"就行,但是如果用DOM操作来做这个交互就很繁琐,所以考虑通过监听页面当前url来改变<li>元素的class,监听页面url当然是使用AngularJS的&dollar;location服务,该服务中有一个广播&dollar;locationChangeSuccess,在每次url改变之后都会广播事件,这里我们用它来修改全局对象nowUrl,我们在app.js中加入run:

  .run(['$rootScope', '$location', function ($rootScope, $location) {$rootScope.$on('$locationChangeSuccess', function () {$rootScope.nowUrl = $location.url();console.log('nowUrl:', $rootScope.nowUrl);// console.log('$route,routes.null.redirectTo:', $route.routes.null.redirectTo);});}])

监听页面切换的日志结果如下:

ke'yi看到获取到的nowUrl都是http://localhost:8080/#!后面的部分,那么了解到这点之后我们就可以尝试在index.tpl.html中借助ng-class指令来完成"根据当前url选中对应导航项"的操作了,修改index.tpl.html中<ul>元素部分如下:

<ul class="nav nav-sidebar"><li ng-class="{'/pokemons': 'active'}[nowUrl]"><a href="/#!/pokemons">图鉴</a></li><li ng-class="{'/skills': 'active'}[nowUrl]"><a href="/#!/skills">技能</a></li><li ng-class="{'/hagberrys': 'active'}[nowUrl]"><a href="/#!/hagberrys">树果</a></li><li ng-class="{'/props': 'active'}[nowUrl]"><a href="/#!/props">道具</a></li><li ng-class="{'/games': 'active'}[nowUrl]"><a href="/#!/games">游戏</a></li>
</ul>

3.3.2 删除二次确认

为了达到二次确认删除的效果,我们可以使用angular-ui-bootstrap提供的模态框Modal,参考Modal.
首先我们在src目录下新建文件夹common来存放通用的html模板,新建文件deleteDialog.tpl.html作为模态框的模板文件:

<div class="modal-header"><h3 class="modal-title" id="modal-title">{{modalTitle}}</h3>
</div>
<div class="modal-body" id="modal-body">{{modalBody}}
</div>
<div class="modal-footer"><button class="btn btn-danger" type="button" ng-click="ok()">删除</button><button class="btn btn-default" type="button" ng-click="cancel()">取消</button>
</div>

接下来就可以编写触发模态框的逻辑了,模仿文档修改pokemon.js中PMListController如下:

PMListController.$inject = ['$scope', '$uibModal'];
function PMListController ($scope, $uibModal) {$scope.pokemons = pokemons;console.log($scope.pokemons);$scope.remove = function (index) {console.log('index:', index);var modalInstance = $uibModal.open({animation: true,ariaLabelledBy: 'modal-title',ariaDescribedBy: 'modal-body',template: delDiage,controller: 'DeleteInstanceController',resolve: {pokemon: function () {return $scope.pokemons[index];}}});modalInstance.result.then(function (content) {console.log('Delete!', content);$scope.pokemons.splice(index, 1);   }, function (content) {console.log('Cancel!', content);});};
}

上面我们做了两处修改:

1. 为PMListController加入了依赖$uibModal,用以调用模态框;
2. 修改remove方法,使用$uibModal.open()创建模态框实例,并用实例编写模态框关闭的promise,关闭时选择close或cancel将触发不同事件。

完成了模态框触发逻辑编写之后,我们开始编写模态框的逻辑:

DeleteInstanceController.$inject = ['$scope', '$uibModalInstance', 'pokemon'];
function DeleteInstanceController ($scope, $uibModalInstance, pokemon) {// console.log('thisIndex:', thisIndex);console.log('pokemon:', pokemon);$scope.modalTitle = '删除';$scope.modalBody  = '是否删除' + pokemon.name + '的数据';$scope.ok = function () {console.log('delete!');$uibModalInstance.close(pokemon);};$scope.cancel = function () {console.log('cancel!');$uibModalInstance.dismiss('cancel');};
}

这里加入了$uibModalInstance和pokemon依赖,$uibModalInstance代表当前模态框对象,pokemon是$uibModal.open()配置中resolve传递过来的数据。在该controller中完成模态框的内容编写以及两个button触发的事件。接下来我们把这个controller加入module:

export default angular.module('pokemon-app.pokemon', [ngRoute]).config(['$routeProvider', function ($routeProvider) {$routeProvider.when('/pokemons', {template: pmlist,controller: 'PMListController'}).when ('/pokemon/:no', {template: pmdetail,controller: 'PMDetailController'})}]).controller('PMListController', PMListController).controller('PMDetailController', PMDetailController).controller('DeleteInstanceController', DeleteInstanceController).name;

倒数第二行就是新加入module的controller,pm-list.html不用作任何修改,保存等待自动编译重载。

至此,系统的操作体验升级已经基本完成,为了系统体验同步,我们需要把模态框的效果也应用到其他的界面上。

四、源码

口袋妖怪SPA系统源码地址:https://github.com/Nodreame/p...

本章基本功能提交:feat(project): refactor ui

五、总结

完成了项目界面重做之后系统终于有了一些管理系统的样子,不过开发过程中也暴露了一些问题,例如模态框完成第一次开发之后应用到别的界面的过程耗费了挺多事件和工作量,经常会写少一些东西导致运行错误,是时候找找有没有能够一处编写到处使用的方法了!请看下章~

系列文章

从零开始搭建口袋妖怪管理系统(1)-从Angular1.x开始

从零开始搭建口袋妖怪管理系统(2)-借助ngRoute实现详情页面跳转

从零开始搭建口袋妖怪管理系统(3)-实现一个简单的SPA管理系统

从零开始搭建口袋妖怪管理系统(4)-借助webpack4.6工程化项目(上)

从零开始搭建口袋妖怪管理系统(5)-借助webpack4.6工程化项目(下)

To be continue...

从零开始搭建口袋妖怪管理系统(6)-界面重做相关推荐

  1. 从零开始搭建口袋妖怪管理系统(3)-实现一个简单的SPA管理系统

    一.目标 上一章我们完成了口袋妖怪的细节展示页面,这次我想要把总计划的框架搭起来,也就是建立起一个口袋妖怪SPA管理系统,包含口袋妖怪.技能.树果.道具.游戏这样五个模块,并且能够通过导航平滑跳转到指 ...

  2. 从零开始搭建口袋妖怪管理系统(2)-借助ngRoute实现详情页面跳转

    一.目标 上一次我们用Angular1.x完成了简单的口袋妖怪展示列表页面,现在我们想要了解口袋妖怪更多的信息,但是发现原有单行表格可能容纳不下口袋妖怪的所有信息,所以现在我们需要一个口袋妖怪详情界面 ...

  3. 从零开始搭建口袋妖怪管理系统(4)-借助webpack4.6工程化项目(上)

    "手动是不可能手动的了,这辈子都不可能手动的了." 一.目标 上一章我们借助ngRoute,完成了口袋妖怪SPA系统的多模块导航开发,但是现在引用的东西越来越多,项目文件目录开始变 ...

  4. 从零开始搭建一个管理系统-vue3.0项目创建-----1

    从零开始搭建一个管理系统-vue3.0项目创建-----1 讲在前面 webpack构建项目 代码编辑器 依赖修改 码云代码地址 讲在前面 假设你已了解关于 HTML.CSS. JavaScript ...

  5. 「实践篇」从零开始搭建公文管理系统

    公文管理是一种重要的综合管理工作,主要针对文流转进行管理如收文管理和发管理.在现代社会中,各类机构和企业的公文流转量都很大,因此进行有效的公文管理变得至关重要.接下来将介绍公文管理的主要内容: 一.收 ...

  6. vue + element ui 的后台管理系统框架_从零开始搭建 VUE + Element UI后台管理系统框架...

    点击右上方红色按钮关注"web秀",让你真正秀起来 前言 后台管理系统前端框架,现在很流行的形式都是,上方和左侧都是导航菜单,中间是具体的内容.比如阿里云.七牛云.头条号.百家号等 ...

  7. elementui 搭建布局页面路由_【项目实践】使用Vue.js和ElementUI快速实现后台管理系统的界面布局...

    前言 大家都知道,后台管理界面不需要很酷炫的动画效果,也不需要花里胡哨的界面布局,只需要简洁易用.清爽明了的界面以便于管理数据.而现在普遍的后台管理系统的界面布局都差不多,上中下结构,然后左边是导航栏 ...

  8. 【Vue 快速入门】从零开始搭建 VUE + Element UI后台管理系统框架

    [Vue 快速入门]从零开始搭建 VUE + Element UI后台管理系统框架 前言 后台管理系统前端框架,现在很流行的形式都是,上方和左侧都是导航菜单,中间是具体的内容.比如阿里云.七牛云.头条 ...

  9. 从零开始搭建多租户自洽的权限数据配置模块(二)- 主界面的跳转管理以及基础数据维护设计

    基于 DevExpress 从零开始搭建多租户自洽的权限数据配置模块(二) 基础数据的维护管理,以简单基本操作的形式展开.主要是演示devexpress做基本的增删改查.加载表单.建立多表关联.用户操 ...

最新文章

  1. MindSpore:自动微分
  2. Udacity机器人软件工程师课程笔记(四)-样本搜索和找回-基于漫游者号模拟器-决策树
  3. 中国AI专利申请量超美国,百度居国内首位
  4. Partition Tables介绍及分区表转换
  5. 语义分割中的深度学习方法全解:从FCN、SegNet到各版本DeepLab
  6. python制作工资计算器-Python计算个人所得税
  7. 模块20135304——刘世鹏
  8. iwconfig的用法
  9. 云服务器系统重装为windows,并进行文件传输
  10. php静态地图api,静态图API | 百度地图API SDK
  11. nameof() 到底是编译时还是运行时行为?
  12. fdisk自动进行分区
  13. html引入php文件中的函数,手把手教你在html中引入另一个html文件的方法(详解)...
  14. DBCA静默建库中的两个小问题
  15. OpenShift应用镜像构建(4) - fabric8-maven-plugin
  16. android 阿拉伯语下布局,android设计的布局在阿拉伯语下界面错乱的解决方法
  17. 2019.3.20 I/O相关
  18. QQ音乐、网易云音乐、酷狗音乐歌单导入到Spotify
  19. USDT信用卡和转账入账接口
  20. vue脚手架vue-cli的卸载与安装方式

热门文章

  1. 阿里云移动推送服务使用教程
  2. 使用Python打开webp等格式图片
  3. 一键部署区块链背后的秘密(上)
  4. EMQX3.0多端数据传输(解决方案)
  5. 股民必须牢记的二十一条军规
  6. 使用phpnuke建站心得
  7. 小学一年级300字优秀作文雪景,心田花开点评
  8. 梦依莲品牌女装 迷人的个性风采
  9. 锤子便签的Monkeyrunner 测试脚本
  10. SAP-FI模块 处理自动生成会计凭证增强