首页

网站建设-如何建设一个优质的网站?

seo达人

 

网站建设-如何建设一个优质的网站?

1.jpeg网站建设

互联网时代的到来,作为一个企业你如果还没有属于自己的网站,那么你就真的“out”了,互联网的日流量达到上亿,这些不是简单的点击率,而是一笔真真正正的大钱。如果你还不会互联网,不了解互联网,那么这些都与你毫无关系。而网站建设是企业面向互联的主要窗口之一。也是大家公认渠道之一。

网站建设对于企业来说意义重大,它不仅是企业展示的窗口那么简单。互联网时代下,众多企业纷纷进行互联网转型,那是因为这些企业的领导者拥有者敏锐的嗅觉,具有洞察时代大趋势的能力。网站建设使他们首先要做的事情。那么如何建设一个优质的网站呢?

2.jpeg网站建设

第一:网站建设平台要有强大开发团队

网站建设是一门技术活,没有强大的技术作为支撑,就无法保障网站的安全性和功能的实现。开发团队的强大决定了网站质量与品质。拥有一稳定的后台,才能确保企业网站的一系列工作顺利进行。

第二:网站建设要有专业的设计团队

一个网站就好比一个门面,设计人员就是装修的师傅。门面不好看给客户的第一印象就是不专业,第一印象分就大打折扣,不能引起客户的重视。设计的不合理,就会影响到客户的体验度。一个没有体验度网站,很难留住客户,进行流量的转化。一切设计以客户的体验度为核心。

第三:网站建设要跟上时代的步伐

时代变,网站建设的功能和需求点也在变,只有去适应时代的打造需求,企业才能在竞争激烈的时代下生存。功能的更新换代也要跟上时代的步伐,才能满足客户的体验需求。客户才会为我们买单。

3.jpegSEO优化

第四:网站建设要有利于SEO优化

网站建设只是完成面建设,而我们建设网站的目的是为了,引来流量,转化流量,实现利润转化。一个没有不利于SEO优化的网站,就像埋藏再深山的金山,难以被发现,吸引不到客户。SEO的意义在于打通前往金山的通道。海、陆、空全方位立体打通,让客户络绎不绝前往。金山才得以被发现,被开发。

第五:网站坚持维护更新

这个时代不是大鱼吃小鱼的时代,而是快鱼吃慢鱼的时代。只有不断的去完善更新,才能适应生存。网站也一样,只有不断优化升级才能不被淘汰。

第六:网站建设要符合国家政策

要及时了解国家的相关法律法规以及一些相关的政策,例如最新的广告法,不能只用不符合要求的字眼。例如分销系统,不能越过法律底线。那些文字可以商业,哪些是有版权的等。

以上是一些个人的网站建设个人见解,如有更好的意见也欢迎大家一起探讨,相互学习。

 

 


蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请扫码蓝小助,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系。

截屏2021-05-13 上午11.41.03.png



文章来源:SEO

分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。


 

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

用户错了吗? 防错与容错

涛涛

据说只要可能犯错,就一定会有人犯错,用户犯错的可能性比我们想象中要大得多得多得多
做好防错与容错,可以让用户更顺畅...




据说只要可能犯错,就一定会有人犯错,用户犯错的可能性比我们想象中要大得多得多得多得多。

做好防错与容错,可以让用户更顺畅的达成自己的目标。


恩,用户总是那么不可理喻对吧

都做得那么明显了,还犯错。该提醒的也提醒了...诶~

打住,打住,当用户犯错时,别急着怪他,我们需要先判断清楚。


一、用户真的错了吗?

用户委屈的说:“来来来,我就想下载一个资源,大家都是讲道理的设计师,来评评理,我错在哪里?”

这,这... 每一步的确都好像没问题,那,问题究竟在哪?

作为设计师的你是不是已经敏锐的察觉到了什么?

对,关键点在 ' 67bh ' 这里。都是空格惹的祸!

那好,你准备好方案救用户了吗?......(这里理论上应该停顿30s以上吧)。

当然,现在不会公布答案,方式有好多种。留着你看完文章中间部分再跟大家一起找方案吧。

这一部分的重点不是方案,而是思维。


很多时候,用户并没有错,作为一个有担当的设计师,我们可不能甩锅。

有了这个前提,我们就可以去掉心中的那些烦躁和埋怨...开始愿意伸出援手,去拯救用户了。


二、如何避免用户“犯错”

对,这个疑问就已经包含了解决方案了。

真的感叹中文的博大精深,避 (防错)、免 (容错)。

为了让大家能更好的理解什么是防错和容错,我特意举一个例子哈~

(什么,防错容错你都懂?你怎么知道我讲的跟你想的一样呢,既然这么厉害赶紧做下面这道题。)

对,就是图片里那个拿滑板的家伙,估计是第一次来这片海滩,有鲨鱼都不知道,还使劲忘海里冲...


从避(防错)的角度来看,你有什么方法帮他呢?(先不考虑哪种方法更好也不要考虑成本哈,能达到目的的都行。)

友情提示:防错的目的是让用户在操作之前,尽量减少用户出错的可能性。


看看的你脑袋是不是真的转得飞起...

我这边有几个想法,看看我们有没有缘,想到一块儿去了:

  • 大声告诉他,这位拿滑板的大哥,海里有鲨鱼,别忘海里冲了...

  • 警告这位大哥,嘿兄弟,这里只允许坐轮船和直升机去海岛那边。

  • 在通往海岛的路线全程搭起防鲨网。

恩,目前就想到这些了,还有方案的同学可以在评论区炸沙发哈。


这些方式,我们试着抽象一下:

  • 【提前做好引导和提示】大声告诉他,这位拿滑板的大哥,海里有鲨鱼,别忘海里冲了...

  • 【优先选择减少输入】警告这位大哥,嘿兄弟,这里只允许坐轮船和直升机去海岛那边。

  • 【限制原操作范围】在通往海岛的路线全程搭起防鲨网。

大家应该都发现了,这种方式虽然能达到让用户安全的目的,但是用户会有挫败感,同时也会有被限制的感觉。

我只要说到USB插头,大家应该就开始脑补画面了吧“为什么我插的进去的第一次永远是错的方向,OMG!”

那,容错呢?


还是这位拿着滑板的大哥,想要去海上的小岛,从免(容错)的角度来看,你有什么方法帮他呢?

友情提示:容错的目的是在用户操作之后,自动纠正/化解错误或提供挽回的方法。


我有几个奇怪的方法,来跟大家分享一下:

  • 呃...驯化/杀死鲨鱼。

  • 派一直专业驱鲨救援队跟随这位大哥(滑板大哥:我何德何能,太感动了)。


同样,我们继续抽象一下:

  • 【让原本的错误不影响用户行为和结果】呃...驯化/杀死鲨鱼。

  • 【出现错误可挽回】派一直专业驱鲨救援队跟随这位大哥,出现鲨鱼就杀鱼。


诶,成本好像非常高哈,但是用户感觉良好哦~

因为他甚至都感知不到这片海还有过危险,按自己喜欢的方式行动就行。

我提一提微信消息的撤销功能,大家可能就会舒缓一口气了。

不知道挽回了多少个尴尬的瞬间,是吧~

可是...我只是一个有无缚鸡之力的设计师,我肝不过鲨鱼怎么办?

来来来,前面那个喊救命的大哥又来了,大家赶紧想想办法吧~


三、试用一下以上方法

(第一步和第四步就是单纯点击,就先删掉了)

前面讲的方法也列在右侧了,看看你们是不是可以针对每种方法找到对应方案了呢?

看看是不是可以这样?


方案一:使用方法-提前做好引导和提示


方案二:使用方法-优先选择减少输入


方案三:使用方法-限制操作范围


方案四:使用方法-出现错误可挽回


方案五:使用方法-让错误不影响结果


在这个案例中,大家觉得那个方案更好呢?

好吧,这个问题的确很蠢。当然是 方案二 + 方案五 结合使用了。因为是两个平台的交互,而对于各自平台而言,一个控制好输出,一个控制好输入。对平台自身的容错性有很大的帮助。

通过上面的案例,相信大家都已经熟练掌握好防错和容错的方法了。

什么,还没掌握?那你滚回去,(望着你正在慢慢蜷缩的身体)不.........不是你滚,是你的鼠标往回滚!

那,我们先不管那几个往回滚的同学,开始进行强化学习啦~


四、带着思考学习

找了一些生活中经常用到的产品,让大家感受一下防错容错的魅力。

如果你根本无法从图中发现什么,那我只能说... 你还是需要经常用银杏叶擦擦眼睛了(因为擦了之后你就更容易发现(人性)。

案例先到这里,大家一定以为要结束了吧~不,不,前面这些都不是最重要的。

方法总会越来越多,靠“别人”总结的方法也就能应个急。

来来来,放空~~~~抛弃你的职业相关认知、抛弃你的设计师的身份,你再看我们的设计。

你像孩子一样,好奇又“无知”。

你会发现,为什么会有这么多奇怪的看不见的规则,为什么我这么难完成我的任务。


五、提前预知错误风险(重点)

如果当客户遇到问题,你能用合适的方法去解决,那你已经是个不错的设计师了,但离优秀还很远,因为优秀的设计师必须具有敏锐的嗅觉,要在产品团队的前面,感知并规避风险。

所以,更重要的还是,我们必须学会变身。

好了,这一次真的需要大家独立思考了,并且没有标准答案,大家可以交流交流。


在以下场景中,可能会出现什么“错误”,如何有效处理用户“犯错”?

在生活中,其实会有很多很多“让用户犯错”的产品/场景,多参与其中,去发现问题,去思考解决方案,慢慢的,你就会具备非常好的问题发现能力和解决能力了。

说了这么多,可能有一部分同学会想说:

“用户出错的情况应该很少吧,你花这么长的篇幅”

“你一定低估了用户的智商”

我告诉你,出来玩玩,谁带智商做事啊~

不信你看。


六、那些你可能想象不到的用户错误

百度一下,你会发现用户犯错的可能性大大超出你的预期。

只是,你以为与你“无关”而已。


七、防止用户犯错的设计可能比你想象中更难,更重要

要防止用户犯错,你需要不断的模拟用户的操作行为,去捕获那些可能存在的问题,然后将它一一化解;这个过程很难,因为你要抛开自己以往的经验和认知,去感受用户的视角。


想想,用户体验的改进不就是让产品越来越符合用户认知,从而减少用户“犯错”吗?

只是有些错误,我们只作为围观者,将它推向了用户。

从现在起,将“错误”留给自己。




 蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请扫码蓝小助,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系。

截屏2021-05-13 上午11.41.03.png



文章来源:站酷   作者:得勿

分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务






移动端导航设计

涛涛

合理的移动导航设计能够尽可能地减少摩擦,引导用户去他们要去的地方。

这篇文章汇总了移动端导航设计最常见的样式、要注意的设计准则以及优秀的案例分析,一起来系统性地掌握这些知识~

什么是移动端导航?

简单来说,导航是用户从 A 点到 B 点的方式,是他们发现设计点并与产品交互的过程。

可能很多用户认为导航的目标是“在尽可能短的时间内让用户从 A 到 B”,但时间短只属于操作结果,这个结果需要依靠合理且简单的设计才能实现。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 一个页面中可以存在多种导航。例如在油管首页,有顶部导航栏、筛选器导航和底部导航,这些导航相互搭配为产品助力。

移动端导航常见的设计样式

1. 汉堡菜单

围绕汉堡菜单有很多争论,但存在即合理,在合适的场景下汉堡菜单也能发挥大的作用。

来看一下汉堡菜单具备的优势:

  • 视觉空间:节省屏幕空间,包含有价值的信息;
  • 心智模型:大多数用户熟悉这种设计样式并知道如何操作;
  • 使用经验:调节学习曲线,改善使用体验。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 使用用户熟悉的设计可以事半功倍。例如美团和饿了么两个产品有着完全不同的主题色,但外卖点餐流程却是一样的,仍然是用户熟悉的操作,并没有因为产品的不同而改变点餐流程。

2. 底部导航

底部导航栏通常包含产品中最主要的导航链接,用户只需要简单的点击就能直观地在不同页面间切换。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 几乎每款产品都缺少不了底部导航栏,它方便用户单手操作,不需要太费力就能快速访问产品页面,提高可用性。

3. 顶部导航

关于顶部导航,可以看之前分享的文章,里面详细介绍了顶部导航的设计方法。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 顶部导航通常包含页面中最重要的信息,与其他辅助导航结合使用。

4. 卡片式导航

卡片式是一种出色的设计样式,支持改变各种形状和大小,并且能展示文本、链接或照片等各种元素。

随着网络上的内容越来越碎片化和个性化,卡片是在页面中聚合单个信息的好方式。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 为了改进体验,卡片可以个性化显示不同的内容。另外卡片很容易适应不同的屏幕尺寸,配合响应性设计。

5. 标签

标签往往是在一个大主题下同时支持多个选项,每个选项都转到不同的界面。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 标签通常用于在同一页面中的几个视图之间切换,展示内容上的差异性。而顶部导航栏有主页、搜索、收藏夹等多个图标,代表不同的功能。

6. 基于手势的导航

基于手势的导航可以让用户在所需方向上快速滑动,来完成特定的操作。

这种样式的优点在于,即使最没有经验的用户也很容易掌握,因为手势通常是直观的。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 国外大火的约会产品Tinder以及国内的探探,都使用了基于手势的导航样式,为用户带来滑动的乐趣。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 基于手势的导航并不是Tinder发明的,但这款产品无疑将这种流行带给了大众。经典的向左或向右滑动模式保持了事物的动态性、简单性和娱乐性。

7. 全屏导航

全屏导航是指将大部分屏幕用于导航操作,能够很好地将用户的注意力聚焦到具体的产品细节上。这是一种以连贯的方式提供大量导航的方法,可以立即帮助用户了解产品的功能。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 点击图片可以跳转到全屏大图导航中,能够更清晰地查看商品的外观状态。

8. 3D touch

最初是由苹果公司提供给用户的,这是一种创建导航快捷方式的方法,可以显示选定的 APP 的一些关键操作。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 苹果为手机创造了一种全新的快捷方式,同时提供了强大的可用性。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 3D touch另一个用途是内容预览,在处理内容选项时例如收件箱或文章列表时,这是给用户提供预览的好方法。

移动端导航设计准则

1. 导航需要直观明显

对所有类型的导航来说都是如此。在移动端中由于屏幕空间的缩小和交互成本的增加,导航体验的好坏会对产品产生很大的影响。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 从可用性角度来看,直观的导航对目标用户来说至关重要。这意味着需要进行严格的测试和大量的研究,可以使用卡片分类或树状图等方法来验证导航的可用性。

2. 考虑手指的位置

这点对于移动应用来说至关重要,没有用户想反复点击图标却没有反应。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 链接和按钮的尺寸需要足够大,以便大多数用户在第一次点击时就能成功点击。页面中按钮的尺寸最小通常保持在10mm。

3. 建立视觉层级避免混乱

小屏幕意味着更容易陷入混乱。即使页面中有少量的元素,如果元素没有平衡,用户仍然会有混乱的感觉。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 极简主义是UI设计的一种特定风格,通过必备的顶部导航栏、留白以及由大小、版式、颜色划分的视觉层级来规划页面内容。

移动端导航示例分析

1. Facebook

Facebook 的导航构成比较复杂,融合了多种不同样式的导航。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ Facebook主页包括汉堡菜单、顶部导航栏和底部导航栏。通过这种方式,这家社交媒体巨头明确的将主要内容与次要内容分隔开。

2. Spotify

Spotif 作为音乐流媒体业务的巨头,即使对于新用户来说,页面的设计也容易理解和探索。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 以高度视觉化的形式突出每张卡片背后的关键内容,另外底部导航也可以完成繁重的任务指引。

3. App Store

App Store 是使用标签进行导航的好例证,每个标签代表了同一内容的不同方面。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 在排行榜中,用户可以浏览付费、免费和热门的标签页,从而快速建立一致性和对其他页面的感知。

4. Telegram

Telegram 可以供任何人使用,汉堡菜单提供了用户可能需要的所有关键导航选项。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 每个用户都能立即找出汉堡菜单,非常容易使用和理解,而且几乎不占用界面中的宝贵空间。

5. Yelp

yelp 在创建全屏导航体验时采用了一种稍微不同的方法。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ Yelp将屏幕划分为两个不同的区域,实际上并没有将整个屏幕专门用于导航选项,而是将顶部用于导航选项,并在底部留下更多的负空间。

6. Trello

页面中的卡片是拉长的矩形,整齐有序地填满屏幕空间而不会让用户不知所措。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ 卡片系统很好地代表了Trello简化复杂工作流程的能力,为用户带来整洁和方便。

7. Twitter

同样使用了多种导航混合的设计样式。

你了解移动端导航设计吗?收下这篇系统性的总结!

△ Twitter将导航重点放在底部栏上,涵盖了整个平台中四个主要的方面。

最后

没有高速公路,我们很难便利地在城市间穿梭。同理,如果没有导航,一款 APP 的使用也会遇到很多麻烦。导航就像高速,不断在为用户提供必要的指引!




 蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请扫码蓝小助,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系。

截屏2021-05-13 上午11.41.03.png



文章来源:站酷   作者:Clip设计夹

分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务



SpringBoot + Shiro + Mybatis-plus + Kaptcha + vue实现权限管理登录功能

前端达人

登录功能

在这里插入图片描述

使用到的技术

  • shiro
  • Mybatis-plus
  • Springboot
  • kaptcha

参考优秀博文

一个博主做的shiro笔记:https://www.guitu18.com/post/2019/07/26/43.html

引入依赖

 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.7.0</version> </dependency> <!--mybatis-plus 持久层--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> <!--   整合swagger     --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.8.0</version> </dependency> <!-- kaptcha 验证码 --> <dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency> </dependencies> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

配置shiro

package com.unclebb.zlgl.config; import com.unclebb.zlgl.utils.CustomRealm; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.util.ThreadContext; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; /**
 * @program: zlgl
 * @description: Shiro配置类:将SecurityManager以及Realm都注入到Spring容器中
 * @author: LiuZhiliang
 * @create: 2021-05-10 08:56
 **/ @Configuration public class ShiroConfig { /** 
    * @Description: 代理生成器,需要借助SpringAOP来扫描@RequiresRoles和@RequiresPermissions等注解。生成代理类实现功能增强,从而实现权限控制。需要配合AuthorizationAttributeSourceAdvisor一起使用,否则权限注解无效。
    * @Param:
    * @return:
    * @Author: Liuzhiliang
    * @Date:
    */ @Bean public DefaultAdvisorAutoProxyCreator lifecycleBeanProcessor(){ DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); return defaultAdvisorAutoProxyCreator; } /** 
    * @Description:  上面配置的DefaultAdvisorAutoProxyCreator相当于一个切面,下面这个类就相当于切点了,两个一起才能实现注解权限控制。
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager){ AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } /**
     * @Description: Filter工厂,设置对应的过滤条件和跳转条件
     * @Param:
     * @return:
     * @Author: Liuzhiliang
     * @Date:
     */ //权限管理,配置主要是Realm的管理认证 @Bean public DefaultWebSecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myShiroRealm()); ThreadContext.bind(securityManager); return securityManager; } //将自己的验证方式加入容器 @Bean public CustomRealm myShiroRealm() { HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); //加密 matcher.setHashAlgorithmName("md5"); matcher.setHashIterations(1); CustomRealm customRealm = new CustomRealm(); customRealm.setCredentialsMatcher(matcher); return customRealm; } @Bean public ShiroFilterFactoryBean shiroFilter(DefaultSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); //        Map<String,String> maps = new HashMap<>(); //        maps.put("/logout","logout"); //        maps.put("/**","authc"); //        shiroFilterFactoryBean.setLoginUrl("/login"); //        shiroFilterFactoryBean.setUnauthorizedUrl("/403.html"); //        shiroFilterFactoryBean.setSuccessUrl("/index"); //        shiroFilterFactoryBean.setFilterChainDefinitionMap(maps); return shiroFilterFactoryBean; } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

配置swagger

package com.unclebb.zlgl.config; import com.google.common.base.Predicates; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; /**
 * @Author unclebb
 * @Description Swagger配置类
 * @Date 2021/5/9
 **/ @Configuration @EnableSwagger2 public class Swagger2Config { @Bean public Docket webApiConfig(){ return new Docket(DocumentationType.SWAGGER_2) .groupName("webApi") .apiInfo(webApiInfo()) .select() //过滤掉admin路径下的所有页面 .paths(Predicates.and(PathSelectors.regex("/user/.*"))) //过滤掉所有error或error.*页面 //.paths(Predicates.not(PathSelectors.regex("/error.*"))) .build(); } private ApiInfo webApiInfo(){ return new ApiInfoBuilder() .title("网站-API文档") .description("本文档描述了网站微服务接口定义") .version("1.0") .contact(new Contact("qy", "http://atguigu.com", "55317332@qq.com")) .build(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

配置kaptcha

package com.unclebb.zlgl.config; import com.google.code.kaptcha.impl.DefaultKaptcha; import com.google.code.kaptcha.util.Config; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import java.util.Properties; /**
 * @program: zlgl
 * @description: Kaptcha配置类
 * @author: LiuZhiliang
 * @create: 2021-05-12 09:03
 **/ @Component public class KaptchaConfig { @Bean public DefaultKaptcha getKaptcha(){ DefaultKaptcha dk = new DefaultKaptcha(); Properties properties = new Properties(); // 图片边框 properties.setProperty("kaptcha.border", "yes"); // 边框颜色 properties.setProperty("kaptcha.border.color", "105,179,90"); // 字体颜色 properties.setProperty("kaptcha.textproducer.font.color", "red"); // 图片宽 properties.setProperty("kaptcha.image.width", "110"); // 图片高 properties.setProperty("kaptcha.image.height", "40"); // 字体大小 properties.setProperty("kaptcha.textproducer.font.size", "30"); // session key properties.setProperty("kaptcha.session.key", "code"); // 验证码长度 properties.setProperty("kaptcha.textproducer.char.length", "4"); // 字体 properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑"); Config config = new Config(properties); dk.setConfig(config); return dk; } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

pojo

User

package com.unclebb.zlgl.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.util.Set; /**
 * @Author unclebb
 * @Description 用户实体类
 * @Date 2021/5/9
 **/ @Data @TableName(value = "user") public class User { @TableId(value = "id",type = IdType.AUTO)//指定自增策略 private int id; @TableField(value = "username") private String username; @TableField(value = "password") private String password; @TableField(exist = false) private Set<Role> rolesSet; private String salt; } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

Role

package com.unclebb.zlgl.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import java.util.Set; /**
 * @Author unclebb
 * @Description 角色实体类
 * @Date 2021/5/9
 **/ @Data public class Role { @TableId(value = "id",type = IdType.AUTO)//指定自增策略 private int id; @TableField(value = "user_name") private String userName; @TableField(value = "role_name") private String roleName; @TableField(exist = false) private Set<Permission> permissionSet; } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

Permission

package com.unclebb.zlgl.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; /**
 * @Author unclebb
 * @Description 权限实体类
 * @Date 2021/5/9
 **/ @Data public class Permission { @TableId(value = "id",type = IdType.AUTO)//指定自增策略 private int id; @TableField(value = "role_name") private String roleName; @TableField(value = "permission_name") private String permissionName; } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

Mapper

基本格式:

package com.unclebb.zlgl.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.unclebb.zlgl.pojo.User; import org.apache.ibatis.annotations.Mapper; /**
 * @Author unclebb
 * @Description 用户映射
 * @Date 2021/5/9
 **/ @Mapper public interface UserMapper extends BaseMapper<User> { } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Service

Interface

LoginService

package com.unclebb.zlgl.service; import com.baomidou.mybatisplus.extension.service.IService; import com.unclebb.zlgl.pojo.User; import org.springframework.stereotype.Service; /**
 * @Author unclebb
 * @Description 用户登录接口
 * @Date 2021/5/9
 **/ @Service public interface LoginService extends IService<User> { public User getByUsername(String username); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

RoleService

package com.unclebb.zlgl.service; import com.baomidou.mybatisplus.extension.service.IService; import com.unclebb.zlgl.pojo.Role; import org.springframework.stereotype.Service; import java.util.List; @Service public interface RoleService extends IService<Role> { public List<Role> getRole(String userName); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

PermissionService

package com.unclebb.zlgl.service; import com.baomidou.mybatisplus.extension.service.IService; import com.unclebb.zlgl.pojo.Permission; import org.springframework.stereotype.Service; import java.util.List; @Service public interface PermissionService extends IService<Permission> { public List<Permission> getPermissions(String roleName); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

ServiceImpl

LoginServiceImpl

package com.unclebb.zlgl.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.unclebb.zlgl.mapper.UserMapper; import com.unclebb.zlgl.pojo.Role; import com.unclebb.zlgl.pojo.User; import com.unclebb.zlgl.service.LoginService; import com.unclebb.zlgl.service.PermissionService; import com.unclebb.zlgl.service.RoleService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.HashSet; import java.util.List; import java.util.Set; /**
 * @Author unclebb
 * @Description 登录实现类
 * @Date 2021/5/9
 **/ @Service public class LoginServiceImpl extends ServiceImpl<UserMapper, User> implements LoginService { @Autowired RoleService roleService; @Override public User getByUsername(String username) { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("username",username); User user = this.getOne(wrapper); List<Role> roleList = roleService.getRole(user.getUsername()); user.setRolesSet(new HashSet<Role>(roleList)); return user; } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

RoleServiceImpl

package com.unclebb.zlgl.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.unclebb.zlgl.mapper.RoleMapper; import com.unclebb.zlgl.pojo.Permission; import com.unclebb.zlgl.pojo.Role; import com.unclebb.zlgl.service.PermissionService; import com.unclebb.zlgl.service.RoleService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.HashSet; import java.util.List; import java.util.Set; /**
 * @program: zlgl
 * @description: Role实现类
 * @author: LiuZhiliang
 * @create: 2021-05-10 16:16
 **/ @Service public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService { @Autowired PermissionService permissionService; @Override public List<Role> getRole(String userName) { QueryWrapper wrapper = new QueryWrapper(); wrapper.eq("user_name",userName); List<Role> roleList = this.list(wrapper); for (Role role:roleList){ List<Permission> permissions = permissionService.getPermissions(role.getRoleName()); role.setPermissionSet(new HashSet<Permission>(permissions)); } return roleList; } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

PermissionServiceImpl

package com.unclebb.zlgl.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.unclebb.zlgl.mapper.PermissionMapper; import com.unclebb.zlgl.pojo.Permission; import com.unclebb.zlgl.pojo.User; import com.unclebb.zlgl.service.PermissionService; import org.springframework.stereotype.Service; import java.util.List; import java.util.Set; /**
 * @program: zlgl
 * @description: Permission实现类
 * @author: LiuZhiliang
 * @create: 2021-05-10 16:18
 **/ @Service public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements PermissionService { @Override public List<Permission> getPermissions(String roleName) { QueryWrapper<Permission> wrapper = new QueryWrapper<>(); wrapper.eq("role_name",roleName); return this.list(wrapper); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

Controller

package com.unclebb.zlgl.controller; import com.unclebb.zlgl.pojo.Role; import com.unclebb.zlgl.pojo.User; import com.unclebb.zlgl.service.RoleService; import com.unclebb.zlgl.utils.Result; import io.swagger.annotations.ApiOperation; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.session.Session; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpSession; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; /**
 * @Author unclebb
 * @Description 登录控制器
 * @Date 2021/5/9
 **/ @RestController public class LoginController { @Autowired RoleService roleService; /** 
    * @Description: 登录接口 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ @ApiOperation(value = "登录接口") @RequestMapping("/user/login") public Result login(@RequestBody User user){ System.out.println("进入/user/login API接口"); if (StringUtils.isEmpty(user.getUsername())||StringUtils.isEmpty(user.getPassword())){ return Result.fail("请输入用户名密码"); } Subject subject = SecurityUtils.getSubject(); Session session = subject.getSession(); Object sessionId = session.getId(); Map ret = new HashMap<String,Object>(); ret.put("token",sessionId); UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword()); try { //登录成功 subject.login(token); return Result.ok(ret); } catch (AuthenticationException e) { e.printStackTrace(); //登录失败 return Result.fail("用户名密码错误"); } } /** 
    * @Description: 基本测试接口 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ @RequestMapping("/user/noauthority") public Result noauthority(){ return Result.fail("没有权限"); } /** 
    * @Description: 测试session接口 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ @RequestMapping("/user/demoSession") @ResponseBody public String demoSession(HttpSession session){ System.out.println("测试session"); Enumeration<String> names = session.getAttributeNames(); while (names.hasMoreElements()){ String name = names.nextElement(); Object value = session.getAttribute(name); System.out.println(name + " -------  "+ value); } return "session 取值"; } /** 
    * @Description: 测试session接口 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ @RequestMapping("/user/demoSession2") @ResponseBody public String demoSession2(){ Subject subject = SecurityUtils.getSubject(); Session session = subject.getSession(); System.out.println(session.getHost()); System.out.println(session.getId()); System.out.println(session.getLastAccessTime().getTime()); System.out.println(session.getTimeout()); System.out.println(session.getAttribute("test")); return "session 取值"; } /** 
    * @Description: 测试session接口 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ @RequestMapping("/user/checkPermission") @ResponseBody public Result checkPermission(User user){ if (StringUtils.isEmpty(user.getUsername())||StringUtils.isEmpty(user.getPassword())){ return Result.fail("请输入用户名密码"); } Subject subject = SecurityUtils.getSubject(); Session session = subject.getSession(); session.setAttribute("test","test"); UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword()); token.setRememberMe(true); try { //登录成功 subject.login(token); } catch (AuthenticationException e) { e.printStackTrace(); //登录失败 return Result.fail("用户名密码错误"); } try { subject.checkRole("admin"); return Result.ok("权限检查成功"); } catch (AuthorizationException e) { e.printStackTrace(); return Result.fail("检查权限失败"); } } /** 
    * @Description: 根据token获取用户授权信息接口 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ @RequestMapping("/user/checkRole") public Result checkRole(@RequestParam String token){ Subject subject = SecurityUtils.getSubject(); Session session = subject.getSession(); List<Role> roleList = null; if (token.equals(session.getId().toString())){ String username = session.getAttribute("org.apache.shiro.subject.support.DefaultSubjectContext_PRINCIPALS_SESSION_KEY").toString(); System.out.println(username); roleList = roleService.getRole(username); } return Result.ok(roleList); } /** 
    * @Description: 测试kaptcha ,获取验证码
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ @RequestMapping("/user/testKaptcha") @ResponseBody public String TestKaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException { byte[] captcha = null; ByteArrayOutputStream out = new ByteArrayOutputStream(); try { // 将生成的验证码保存在session中 String createText = defaultKaptcha.createText(); request.getSession().setAttribute("rightCode", createText); BufferedImage bi = defaultKaptcha.createImage(createText); ImageIO.write(bi, "jpg", out); } catch (Exception e) { response.sendError(HttpServletResponse.SC_NOT_FOUND); } captcha = out.toByteArray(); response.setHeader("Cache-Control", "no-store"); response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); response.setContentType("image/jpeg"); ServletOutputStream sout = response.getOutputStream(); sout.write(captcha); sout.flush(); sout.close(); return "测试Kaptcha"; } /**
     * 校对验证码
     *
     * @param request
     * @param response
     * @return
     */ @RequestMapping(value = "/user/verifyKaptcha") public Result imgvrifyControllerDefaultKaptcha(HttpServletRequest request, HttpServletResponse response) { ModelAndView model = new ModelAndView(); String rightCode = (String) request.getSession().getAttribute("rightCode"); String tryCode = request.getParameter("tryCode"); System.out.println("rightCode:" + rightCode + " ———— tryCode:" + tryCode); if (!rightCode.equals(tryCode)) { model.addObject("info", "验证码错误,请再输一次!"); model.setViewName("login"); return Result.fail("验证码错误"); } else { model.addObject("info", "登陆成功"); model.setViewName("index"); return Result.ok("验证成功"); } } /** 
    * @Description: 测试 校验权限 permission 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ @RequestMapping("/user/testShiroPermission") @RequiresPermissions("user:add") public Result TestShiroPermissions(){ System.out.println("访问 TestShiroPermissions API"); Subject subject = SecurityUtils.getSubject(); String username = (String) subject.getPrincipal(); System.out.println(username); return Result.ok(username); } /** 
    * @Description: 登出功能API
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ @RequestMapping("/user/logout") public Result logout(){ Subject subject = SecurityUtils.getSubject(); String username = (String) subject.getPrincipal(); subject.logout(); return Result.ok(username); } /** 
    * @Description:  测试校验 角色 role
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ @RequestMapping("/user/TestRole") @RequiresRoles("admin") public Result TestRole(){ System.out.println("测试TestRole"); return Result.ok("测试Role"); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279

util

Result

package com.unclebb.zlgl.utils; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /**
 * @Author unclebb
 * @Description 统一返回API格式
 * @Date 2021/5/9
 **/ @Data @ApiModel(value = "全局统一返回结果") public class Result<T> { @ApiModelProperty(value = "返回码") private Integer code; @ApiModelProperty(value = "返回消息") private String message; @ApiModelProperty(value = "返回数据") private T data; public Result(){} public static <T> Result<T> build(T data) { Result<T> result = new Result<T>(); if (data != null) result.setData(data); return result; } public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) { Result<T> result = build(body); result.setCode(resultCodeEnum.getCode()); result.setMessage(resultCodeEnum.getMessage()); return result; } public static<T> Result<T> ok(){ return Result.ok(null); } /**
     * 操作成功
     * @param data
     * @param <T>
     * @return
     */ public static<T> Result<T> ok(T data){ Result<T> result = build(data); return build(data, ResultCodeEnum.SUCCESS); } public static<T> Result<T> fail(){ return Result.fail(null); } /**
     * 操作失败
     * @param data
     * @param <T>
     * @return
     */ public static<T> Result<T> fail(T data){ Result<T> result = build(data); return build(data, ResultCodeEnum.FAIL); } public Result<T> message(String msg){ this.setMessage(msg); return this; } public Result<T> code(Integer code){ this.setCode(code); return this; } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

ResultCode

package com.unclebb.zlgl.utils; import lombok.Getter; /**
 * @Author unclebb
 * @Description API状态信息
 * @Date 2021/5/9
 **/ @Getter public enum ResultCodeEnum { SUCCESS(200,"成功"), FAIL(201, "失败"), SERVICE_ERROR(202, "服务异常"), DATA_ERROR(204, "数据异常"), SIGN_ERROR(300, "签名错误"), PAY_PASSWORD_ERROR(401, "支付密码错误"), REPEAT_ERROR(402, "重复提交"), INVEST_AMMOUNT_MORE_ERROR(501, "出借金额已经多余标的金额"), RETURN_AMMOUNT_MORE_ERROR(502, "还款金额不正确"), PROJECT_AMMOUNT_ERROR(503, "标的金额不一致") ; private Integer code; private String message; private ResultCodeEnum(Integer code, String message) { this.code = code; this.message = message; } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

CustomRealm

package com.unclebb.zlgl.utils; import com.unclebb.zlgl.pojo.Permission; import com.unclebb.zlgl.pojo.Role; import com.unclebb.zlgl.pojo.User; import com.unclebb.zlgl.service.LoginService; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; /**
 * @program: zlgl
 * @description: 自定义Realm
 * @author: LiuZhiliang
 * @create: 2021-05-10 09:09
 **/ public class CustomRealm extends AuthorizingRealm { @Autowired LoginService loginService; /** 
    * @Description: 授权配置
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = (String) principals.getPrimaryPrincipal(); User user = loginService.getByUsername(username); if (user == null){ return null; }else { SimpleAuthorizationInfo  simpleAuthorizationInfo = new SimpleAuthorizationInfo(); for (Role role : user.getRolesSet()){ simpleAuthorizationInfo.addRole(role.getRoleName()); for (Permission permission : role.getPermissionSet()){ simpleAuthorizationInfo.addStringPermission(permission.getPermissionName()); } } return simpleAuthorizationInfo; } } /** 
    * @Description: 认证配置 
    * @Param:  
    * @return:  
    * @Author: Liuzhiliang
    * @Date:  
    */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = token.getPrincipal().toString(); User user = loginService.getByUsername(username); if (user == null){ return null; }else { //匹配密码 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,user.getPassword(),getName()); simpleAuthenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(user.getSalt())); return simpleAuthenticationInfo; } } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

前端代码

前端我这里直接用了 开源的管理系统框架
附地址:

https://github.com/PanJiaChen/vue-admin-template

运行截图如下
在这里插入图片描述
在这里插入图片描述

完事儿只需要改一下它的 返回状态码校验,配置下跨域就可以了
在这里插入图片描述

整合验证码

验证码部分包含两个API接口

/user/testKaptcha 获取验证码信息
/user/verifyKaptcha 校验

其中获取验证码图片信息相当于请求静态图片资源,直接将验证码图片的src指向 该接口即可,前端源码如下:

 <el-image :src="kaptcha" @click="refreshCode()" alt="加载失败" style="margin-left:10px;height:40px;margin-top:5px"> <div slot="placeholder" class="image-slot"> <i class="el-icon-loading"></i> </div> </el-image> 
  • 1
  • 2
  • 3
  • 4
  • 5

其中路径定义为:

kaptcha:"http://localhost:8082/user/testKaptcha?t="+ new Date().getTime(), 
  • 1

后面加的时间参数是为了刷新url用的
前端的刷新函数就是将kaptcha重新赋值

 refreshCode(){ console.log("测试切换验证码") this.kaptcha = "http://localhost:8082/user/testKaptcha?t="+ new Date().getTime() console.log(this.kaptcha) }, 
  • 1
  • 2
  • 3
  • 4
  • 5

登录、权限校验、登出效果如下

登录

登录首先会验证 验证码的正确性,登陆成功进入主界面

验证码错误如下:
在这里插入图片描述
用户名密码错误如下:
在这里插入图片描述
登陆成功后请求校验角色 校验成功
在这里插入图片描述请求校验权限 校验成功
在这里插入图片描述

退出,执行两次,data为null
在这里插入图片描述

再次校验权限,报出异常

在这里插入图片描述


 蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请扫码蓝小助,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系。

截屏2021-05-13 上午11.41.03.png


文章来源:csdn   作者:黑胡子大叔的小屋

分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

SpringBoot + Spring Cloud +Vue 管理系统前端搭建(二、visual studio code开发前端项目)

前端达人

我们打开visual studio code , 选择文件------------->将文件夹添加到工作区,导入我们的项目

 

安装Element

导入后,我们安装以下element

官网:https://element.eleme.cn/#/zh-CN/component/installation

安装命令:npm add element-ui或者也可以用yarn

安装完成后,我们在main.js中引入Element

import Vue from 'vue'

import App from './App'

import router from './router'

import ElementUI from 'element-ui'

import 'element-ui/lib/theme-chalk/index.css'

Vue.config.productionTip = false

 

/* eslint-disable no-new */

Vue.use(ElementUI)

new Vue({

el: '#app',

router,

components: { App },

template: '<App/>'

})

页面路由

 我们把components改名为views,并在目录下添加3个页面:Login.vue、Home.vue、404.vue。

页面内容类似:

<template>

<div class="page">

<h2>Login Page</h2>

</div>

</template>

 

<script>

export default {

name: 'Login'

}

</script>

配置路由

打开router/index.js,添加3个路由分别对应主页、登录、404页面

import Vue from 'vue'

import Router from 'vue-router'

import Login from '@/views/Login'

import Home from '@/views/Home'

import NotFound from '@/views/404'

 

Vue.use(Router)

 

export default new Router({

routes: [

{

path: '/',

name: 'Home',

component: Home

}, {

path: '/login',

name: 'Login',

component: Login

}, {

path: '/404',

name: 'notFound',

component: NotFound

}

]

})

配置完后启动项目,在浏览器访问测试

http://localhost:8080/#/

http://localhost:8080/#/login

 

http://localhost:8080/#/404

说明我们的配置已经生效了

安装scss

安装依赖:

npm uninstall sass-loader //卸载当前版本) 
npm install sass-loader@7.3.1 --save-dev //卸了重新安装了一个低版本
npm install node-sass@4.14.1 --save-dev //安装node-sass 

安装的时候注意对应版本,版本不对应,启动会报错

安装后修改404页面

<template>

<div class="site-wrapper site-page--not-found">

<div class="site-content__wrapper">

<div class="site-content">

<h2 class="not-found-title">404</h2>

<p class="not-found-desc">抱歉!您访问的页面<em>失联</em>啦 ...</p>

<el-button @click="$router.go(-1)">返回上一页</el-button>

<el-button type="primary" class="not-found-btn-gohome" @click="$router.push('/')">进入首页</el-button>

</div>

</div>

</div>

</template>

 

<script>

export default {

name: '404'

}

</script>

 

<style lang="scss">

.site-wrapper.site-page--not-found {

position: absolute;

top: 60px;

right: 0;

bottom: 0;

left: 0;

overflow: hidden;

.site-content__wrapper {

padding: 0;

margin: 0;

background-color: #fff;

}

.site-content {

position: fixed;

top: 15%;

left: 50%;

z-index: 2;

padding: 30px;

text-align: center;

transform: translate(-50%, 0);

}

.not-found-title {

margin: 20px 0 15px;

font-size: 8em;

font-weight: 500;

color: rgb(55, 71, 79);

}

.not-found-desc {

margin: 0 0 30px;

font-size: 26px;

text-transform: uppercase;

color: rgb(118, 131, 143);

> em {

font-style: normal;

color: #ee8145;

}

}

.not-found-btn-gohome {

margin-left: 30px;

}

}

</style>

再浏览器访问http://localhost:8080/#/404

 

可以看到样式改变了

安装axios

命令:npm install axios

安装完成后修改Home页面,进行一个简单的测试

<template>

<div class="page">

<h2>Home Page</h2>

<el-button type="primary" @click="testAxios()">测试Axios调用</el-button>

</div>

</template>

 

<script>

import axios from 'axios'

import mock from '@/mock/mock.js'

export default {

name: 'Home',

methods: {

testAxios() {

axios.get('http://localhost:8080').then(res => { alert(res.data) })

}

}

}

</script>

可以看到我们的请求已经成功了

安装Mock.js

为了模拟后台接口提供页面需要的数据,引入mock.js

安装依赖:npm install mockjs -dev

安装完成,在src新建一个mock目录,创建mock.js,在里面模拟两个接口,分别拦截用户和菜单的请求并返回相应数据。

import Mock from 'mockjs'

 

Mock.mock('http://localhost:8080/user', {

'name': '@name', // 随机生成姓名

'name': '@email', // 随机生成邮箱

'age|1-12': 7, // 年龄1-12之间

})

Mock.mock('http://localhost:8080/menu', {

'id': '@increment', // id自增

'name': 'menu', // 名称为menu

'order|1-10': 6, // 排序1-10之间

})

修改Home.vue,在页面添加两个按钮,分别触发用户和菜单请求。成功后弹出返回结果

注意:要在页面引入mock    import mock from '@/mock/mock.js'

Home.vue

<template>
  <div class="page">
    <h2>Home Page</h2>
    <el-button type="primary" @click="testAxios()">测试Axios调用</el-button>
    <el-button type="primary" @click="getUser()">获取用户信息</el-button>
    <el-button type="primary" @click="getMenu()">获取菜单信息</el-button>
  </div>
</template>

<script>
import axios from 'axios'
import mock from '@/mock/mock.js'
export default {
  name: 'Home',
  methods: {
    testAxios() {
      axios.get('http://localhost:8080').then(res => { alert(res.data) })
    },
    getUser() {
      axios.get('http://localhost:8080/user').then(res => { alert(JSON.stringify(res.data)) })
    },
    getMenu() {
      axios.get('http://localhost:8080/menu').then(res => { alert(JSON.stringify(res.data)) })
    }
  }
}
</script>

访问http://localhost:8080/#/

点击获取用户信息

点击获取菜单信息

可以看到我们已经得到响应数据,这样mock就集成进来了

看完记得点赞哦

  蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请扫码蓝小助,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系。

截屏2021-05-13 上午11.41.03.png


文章来源:csdn   作者:Java璐到底

分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务


讲真,我从这些Dribbble顶尖图标大神的作品中学到很多,建议你一定要看看

seo达人

今天彩云就跟大家分享一些Dribbble上,我这些年一直珍藏的10位图标设计大佬们,我建议你一定要收藏学习。

 

1、Scott Tusk

https://dribbble.com/Tusk

首先推荐的就是这位大佬了,他在Drbbble上坚持了365天打卡练习,每天更新一张高质量图标或者矢量小插图设计,图形简单好看,非常值得借鉴和临摹练习。

图片

图片

图片

2、Dmitri Litvinov

https://dribbble.com/dmitrilitvinov

一个在图标设计上非常有造诣的自由设计师,他在图标的造型和风格一致性上做的非常好,而且能看出来他的作品大部分都是能落地的稿子,值得学习。

图片

图片

图片

 

3、Paulius Kairevicius

https://dribbble.com/kairevicius

这位大神在设计图标Logo的时候擅长用严谨的比例,而且他会把他的作图过程和辅助线放出来,我觉得从这些作品中可以学到很多设计的思路,推荐给大家。

图片

图片

图片

图片

 

4、Justas Galaburda

https://dribbble.com/jucha

这些大神的作品喜欢加噪点,很有自己的风格特点,图标都做的比较可爱。如果是做些偏萌系的设计,包括一些小表情之类的可以参考他的作品了。

图片

图片

图片

 

5、Martin David

https://dribbble.com/srioz

专业的图标设计师,在图标设计这个领域相当专注,看他的个人主页几乎全是图标的设计,以商业图标设计居多。做项目的时候,非常值得参考了。

图片

图片

图片

 

6、Myicons

https://dribbble.com/lineicons

这位其实是一个图标平台的账号,会定期更新大量的图标资源,把它收藏起来当成灵感库还不错。

图片

 

7、Eddie Lobanovskiy

https://dribbble.com/lobanovskiy

这位大佬(也可能是一个团队账号)非常的全面,不仅仅只是在图标领域,在3D、插画、品牌、网页、UI都顶级优秀。我个人非常喜欢他在图标这块的创意,能看到他展示了很多图形的方案,过程稿,我觉得可以学习到很多,比如用到自己的作品集展示中,就很实用了。

图片

图片

图片

图片

 

8、Eddy Gann

https://dribbble.com/Ed117

这位大佬非常擅长动态图标的设计,他做的图标动效并不夸张,但却是非常容易的落地到实际项目中。在动效创意如此流行的今天,他的这些作品一定可以给你提供灵感,专注在图标动效上的作品并不多见,值得收藏。

图片

图片

图片

 

9、Zach Roszczewski

https://dribbble.com/ZachRoszczewski

这位大佬在图标的多种风格方面把握的都比较好,想要同时看多种风格搭配的可以收藏了。

图片

图片

图片

 

10、Evgenii Dolgov

https://dribbble.com/numicor

最后推荐一位矢量风格图标设计大神,他的作品风格独特,擅长在图标上叠加很多插画形式的明暗纹理,形式感比较强,值得学习。

图片

图片

图片

 

总结

文章中列出来的这些是我从关注列表中再三筛选出来的比较有代表性的图标设计大神,在我的工作学习过程中,他们给了我很多的灵感。当然,这份推荐名单只是我自己的个人喜好,无关粉丝数量,排名也不分先后。

这篇分享,一定是值得收藏的,不论是找灵感,还是临摹学习,不用到处找,这10位大佬的作品就足够你研究了。

在看的过程中,要学会去分析优秀的图标,并用到自己的设计中。不管是练习还是实际项目,好的设计一定是有道理的,多想想别人为什么这么做。比如:为什么对方用这个颜色?颜色的配比怎样?辅助图形用的哪些?一套图标中的辅助图形如何做到丰富又不失一致性等等问题。不要只是依葫芦画瓢,要思考背后的道理,这个很重要。

最后,对于图标设计本身,最重要的还是要保持练习,多看多做才能真正提升

 

原文地址:彩云译设计(公众号) 作者:彩云Sky

转载请注明:学UI网》讲真,我从这些讲真,我从这些Dribbble顶尖图标大神的作品中学到很多,建议你一定要看看

 

蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请扫码蓝小助,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系。

微信图片_20210513163802.png

 

分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。

 

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务


怎样让UI作品集脱颖而出?试试这3种方法!

seo达人

 

优秀的设计师不仅要会设计,更要会包装。经过设计师精心包装的设计作品,总是更容易被甲方接受,经过精心包装的作品集,自然也更受招聘公司的青睐。通过对国外优秀UI设计作品的归纳,以下总结出了下面三种UI作品的展示方法,希望能对大家有些帮助。

 

01.错位平铺

平铺展示是最简单粗暴的展示方法,很容易让你的作品显得枯燥乏味。可以将页面进行简单的错位处理,让版式更灵动,画面外的部分也会给人以想象的空间,让面试官觉得整个作品丰富且庞大,是最为稳妥的展示方法。

图片

图片

图片

图片

 

02.模块分离

将部分模块分离出页面,通过阴影效果进行前后位置的划分,有助于表现你对整个页面层级的理解。UI不仅仅是简单的时间工作,多展示你对整个框架和层级的思考,会让作品集更加出彩。

图片

图片

图片

图片

 

03.组件化展示

UI设计师们对整体风格的把控能力越来越重要,这关乎你能否独立负责整个项目,所以在作品集中展示你的组件库尤为重要。组件是否能方便的复用,各个组件是否能保持风格的统一,这些都会对你面试更高级的设计师有很大帮助。

图片

图片

图片

图片

 


 蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请扫码蓝小助,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系。

截屏2021-05-13 上午11.41.03.png


文章来源:深海公众号   作者:深海

分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务




精进自己、正面思考、调整情绪的三个小方法

蓝蓝设计的小编

 西藏有一个六时书修行方法,确定要改进的一两个弱点(比如懒惰,拖延),每天分六个时辰反省自己(间隔两三个小时的样子),在每个时间段有好的,不好的念头或者行为都记下来反省总结,以便在下一个时间去调整)和鼓励自己。这个弱点改的差不多了,就继续下一个弱点作为目标去改正。读富兰克林的自传,他基本上也是用这种方法,但没有分每天六次这么细。

11个超实用的数据表单设计技巧,全是关键知识点!

高劲


数据表单是一种常见的平台设计样式,在看似乏味呆板的设计中,沉淀着许多优质的设计体验方法。尤其是在信息组织、信息传达、信息承载和阅读性方面,数据表单蕴含了许多设计规则和设计模式,使用户能够轻松地获取、处理信息。该篇文章介绍了固定表头、固定侧栏、自定义栏、分页器、过滤器、数据排序、多选项同时操作、简单且简约、普通的字体样式、项目链接、鼠标悬停这 11 个设计指南,为大家提供有关数据表单设计的实用性建议。

当然,在实际的数据表单设计中,还需要根据产品要求和用户目标进行相应的调整。你对数据表单设计有什么经验体会?有过哪些很棒的设计案例?欢迎交流分享~

11个超实用的数据表单设计技巧,全是关键知识点!

△ 插画来自 icon8

对于大多数 SaaS 平台,数据表单是必不可少的组件,可让用户获得相关数据和洞察,从而采取正确的决策。

作为一个前企业家,我使用过各种 SaaS 平台,例如 Mailchimp,Shopify,Klaviyo,Zendesk 等。它们提供不同的服务 —— 电子邮件、订单管理、客户服务等解决方案,其共同点是,都需要将数据反馈给用户。而数据表单则是传输大量数据最好的方法。

数据表单之所以有效,是因为它们能够有序地组织信息和数据,使用户能够轻松地扫描、比较和分析自己选择的信息。这篇文章介绍了我在设计数据表时遵循的关键设计模式。

固定表头

这一点对于超过 30 行的表单尤其重要。当用户必须向下滚动才能查看所有可用信息时,如果没有固定表头,用户将很难理解和区分多行数据 —— 其中大部分可能是随机数。一个固定的表头可以帮助他们轻松地使用数据表,避免了向上滚动查看字段含义 。

设计提示

我倾向使用 8px 网格系统进行设计,将表头尺寸保持在 16px(最小值)—— 防止你的设计看起来过于沉重和拥挤。

11个超实用的数据表单设计技巧,全是关键知识点!

△ 当用户不理解某个数据字段时,固定的表头使得用户无需重复向上滚动查看表头内容

固定侧栏

对于数据表单,需要两个组件对所有信息进行排序。一是表头,用于理解显示的数据。二是固定侧栏 ,与每一行数据相连接 ,常用于项目名称,例如活动名称、产品名称、股票名称等。

当数据表单需要水平滚动展示隐藏列时,通过固定第一列项目名称,可以获得与固定表头相同的组件优势,提升信息传达效率。

设计提示

设计固定侧栏时,请在该列的右侧添加阴影和垂直分隔线,提示用户该表支持水平滚动。

11个超实用的数据表单设计技巧,全是关键知识点!

△ 当数据表有太多列时,通过固定第一列项目名称,使表单更易理解

自定义栏

自定义栏允许用户根据自己的偏好选择表单显示内容。当涉及多个指标和数据集时,该功能可以满足不同的用户目标 。常用于自助广告平台,例如 Facebook Ad Manager、Google Ads、AdRoll 等,在这些平台上有多种营销指标,每个用户的优先级都不同。

11个超实用的数据表单设计技巧,全是关键知识点!

△ 自定义栏允许用户根据自己的喜好对数据表进行个性化设置

分页器

我从开发朋友那里学会了对表单进行分页,通过限制正在处理的信息量,减少加载时间。另一种方法是使用渐进式加载,当鼠标滚动到最后一行时,表单自动加载一组新的数据。对比后者,分页器允许用户一次跳过好几个组数据,满足用户非连续性浏览的需求。

设计提示

大多数表单每页显示行数可能超过 30 行,因此,将分页器固定在表单顶部或底部会更加友好,方便用户在页面之间切换,无需过度地快速滚动。

11个超实用的数据表单设计技巧,全是关键知识点!

△ 使用分页可以减少加载时间,因为它限制了正在处理的信息量

过滤器

过滤器组件对于筛选目标信息、屏蔽无关数据量至关重要。日期筛选是最基本的过滤器,能够根据用户指定日期来显示信息。当每列具有固定的展示字段,这意味着信息不是随机的,而是固定的选择,您还可以设置单项信息的过滤器。

最好在过滤器下拉列表中提供复选功能,允许用户选择多个变量 —— 过滤系统越灵活,用户就越容易操纵他们的信息。

11个超实用的数据表单设计技巧,全是关键知识点!

△ 过滤器组件对于减少根据用户要求显示的数据量至关重要

数据排序

排序类似于过滤,可以根据用户的需要重新排列信息,调整信息展示顺序。在大多数情况下,左列会对表单进行默认排序,用户可以单击标题对表单进行相应的排序设置。

您可以将排序添加到表头中,例如按数字或字母顺序对各个数据进行排序。但请不要滥用此功能,它对于状态或类别等特定指标,可能是多余的 —— 过滤器处理这些数据会更合理。

设计提示

尽量避免使用线型图标,选用面型图标来增加可见性。悬停状态能够传达整个区域可单击的视觉提示 。

11个超实用的数据表单设计技巧,全是关键知识点!

△ 排序类似于过滤,根据用户的需要重新排列信息

多选项同时操作

复选框允许用户选择多个项目,并对所选项目执行某种操作 。帮助用户节省时间和精力,不必重复相同的步骤。想象一下,所有的行都有相同的选框,这些选框会重复出现 —— 这会使你的表单看起来杂乱无章。

设计提示

我通常将复选框的大小保持在 24px(最小尺寸),居中布局,提高可用性。此外,高亮显示被选定的行,增强对比性 。

11个超实用的数据表单设计技巧,全是关键知识点!

△ 复选框允许用户选择多个项目并对所选项目执行操作

简单且简约

“极简主义” 这个词已经被广泛地使用,空白似乎是现在的趋势,但在这种情况下,少就是多。在设计数据表单时,重点应该放在数据本身而不是用户界面上。用户已经在与大量的数字和信息交互,复杂的界面只会增加用户的认知负荷。

设计提示

没有必要添加额外的视觉干扰,例如不必要的图标、斑马行、随机颜色等。

11个超实用的数据表单设计技巧,全是关键知识点!

△ 当你让你的 UI 设计师疯狂的时候会发生什么?

普通的字体样式

在设计中,排版是样式指南中的一个关键元素,对于品牌推广至关重要。但在设计表格时,您应该遵循上面的指示(简单和简约),不要在表格中使用任何复杂的字体样式。

设计提示

没有推荐的字体,但建议尽量避免使用衬线字体,因为它们往往会吸引人的注意力,导致额外的视觉负担。此外,避免出现大写单词,它会使你的设计看起来沉重。

11个超实用的数据表单设计技巧,全是关键知识点!

△ 衬线字体在表格上看起来很奇怪 —— 不知道你们是否看到过使用衬线字体的数据表单

项目链接

对于特定的表单,项目名称还可以充当链接,这是一种符合用户习惯的交互形式,用户很容易理解链接会将其带到何处。

设计提示

设计文本链接时,请使用不同的颜色向用户展示此链接 —— 仅在文本上加粗或设置下划线并不能提供足够的视觉提示。

11个超实用的数据表单设计技巧,全是关键知识点!

设计文本链接时,请使用不同的颜色向用户展示此链接

鼠标悬停

表单的操作通常放在最后一列。当没有太多的信息列,需要水平滚动信息时,这种模式就很适合。也可以将操作放在第一列或第二列,这样用户就不需要在滚动时跟踪这一行,但操作较多时,可能会产生认知过载,导致不必要的错误。

鼠标悬停可以保持简约的外观 —— 只有当用户将鼠标悬停在相应的行上时, 操作图标和文本才会出现。

11个超实用的数据表单设计技巧,全是关键知识点!

最后

以上内容只是原则性说明,主要为你提供一般性的建议,在实际的数据表单设计中,还需要根据具体的产品要求和用户目标进行相应的调整。


文章来源:优设网    推荐:TCC翻译情报局


蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务



QQ音视频产品,是如何做好体验设计的?

涛涛

面对国内外竞品崛起,QQ 音视频如何寻求突破,制定对用户有价值的策略,赢得数据增长和用户口碑。

现状分析

要突破用户增长停滞的困境,需从全局角度思考,对问题所处的系统进行分析与洞察,找出拓展应用场景的机会点,提供技术或价值创新的功能与服务。

1. SWOT 分析

首先,针对 QQ 音视频所处行业的竞争态势进行全面、系统的分析,包括内部优劣势,以及外部机会和威胁,以此制定相应的策略与计划。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

2. 用户洞察

关注竞争对手,更要研究用户,通过用户调研洞察用户需求,找出 QQ 音视频应持续巩固优化,以及需考虑突破的使用场景。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

3. 行业趋势

综观音视频的行业发展与科技趋势,有三个主要的发展方向:

超高清视频

5G 高带宽、低时延将开拓更多玩法和业务场景,超高清视频通话将带来更豐富的感官體驗,犹如面对面沟通。

应用场景多元化

从使用场景来看,音视频已走出会议室,从单纯的沟通交流,扩展至丰富的应用场景,广泛服务于人们的日常生活。

智能终端+音视频云服务

未来智能终端将具备联网连接的能力,搭配音视频服务提供商的云服务,使得通话无所不在,可随时通过智能终端进行音视频通话。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

愿景规划

综合前述分析,提出 QQ 音视频的用户体验愿景(UX Vision)和价值主张(Value Proposition)。

1. 用户体验愿景

QQ 音视频的本质在于沟通,其声影重现的独特优势,消弭了空间距离。未来随着通信技术的演进,高画质、低时延、实时互动的感官体验,将实现远距临场(Telepresence)的终极目标,使用户感受到近乎面对面交流的体验,就像在眼前〝一起做〞某些事。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

2. 价值主张

综上所述,提出 QQ 音视频为用户传递的价值——社交临场感(Social Presence)。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

临场感相关理论与研究,源自 Short、Williams 和 Christie 等三位学者于 1976 年所提出的社会临场感理论(Social Presence Theory),指的是双方通过传播媒介进行沟通的过程中,所能感受到对方真实存在的程度。QQ 音视频凭借高度的媒介丰富性,提供实时响应、语言/非语言线索交互,使用户能轻易地在社交互动中感知对方真实存在,进而在心理上获得归属感与认同感。

根据过往文献与媒介特性,将 QQ 音视频的社交临场感分为四个层次:

界面质量(Interface Quality)

界面可用性与视觉设计不会干扰操作,能让用户专注于音视频沟通。

真实感(Realness)

音视频的声画体验及与人交流的方式,能贴近于真实世界。

互动性(Interactivity)

用户之间的互动交流越强,越能感受到对方的存在。

情感传达(Affective Expression)

通过情绪表达、环境氛围的营造与对方建立连接,在心理上感到彼此的存在。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

策略制定

围绕社交临场感的价值主张,制定突破当前困境的目标与设计策略。音视频产品除了广泛应用在通讯场景,正逐渐赋能更多创新场景。基于 Q 群生态和调研结果,聚焦于生活、办公、娱乐等场景进行探索。

1. 设计目标

两个主要的设计目标:

重塑体验

回归通讯本质、夯实基础体验,对通话界面进行改版设计,让用户专注于音视频沟通。

场景探索

开拓应用场景,通过社交临场感赋能生活、办公、娱乐等场景需求,为用户创造价值。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

2. 设计策略

针对 QQ 音视频的应用场景,制定相应的设计策略。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

通讯本质——专注沟通更无碍

过亿人使用的QQ音视频产品,是如何做好体验设计的?

界面质量是影响社交临场感的关键要素,然而调研结果显示,QQ 音视频的通话质量和体验落后竞品,包括主界面功能复杂、通话流程痛点多、核心能力落后竞品等体验问题。

针对上述问题,提出 QQ 音视频改版的设计思路:

  • 化繁为简,回归本质
  • 链路排查,解决痛点
  • 补齐短板,超越竞品

过亿人使用的QQ音视频产品,是如何做好体验设计的?

1. 化繁为简,回归本质

分析不同通话类型的用户习惯,以及各个功能的使用数据,区分功能优先级,分为核心功能、辅助功能、高级功能等三个层次,重新定义功能架构与布局。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

  • 主界面:少就是多,收折非常用功能,确保核心通话体验。
  • 更多面板:主界面功能越少,更多功能入口越能起到引导作用,从右上角找到进阶功能。
  • 快捷手势:通过新手引导、红点引导、快捷手势,满足高级用户的需求。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

2. 链路排查,解决痛点

运用认知走查(Cognitive Walkthrough)快速排查通话过程存在的用户痛点,并将缺失的功能与信息补齐,最终解决超过 16 个以上的可用性问题。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

3. 补齐短板,超越竞品

对于视频画面的思考:

QQ 采用 1 大 N 小的画中画模式,适合 1 人主讲、他人观看的场景,主客位明显,缺少一种与他人共在的感觉;反观微信是 9 路宫格画面,视频画面仅聚集在上半部,有利于节省流量,但同时也限制了屏幕利用率。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

兼合上述两者的优点,设计了 16 人视频通话的功能:

  • 100%屏幕利用率,让内容展示更完整。
  • 力求面画均等分割,最多支持 16 路视频。
  • 手势快速切换宫格与画中画

过亿人使用的QQ音视频产品,是如何做好体验设计的?

16 人视频通话也延伸了不同的创意玩法,例如祝朋友生日快乐、拼字表白或求婚。

生活场景——与你陪伴更有感

过亿人使用的QQ音视频产品,是如何做好体验设计的?

QQ 视频通话重度用户的通话对象,主要来自恋人伴侣,这类用户最希望获得〝在一起〞的陪伴感,而社交临场感的〝真实感〞是营造音视频陪伴感的关键因素——彷佛对方就在眼前,两个人一起聊天、一起做某些事。

1. 让他 ‧ 她看到最好的你

声影的真实感,是让对方感受到你就在眼前的必要条件。真实感包括流畅度与清晰度,通过用户行为分析,发现在不同场景下,用户对视频清晰度与流畅度有不同偏好 。

为此,QQ 音视频开创了的高清 / 流畅模式,用户能根据自身需求做选择,切换自动、流畅 480P、高清 720P 三种模式,针对不同模式定制码表,匹配不同的帧率和码率。除此之外,QQ 音视频也与手机厂商合作,开发 QQ 5G 1080P 超高清视频通话,提前布局 5G 应用。

2. 记录〝在一起〞的时刻

创造情感连接,有助于拉近彼此距离,营造陪伴感。有什么是情侣、闺蜜平时会做的事,因为分隔两地不能一起做了?

循着这个思路,设计了全新的双人合拍模式。距离遥远的两人,通过 QQ 音视频实现留影合拍,让彼此的情感得以沉淀。另外,为了增添合拍时的乐趣,该模式结合了 P 图资源与人脸识别的能力,让用户可选用场景模版与挂件,创造属于两人的高光时刻。

3. 〝一起做〞某些事

情绪之间的感染,更能强化陪伴感与互动感,据此设计了第一款支持视频聊天的一起看应用 ——视频包厢。有别于Q群一起看的广播形式,视频包厢诉求更亲密、1v1的陪伴感,彷佛对方坐在身边,一边看剧、一边聊天。视频包厢的价值,除了提升QQ音视频的互动性,也为关系链活跃拓展应用场景。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

办公场景——群体沟通更高效

过亿人使用的QQ音视频产品,是如何做好体验设计的?

线上会议是办公用户主要的使用场景,一方面将聚焦用户核心需求,思考办公场景的功能设计,另一方面增强用户之间的互动性,让会议更具社交临场感,就像面对面沟通一样高效。

1. 沟通更多元

为什么需要文字聊天?当主持人开启全员静音,此时无法回应讲者;多人通话时,最多同时听到 6 个人的声音;不方便开麦或摄像头,又想参与互动。

为了解决痛点、完善沟通形式,QQ 音视频新增了文字聊天的功能,可通过手势快速展开 / 收起聊天界面,并支持发送表情和文字消息;这些消息只会在通话界面沉淀,不影响未参与会议的群成员,避免不必要的打扰。

2. 轻松会议管理

对办公用户而言,线上会议需要完善的管理机制。QQ 音视频尝试结合 Q 群管家机器人,小成本实现通话预定。用户能设置通话主题、开始时间、会议提醒,并支持分享会议链接到微信、QQ 空间等其他渠道;获取链接的用户,能快速拉起 QQ 加入会议。

3. 以我所⻅为你呈现

面对面分享是信息共享最有效率的方式,分享者能够当面指出要点,有效地表达与沟通。QQ 音视频借助系统镜像投屏,实现边通话、边共享的能力。在商务会议中,将手机中的信息与操作,实时同步给其他参会成员,例如文件、照片、邮件内容等,并支持同步画面、设备声音。另外,诸如游戏分享、作业辅导、操作协助等场景,都能通过屏幕共享满足需求,为用户带来实质性的效益。手机端最后一块拼图完成后,QQ 多端皆可实现屏幕共享,现已成为 QQ 音视频的口碑能力。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

娱乐场景——线上聚会更好玩

过亿人使用的QQ音视频产品,是如何做好体验设计的?

疫情期间,线下聚会被迫停止,线上娱乐应运而生。在此契机之下,借助音视频实时互动、声影重现的特性,开启了新的娱乐形态——QQ一起派对。

QQ 一起派对是基于音视频展开的实时社交游戏,旨在打造真实聚会游戏体验,让游戏过程更贴近线下真实场景,就像面对面一起玩聚会游戏,能与好友边玩游戏、边聊天。通过环境氛围的营造,包括场景具象化、增强代入感等方法,实时感知好友的情感变化,从而获得社交临场感,在心理上感到彼此的存在。

此外,有别于竞品需通过点击按钮作答,QQ 一起派对利用实时语音识别,让用户通过更自然的人机交互方式,在游戏过程中进行语音抢答,彷佛置身于真实世界中与好友互动。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

成果:深挖场景,用户为口碑功能点赞

QQ 音视频经过一系列的体验重塑,获得不错的成果。基于生活、办公、娱乐等场景深挖需求,新增许多实用功能和创新玩法,为用户体验和价值创新奠定良好的基础,且归功于这些积累,以致于在疫情期间,QQ 音视频能不畏竞品的挑战。除此之外,多项新增能力,例如屏幕共享、视频包厢、QQ 一起派对,获得用户广泛好评;调研显示,用户乐于将 QQ 一起派对推荐给其他人,足见该功能获得不错的用户口碑。

过亿人使用的QQ音视频产品,是如何做好体验设计的?

结语

对于音视频行业来说,疫情迎来的爆发式增长,无法保持高速成长态势,会有一定程度的回落,但能提高大众接受度和习惯养成,认识到音视频能在一定程度上替代线下沟通,不仅限于通讯功能,音视频应用拥有更多的可能性,正逐渐赋能更多创新场景。为此,QQ 音视频基于社交临场感,探索并赋能生活、办公、娱乐等应用场景,提供技术或价值创新的功能与服务,为用户创造独特的价值。

面对市场上同类竞品的崛起,作为一个 UX 设计师,除了因应产品思维设定目标,亦应思考如何以价值驱动解决问题:对于如何解决问题、迈向未来愿景,提出最适切、有效的方法。换言之,要关注的不只是当前面临的问题,更重要的是擘画用户体验愿景,提出该产品的价值主张,并制定相应的设计策略。


 蓝蓝设计建立了UI设计分享群,每天会分享国内外的一些优秀设计,如果有兴趣的话,可以进入一起成长学习,请扫码蓝小助,报下信息,蓝小助会请您入群。欢迎您加入噢~~希望得到建议咨询、商务合作,也请与我们联系。

截屏2021-05-13 上午11.41.03.png



文章来源:优设   作者:腾讯ISUX

分享此文一切功德,皆悉回向给文章原作者及众读者.
免责声明:蓝蓝设计尊重原作者,文章的版权归原作者。如涉及版权问题,请及时与我们取得联系,我们立即更正或删除。

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务


日历

链接

个人资料

蓝蓝设计的小编 http://www.lanlanwork.com

存档