登录和瀑布流

登录和瀑布流是两个非常常用的功能,本文将讨论这两个功能的具体的设计思路

登录

表单展示(单例模式)

验证登录,前端验证(策略模式)

登录成功后,各模块的显示(发布订阅模式封装,父组件向其他组件父组件或孙组件等传值)

  1. 最简单

    用户输入用户名和密码等,浏览器将其发给服务器,服务器验证成功,通知浏览器更新保存session的cookie,并返回登录成功后的页面给用户

  2. 端点登录sso

权限管理
  1. 常规权限模型

    RBAC(Role-Based Access Control)基于角色的权限访问控制

    image-20200220112421950

    image-20200220112449243

    1. 常规管理系统中的权限
    • 根据登录用户返回菜单列表— — 可能是一颗树

    • 根据登录用户控制接口权限

    1. 前台系统中的权限管理
    • 更专注于入口的控制
    • 越权的提示也不可少
    • 多级权限可能会涉及到计算
根据数组构建树

菜单列表通常是一个树的结构,后端一般喜欢给一个列表(数组),该如何去构建?

给定原始数据(originData),期待生成一个树形结构的对象(treeData)

 export type Item = {
  id: number;
  parentId: number;
  name: String;
};
export type Node = Item & { children: Node[] };
export const originData: Item[] = [
  {
    id: 0,
    parentId: null,
    name: "生物"
  },
  {
    id: 1,
    parentId: 0,
    name: "动物"
  },
  {
    id: 2,
    parentId: 0,
    name: "植物"
  },
  {
    id: 3,
    parentId: 0,
    name: "微生物"
  },
  {
    id: 4,
    parentId: 1,
    name: "哺乳动物"
  },
  {
    id: 5,
    parentId: 1,
    name: "卵生动物"
  },
  {
    id: 6,
    parentId: 2,
    name: "种子植物"
  },
  {
    id: 7,
    parentId: 2,
    name: "蕨类植物"
  },
  {
    id: 8,
    parentId: 4,
    name: "大象"
  },
  {
    id: 9,
    parentId: 4,
    name: "海豚"
  },
  {
    id: 10,
    parentId: 4,
    name: "猩猩"
  },
  {
    id: 11,
    parentId: 5,
    name: "蟒蛇"
  },
  {
    id: 12,
    parentId: 5,
    name: "麻雀"
  }
];

export const treeData: Node = {
  children: [
    {
      children: [
        {
          children: [
            { children: [], id: 8, name: "大象", parentId: 4 },
            { children: [], id: 9, name: "海豚", parentId: 4 },
            { children: [], id: 10, name: "猩猩", parentId: 4 }
          ],
          id: 4,
          name: "哺乳动物",
          parentId: 1
        },
        {
          children: [
            { children: [], id: 11, name: "蟒蛇", parentId: 5 },
            { children: [], id: 12, name: "麻雀", parentId: 5 }
          ],
          id: 5,
          name: "卵生动物",
          parentId: 1
        }
      ],
      id: 1,
      name: "动物",
      parentId: 0
    },
    {
      children: [
        { children: [], id: 6, name: "种子植物", parentId: 2 },
        { children: [], id: 7, name: "蕨类植物", parentId: 2 }
      ],
      id: 2,
      name: "植物",
      parentId: 0
    },
    { children: [], id: 3, name: "微生物", parentId: 0 }
  ],
  id: 0,
  name: "生物",
  parentId: null
};
  1. 单元测试
import arrayToTree from "./arrayToTree";
import { originData, treeData } from "./data";

test("arrayToTee", () => {
  expect(arrayToTree(originData)).toEqual(treeData);
});

  1. 实现步骤

    • 实现函数签名

    • 实现函数体

      • 处理 root

      • 实现 addChildren 方法

      • 执行 addChildren

【解题步骤】

import { Item, Node } from "./data";
// + 实现函数签名
// + 实现函数体
//   + 处理 root
//   + 实现 addChildren 方法
//   + 执行 addChildren

const arrayToTree = (arr) => {
  if (!Array.isArray(arr) || arr.length < 1) return null;
  const [root] = arr.filter(item => item.parentId === null);
  const addChildren = (node, dataList) => {
    const children = dataList
      .filter(item => item.parentId === node.id)
      .map(item => addChildren(item, dataList));
    return { ...node, children };
  };
  return addChildren(root, arr);
};

export default arrayToTree;


树的遍历

【题目分析】

把上一节树的数据转成数组?

【答题思路】

  1. 单元测试
import treeToArray from "./treeToArray";
import { originData, treeData } from "./data";

test("arrayToTee", () => {
  expect(
    treeToArray(treeData).sort((a, b) => (a.name > b.name ? 1 : -1))
  ).toEqual(originData.sort((a, b) => (a.name > b.name ? 1 : -1)));
});
  1. 大概步骤

    • 编写函数框架
    • 实现 const nodeToArray = (node: Node, result: Item[]) => {}
    • 调用 nodeToArray

【解题步骤】

import { Node, Item } from "./data";
const treeToArray = (node: Node) => {
  const nodeToArray = (node: Node, arr: Item[]) => {
    const { children, ...item } = node;
    arr.push(item);
    children.forEach(child => nodeToArray(child, arr));
    return arr;
  };
  return nodeToArray(node, []);
};

export default treeToArray;


【问题延伸】

  • 二叉树遍历 https://leetcode.com/problems/binary-tree-preorder-traversal/
  • 二叉树层序遍历 https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
  • bfs

瀑布流

图片排列

同宽同高:inline-block,flex,float,自动按内容排列

同宽不空高:position(top:列的高[ ]arrCol/index,left:index)

遍历items

  1. 第一行

  2. 其他行:找出最小高的列显示图片:遍历arrColj

<style>
    .item{
        position: absolute;
    }
</style>
<body>
    <div id="box">
        <div class="item"><img src="270*270.jpg" alt=""></div>
        <div class="item"><img src="188.png" alt=""></div>
    </div>
    <script>
        var box = document.getElementById('box')
        var items = box.children
        var gap = 10

        window.onload = function(){
            waterFall()
            function waterFall(){
                // 视口的宽度
                var pageWidth = getClient().width
                // 图片item的宽
                var itemWidth = items[0].offsetWidth
                // 列数取整
                var columns = parseInt(pageWidth/(itemWidth+gap))

                // 列的高数组
                var arr = []
                for (var i=0; i<items.length;i++){
                    // 第一行
                    if(i<columns){
                        items[i].style.top = 0
                        items[i].style.left = (itemWidth+gap)*i + 'px'
                        arr.push(items[i].offsetHeight)
                    }else{
                        // 其他行
                        // 1. 找出arr列的最小高度和最小索引

                        // 假设
                        var minHeight = arr[0]
                        var index = 0

                        // for循环找出
                        for (var j=0; j<arr.length; j++){
                            if (minHeight > arr[j]){
                                minHeight = arr[j]
                                index = j
                            }
                        }

                        // 2. 第二行的第一张图片出现的位置
                        items[i].style.top = arr[index] + gap + 'px'
                        items[i].style.left = items[index].offsetLeft + 'px'    
                        // 最小高度的图片的offsetLeft也是第二行的第一张图片的left

                        // 3. 显示出第一张图片后,改变最小列的高度
                        arr[index] = arr[index] + items[i].offsetHeight + gap
                    }
                }
            }
            
            // 获取 视口的宽度或宽度 的兼容处理
            function getClient(){
                return {
                    width: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
                    height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
                }
            }
            // 获取 滚动条的高度 的兼容处理
            function getScrollTop(){
                return window.pageYOffset || document.documentElement.scrollTop
            }
        }
    </script>
懒加载
  1. 一页里的图片不多可以都写在html里,可再结合,分页请求

    视口高+滚动条高> 图片items[i] 的顶部位置top,将data-src赋值给data

    if (items[i].offsetTop < _clientHeight + _srollTop)

见js节流(滚动限制)防抖(屏幕高度计算)函数的懒加载应用

  1. 一页里的图片太多,后端一段一段请求

    视口高+滚动条高>最后一张图片的顶部位置top时,去请求新的图片地址

    将请求到的data加入dom(createDocumentFragment一次性dom操作)

    接下来就和1一样了

    		<div id="box">
            <div class="item"><img src="270*270.jpg" alt=""></div>
            <div class="item"><img src="188.png" alt=""></div>
        </div>
        <script>
            var box = document.getElementById('box')
            var items = box.children
            var gap = 10
    
            window.onload = function(){
                waterFall()
                function waterFall(){
                    // 视口的宽度
                    var pageWidth = getClient().width
                    // 图片item的宽
                    var itemWidth = items[0].offsetWidth
                    // 列数取整
                    var columns = parseInt(pageWidth/(itemWidth+gap))
    
                    // 列的高数组
                    var arr = []
                    for (var i=0; i<items.length;i++){
                        // 第一行
                        if(i<columns){
                            items[i].style.top = 0
                            items[i].style.left = (itemWidth+gap)*i + 'px'
                            arr.push(items[i].offsetHeight)
                        }else{
                            // 其他行
                            // 1. 找出最小高度和最小索引
    
                            // 假设
                            var minHeight = arr[0]
                            var index = 0
    
                            // for循环找出
                            for (var j=0; j<arr.length; j++){
                                if (minHeight > arr[j]){
                                    minHeight = arr[j]
                                    index = j
                                }
                            }
    
                            // 第二行的第一张图片出现的位置
                            items[i].style.top = arr[index] + gap + 'px'
                            items[i].style.left = items[index].offsetLeft + 'px'    
                            // 最小高度的图片的offsetLeft也是第二行的第一张图片的left
    
                            // 显示出第一张图片后,改变最小列的高度
                            arr[index] = arr[index] + items[i].offsetHeight + gap
                        }
                    }
                }
                var _clientHeight = getClient().height
                // 懒加载(后端请求图片地址)
                var lazyload = function(){
                    // 视口高+滚动条高>最后一张图片的顶部位置top时,去请求新的图片地址
                    if (_clientHeight +getScrollTop() >= items[items.length-1].offsetTop){
                        // 请求后端,加载图片地址
                        var data = ["188.png",
                                    "270*270.jpg"
                                    ]
                        // 将data里的图片地址插入dom,此处如果从优化的角度,可以用上createDocumentFragment一次性dom操作
                        // 100条数据以内,就不用分段渲染了,分段渲染一般用于动画(loop,requestAnimationFrame)
                        const fragment = document.createDocumentFragment();
                        for (var n = 0; n<data.length;n++){
                            var div = document.createElement('div')
                            div.className = 'item'
                            div.innerHTML = '<img src="' + data[n] + '"alt="">'
                            fragment.appendChild(div);
                        }
                        box.appendChild(fragment);
    
                        waterFall()
    
                    }
                }
    
                
                // 获取视口的 宽度或宽度 的兼容处理
                function getClient(){
                    return {
                        width: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
                        height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
                    }
                }
                // 获取滚动条的高度
                function getScrollTop(){
                    return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
                }
    
                // 节流防抖的优化
                // 1)改getClient().height
                function computedClientHeight(){
                    _clientHeight = getClient().height
                }
                // 节流函数
                function throttle(callback,delay){
                    let timeout
                    return function(){
                        if (!timeout){
                            timeout = setTimeout(function(){
                                callback()
                                timeout = null    
                            // 完成功能前,别的操作都不管,等到该定时器销毁,新操作才有效
                            },delay)
                        }
                        
                    }
                }
    
                // 防抖函数
                function debounce(callback,delay){
                    let timer
                    return function(){
                        clearTimeout(timer)
                        timer = setTimeout(function(){
                            callback()
                        },delay)
                    }
                }
                
                // 加载图片需要一定的时间,滚动 屏幕限制,节流函数
                window.addEventListener('scroll',throttle(lazyload,100))
    
                // 监听可视区域高度变化,防止频繁滚动屏幕多次 计算 ,防抖函数
                window.addEventListener('resize',debounce(computedClientHeight,100))
            }
        </script>
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值