精华内容
下载资源
问答
  • 因此本文力求以最简单易懂的项目结构和代码搭建一个还较为完整(即从登录到退出的整个流程)的后台系统。(适合新手) 整个项目的操作流程动态图如下(由于CSDN对上传图片大小有限制,这里录制的gif画面很不清晰,...

    前言

    相信很多小伙伴在学习完SSM三大架构以后,不知道该如何找到一个简单容易上手的项目进行实战训练,经常在博客上看到一个不错的项目下载下来以后全部都是代码,无处下手。因此本文力求以最简单易懂的项目结构和代码搭建一个还较为完整(即从登录到退出的整个流程)的后台系统。(适合新手)

    整个项目的操作流程动态图如下(由于CSDN对上传图片大小有限制,这里录制的gif画面很不清晰,点击图片查看更清晰):
    这里写图片描述
    (点击图片查看)

    已经将服务部署到服务器,可以在线进行体验: 线上体验链接

    项目的整体结构:
    这里写图片描述
    这里写图片描述

    用到的技术栈有:

    • 框架:SSM
    • 数据库:MySQL
    • 前端框架:Bootstrap快速搭 搭建JSP页面
    • 项目管理:MAVEN
    • 开发工具:Intellij IDEA
    • 开发环境:Windows

    从这个项目中你可以完整独立地体验从前端到后台的搭建过程,以及使用SSM框架完成后台的CRUD整个流程。
    注:项目代码下载链接:SSM_HRMS代码下载链接 .

    一、准备

    准备部分主要包括数据库建表、SSM框架的搭建启动。
    1 数据库建表
    tbl_emp表:

    DROP TABLE IF EXISTS `tbl_emp`;
    CREATE TABLE `tbl_emp`(
    	`emp_id` int(11) UNSIGNED NOT NULL auto_increment,
    	`emp_name` VARCHAR(22) NOT NULL DEFAULT '',
      `emp_email` VARCHAR(256) NOT NULL DEFAULT '',
      `gender` CHAR(2) NOT NULL DEFAULT '',
       `department_id` int(11) NOT NULL DEFAULT 0,
    	 PRIMARY KEY(`emp_id`)
    ) ENGINE=INNODB DEFAULT CHARSET=UTF8;
    

    tbl_dept表:

    DROP TABLE IF EXISTS `tbl_dept`;
    CREATE TABLE `tbl_dept`(
    	`dept_id` int(11) UNSIGNED NOT NULL auto_increment,
      `dept_name` VARCHAR(255) NOT NULL DEFAULT '',
    	`dept_leader` VARCHAR(255) NOT NULL DEFAULT '',
    	PRIMARY KEY(`dept_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

    对应的实体类见bean/Employee.java和bean/Department.java。
    (在测试部分,运行EmployeeMapperTest.java 和 DepartmentMapperTest.java,可以将数据插入到表中)

    2 SSM项目搭建与启动
    (1)本次项目使用的是MAVEN进行JAR包的管理,首先导入项目中可能用到的依赖包:
    见pom.xml.

    (2)Web项目的配置文件:web.xml:
    见WEB-INF/web.xml.

    (3)Spring容器配置文件:applicationContext.xml:
    见resources/applicationContext.xml.

    (4)SpringMVC配置文件:springmvc.xml:
    见resources/springmvc.xml.

    3 测试
    写好上述配置文件后,可以在controller目录下新建TestController.java文件和WEB-INF/jsp/test.jsp,启动容器测试是否成功。

    二、DAO层代码完成与测试

    这一章主要完成数据库底层的CRUD代码实现与测试工作。
    1. MyBasits配置文件
    见resources/MyBatis.xml.

    2. DAO层代码
    首先编写实体类Employee 与 表tbl_emp相关操作代码。
    EmployeeMapper.java主要接口有:

    int deleteOneById(@Param("empId") Integer empId);
    int updateOneById(@Param("empId") Integer empId,
                       @Param("employee") Employee employee);
    int insertOne(Employee employee);           
     Employee selectOneById(@Param("empId") Integer empId);
    Employee selectOneByName(@Param("empName") String empName);
    // 查询带有部门信息的Employee
    Employee selectWithDeptById(@Param("empId") Integer empId);
    // 分页查询
    List<Employee> selectByLimitAndOffset(@Param("limit") Integer limit,@Param("offset") Integer offset);
    int countEmps();
    

    具体实现参考EmployeeMapper.java与EmployeeMapper.xml中代码。

    写完后需要对实现的代码进行测试,以验证代码的正确性。
    测试用例代码见EmployeeMapperTest.java。

    类似地,
    实体类Department与 表tbl_dept相关操作代码实现也如上类似,具体实现见DepartmentMapper.java 与 DepartmentMapper.xml,测试用例代码见DepartmentMapperTest.java。

    三、前端页面的搭建

    前端页面实现的最终效果如下。
    主页面:
    Alt text

    员工操作页面(部门操作页面类似):
    这里写图片描述
    (点击图片查看)

    最后加上一个登陆页面(比较简单的页面加上最简单的登录判断):
    这里写图片描述
    (点击图片查看)

    主要就是采用Bootstrap3去搭建这个前端页面,然后再利用SSM框架+JSP完成从前端到后端的整个流程。
    下面首先Bootstrap3去搭建前端页面。

    1 主页面的静态搭建

    主页面的HTML代码实现放在webapp/static/html/hrms_main.html,(此处仅仅为了方便查看和参考)。
    整个主页面完成后,分别将其中公共部分的代码提取出来,如导航栏,左侧栏,尾部这3个部分都属于公共部分,
    分别见hrms_head.html、hrms_foot.html、hrms_leftsidebar.html三个部分。

    2 公共页面的JSP实现及分层

    下面将上述公共部分的HTML代码用JSP实现,详细见WEB-INF/jsp/commom目录下的head.jsp、foot.jsp、leftsidebar.jsp。

    然后实现主页面的内容,主要包括三个公共部分(导航栏+左侧栏+尾部+轮播部分),实现效果如下:
    这里写图片描述
    (点击图片查看)

    新建main.jsp,将上述三个公共部分的代码用
    <<%@ include file="./commom/xx.jsp"%>>引入,再用Bootstrap实现轮播图部分即可,具体实现见main.jsp中代码。

    3 员工操作/部门操作的静态页面实现

    员工操作页面与主页面3个公共部分相同,不同之处在于中间部分展示的是员工信息的表格显示,而主页面是一个轮播图。
    这里写图片描述
    (点击图片查看)

    下面就将实现employeePage的页面,详细代码见employeePage.jsp(即将main.jsp中的轮播部分换成员工表单显示部分即可)。

    (为了方便对比与查看,将实现的HTML部分代码留在了项目目录中,
    实现的HTML代码见WEB-INF/static/html/hrms_employee.html。
    相应的部门显示的页面类似,
    实现的HTML代码见WEB-INF/static/html/hrms_department.html。
    然后将上述代码分别用JSP页面实现,即对应的employeePage.jsp和departmentPage.jsp。)

    四、员工CRUD操作前后端实现

    1 员工信息查询的数据显示

    页面搭建完成以后,就要将从后台获取到的数据展示在对应的页面中。页面数据展示部分主要实现是利用JSP的JSTL表达式和AJAX+jQuery,将从后台获取到的数据显示在页面对应的位置。

    由于部门操作与员工操作类似,下面主要讲解员工显示页面的实现。
    整个流程是从数据库中查询到数据后,放在SpringMVC的ModelAndView中,然后前端通过JSTL就可以解析获取到的结果集。
    (1)首先写一个JSON相关的操作类:JsonMsg.java。
    (2)业务操作:EmployeeService.java;
    (3)Controller类:EmployeeController.java;
    EmployeeController.java中接口"“emp/getEmpList?pageNo=XXX”"是根据输入的页码返回对应页数的数据,然后用JSTL表达式进行解析显示。

        @RequestMapping(value = "/getEmpList", method = RequestMethod.GET)
        public ModelAndView getEmp(@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo){
            ModelAndView mv = new ModelAndView("");
            int limit = 5;
            // 记录的偏移量(即从第offset行记录开始查询),
            // 如第1页是从第1行(offset=(21-1)*5=0,offset+1=0+1=1)开始查询;
            // 第2页从第6行(offset=(2-1)*5=5,offset+1=5+1=6)记录开始查询
            int offset = (pageNo-1)*limit;
            //获取指定页数包含的员工信息
            List<Employee> employees = employeeService.getEmpList(offset, limit);
            //获取总的记录数
            int totalItems = employeeService.getEmpCount();
            //获取总的页数
            int temp = totalItems / limit;
            int totalPages = (totalItems % limit == 0) ? temp : temp+1;
            //当前页数
            int curPage = pageNo;
    
            //将上述查询结果放到Model中,在JSP页面中可以进行展示
            mv.addObject("employees", employees)
                    .addObject("totalItems", totalItems)
                    .addObject("totalPages", totalPages)
                    .addObject("curPage", curPage);
            return mv;
        }
    
    

    然后在employeePage.jsp页面上将后端的Modal中数据取出来进行显示。
    主要代码有:

    <tbody>
    	<c:forEach items="${employees}" var="emp">
    		 <tr>
    		     <td>${emp.empId}</td>
    		     <td>${emp.empName}</td>
    		     <td>${emp.empEmail}</td>
    		     <td>${emp.gender == "M"? "女": "男"}</td>
    		     <td>${emp.department.deptName}</td>
    		     <td>
    		         <a href="#" role="button" class="btn btn-primary">编辑</a>
    		         <a href="#" role="button" class="btn btn-danger">删除</a>
    		      </td>
    		</tr>
    	</c:forEach>
    </tbody>
    

    <div class="table_items">
    	当前第<span class="badge">${curPage}</span>页,共有<span class="badge">${totalPage}</span>页,总记录数<span class="badge">${totalItems}</span>条。
    </div>
    

    (4)分页栏的代码实现。
    分页栏完成效果如下:
    这里写图片描述
    (点击图片查看)

    关于分页栏需要完成的需求有:

    • 当前页需要激活(主要是页面上的1,2,3,4,5页);
    • 在首页(第1页)的时候,<< 禁止点击;
    • 在末页(最后一页)的时候,>>禁止点击;
    • 默认显示首页数据;
    • 首页,上一页,末页,下一页添加事件,显示对应页码数据
    • 中间页每一页,为其添加点击事件,并跳转到对应页面;
    • 左边信息栏中当前第X页需要根据点击的页数同步显示。

    主要的代码实现都是在前端使用jQuery+JSTL实现的。代码如下:

    <div class="panel-body">
    此处代码略
    </div>
    

    以及对应的jQuery实现上一页、下一页的操作:

    $(function () {
            //上一页
            var curPage = ${curPage};
            var totalPages = ${totalPages};
            $(".prePage").click(function () {
                if (curPage > 1){
                    var pageNo = curPage-1;
                    $(this).attr("href", "/emp/getEmpList?pageNo="+pageNo);
                }
            });
            //下一页
            $(".nextPage").click(function () {
                if (curPage < totalPages){
                    var pageNo = curPage+1;
                    $(this).attr("href", "/emp/getEmpList?pageNo="+pageNo);
                }
            });
        })
    

    最后在主页面中的员工信息加上一个指定链接,跳转到当前员工信息显示的页面(部门操作类似,不再赘述),以及点击公司LOGO的时候跳转到主页面。
    代码如下:
    head.jsp:

    <script type="text/javascript">
        //跳转到主页面
        $("#company_logo").click(function () {
            $(this).attr("href", "/hrms/main");
        });
    

    leftsidebar.jsp:

    <script type="text/javascript">
        //跳转到员工页面
        $(".emp_info").click(function () {
            $(this).attr("href", "/hrms/emp/getEmpList");
        });
        //跳转到部门页面
        $(".dept_info").click(function () {
            $(this).attr("href", "/hrms/dept/getDeptList");
        });
    </script>
    

    至此,员工信息的显示部分基本完成。

    2 员工添加

    接下来将会实现员工的新增操作,以及对新增数据的简单验证。
    完成的页面效果如下:
    这里写图片描述
    (点击图片查看)

    主要完成的需求有:

    • (1)点击左侧栏的员工新增按钮,弹出员工新增的模态框页面;
    • (2)对输入的姓名和邮箱格式进行验证,格式不正确,提示错误信息;
    • (4) 对输入的姓名进行姓名是否重复判断,重复则提示重复信息;
    • (5)添加成功后跳转到添加记录所在的页面(即最后一页);
    • (6)添加失败则提示错误信息。

    后台代码实现主要有:

        /**
         * 查询输入的员工姓名是否重复
         * @param empName
         * @return
         */
        @RequestMapping(value = "/checkEmpExists", method = RequestMethod.GET)
        @ResponseBody
        public JsonMsg checkEmpExists(@RequestParam("empName") String empName){
            //对输入的姓名与邮箱格式进行验证
            String regName = "(^[a-zA-Z0-9_-]{3,16}$)|(^[\\u2E80-\\u9FFF]{2,5})";
            if(!empName.matches(regName)){
                return JsonMsg.fail().addInfo("name_reg_error", "输入姓名为2-5位中文或6-16位英文和数字组合");
            }
            Employee employee = employeeService.getEmpByName(empName);
            if (employee != null){
                return JsonMsg.fail().addInfo("name_reg_error", "用户名重复");
            }else {
                return JsonMsg.success();
            }
        }
    
        /**
         * 新增记录后,查询最新的页数
         * @return
         */
        @RequestMapping(value = "/getTotalPages", method = RequestMethod.GET)
        @ResponseBody
        public JsonMsg getTotalPage(){
            int totalItems = employeeService.getEmpCount();
            //获取总的页数
            int temp = totalItems / 5;
            int totalPages = (totalItems % 5 == 0) ? temp : temp+1;
            return JsonMsg.success().addInfo("totalPages", totalPages);
        }
    
        /**
         * 新增员工
         * @param employee 新增的员工信息
         * @return
         */
        @RequestMapping(value = "/addEmp", method = RequestMethod.POST)
        @ResponseBody
        public JsonMsg addEmp(Employee employee){
            int res = employeeService.addEmp(employee);
            if (res == 1){
                return JsonMsg.success();
            }else {
                return JsonMsg.fail();
            }
        }
    

    前端代码见employeeAdd.jsp。
    主要是jQuey的操作,很多操作可以封装成一个函数进行调用,例如错误信息的显示。本文为了便于查看,没有进行封装。

    3 员工更改

    员工修改操作完成页面如下:
    这里写图片描述
    这里写图片描述
    (点击图片查看)

    更改主要完成的需求有:

    • (1) 获取点击修改员工的id与name;
    • (2) 根据id或name查询出对应员工信息进行回显;
    • (3) 回显部门列表;
    • (4) 进行修改,对修改的邮箱格式进行判断;
    • (5) 点击更新按钮,发送AJAX请求到后台进行保存;
    • (6)更改成功后跳转到当前更改页面。

    后台代码实现主要有:

        /**
         * 更改员工信息
         * @param empId
         * @param employee
         * @return
         */
        @RequestMapping(value ="/updateEmp/{empId}", method = RequestMethod.PUT)
        @ResponseBody
        public JsonMsg updateEmp(@PathVariable("empId") Integer empId,  Employee employee){
            int res = employeeService.updateEmpById(empId, employee);
            if (res != 1){
                return JsonMsg.fail().addInfo("emp_update_error", "更改异常");
            }
            return JsonMsg.success();
        }
    

    前端页面+jQuery代码见:employeeUpdate.jsp

    4 员工删除

    员工删除操作完成页面如下:
    这里写图片描述
    (点击图片查看)

    删除主要完成的需求有:

    • (1)弹出确认框:是否删除XX信息;
    • (2)发送AJAX请求,执行删除操作;
    • (3)删除成功后,跳转到当前页。

    后台代码:

        /**
         * 员工删除操作
         * @param empId
         * @return
         */
        @RequestMapping(value = "/deleteEmp/{empId}", method = RequestMethod.DELETE)
        @ResponseBody
        public JsonMsg deleteEmp(@PathVariable("empId") Integer empId){
            int res = employeeService.deleteEmpById(empId);
            if (res != 1){
                return JsonMsg.fail().addInfo("emp_del_error", "员工删除异常");
            }
            return JsonMsg.success();
        }
    

    前端页面代码见employeePage.jsp:

        <!-- ==========================员工删除操作=================================== -->
        $(".emp_delete_btn").click(function () {
            var curPage = ${curPage};
            var delEmpId = $(this).parent().parent().find("td:eq(0)").text();
            var delEmpName = $(this).parent().parent().find("td:eq(1)").text();
            if (confirm("确认删除【" + delEmpName+ "】的信息吗?")){
                $.ajax({
                    url:"/hrms/emp/deleteEmp/"+delEmpId,
                    type:"DELETE",
                    success:function (result) {
                        if (result.code == 100){
                            alert("删除成功!");
                            window.location.href="/hrms/emp/getEmpList?pageNo="+curPage;
                        }else {
                            alert(result.extendInfo.emp_del_error);
                        }
                    }
                });
            }
        });
    

    至此,SSM项目的增删改查操作也基本完成,部门操作与上类似,本文不再赘述,感兴趣的可以略看Department相关操作的代码。

    5 登录页面

    最后,为求项目的完整性,加上一个登陆页面,实现的效果
    图如下:
    这里写图片描述
    (点击图片查看)

    后台主要做了一个简单的登录验证,代码见LoginController.java:

        /**
         * 登录:跳转到登录页面
         * @return
         */
        @RequestMapping(value = "/login", method = RequestMethod.GET)
        public String login(){
            return "login";
        }
    
        /**
         * 对登录页面输入的用户名和密码做简单的判断
         * @param request
         * @return
         */
        @RequestMapping(value = "/dologin", method = RequestMethod.POST)
        @ResponseBody
        public JsonMsg dologin(HttpServletRequest request){
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            System.out.println(username + password);
            if (!"admin1234".equals(username + password)){
                return JsonMsg.fail().addInfo("login_error", "输入账号用户名与密码不匹配,请重新输入!");
            }
            return JsonMsg.success();
        }
    
        /**
         * 跳转到主页面
         * @return
         */
        @RequestMapping(value = "/main", method = RequestMethod.GET)
        public String main(){
            return "main";
        }
    
        /**
         * 退出登录:从主页面跳转到登录页面
         * @return
         */
        @RequestMapping(value = "/logout", method = RequestMethod.GET)
        public String logout(){
            return "login";
        }
    
    

    前台页面见login.jsp代码;以及退出登录按钮的操作见head.jsp:

    //账号退出
    $(".hrms_logout").click(function () {
      window.location.href = "/hrms/logout";
    });
    

    注:
    容器配置:
    在这里插入图片描述

    五、项目代码下载

    最后,将本次项目的代码上传到我的github当中,想要下载项目源码的话:戳这里
    如果觉得对你有帮助别忘了在我的github上随手点个star,THX!

    If any problem, contact me with QQ : 1032335358.(请注明来源于CSDN)

    2018/03/09 in NJ.


    【页面404 fix】 - fix in 2019/01/31

    这段时间比较忙,很多同学的评论都没有来得及回复,很是抱歉。
    看了下评论,基本上都是登录无反应 和 点击页面404的问题,404一般是绝对路径的问题,可以在代码中加上绝对路径就不会出错。
    在配置容器的时候,只要点上如下配置,即可解决上述问题。
    在这里插入图片描述

    展开全文
  • 后台系统

    千次阅读 2017-08-16 17:12:04
    后台系统 (Foreground/Background System)  这种系统可称为前后台系统或超循环系统(Super-Loops)。应用程序是一个无限的循环,循环中调用相应的函数完成相应的操作,这部分可以看成后台行为(background)。中断...

    前后台系统 (Foreground/Background System)
            这种系统可称为前后台系统或超循环系统(Super-Loops)。应用程序是一个无限的循环,循环中调用相应的函数完成相应的操作,这部分可以看成后台行为(background)。中断服务程序处理异步事件,这部分可以看成前台行为(foreground)。后台也可以叫做任务级。前台也叫中断级。时间相关性很强的关键操作(Critical operation)一定是靠中断服务来保证的。因为中断服务提供的信息一直要等到后台程序走到该处理这个信息这一步时才能得到处理,这种系统在处理信息的及时性上,比实际可以做到的要差。这个指标称作任务级响应时间。最坏情况下的任务级响应时间取决于整个循环的执行时间。因为循环的执行时间不是常数,程序经过某一特定部分的准确时间也是不能确定的。进而,如果程序修改了,循环的时序也会受到影响。

          其实第一眼看到前后台系统,我脑海里不知怎么地就想起来初中学过的华罗庚的统筹方法。原文已经忘的干干静静了,只是清楚里面的道理。统筹方法告诉我们要合理分配时间,这样可以避免不必要等待,造成时间的浪费和效率的下降,这个在公司的项目管理中尤为有用。下面给出统筹法的连接

    华罗庚的统筹方法——小学对我影响最深的课文

    http://baike.baidu.com/view/293640.htm

    在介绍前后台系统前,我们还是从最简单的程序开始,抛砖引玉。还是从最简单的LED灯闪烁开始,要求LED0以1Hz闪烁。

          这里给出教科书上最经典的闪烁LED程序。

    [cpp] view plain copy
    1. void main(void)  
    2. {  
    3.         While(1)   
    4.     {  
    5.         LED0 = ON;  
    6.         DelayMs(500);  
    7.         LED0 = OFF;  
    8.         DelayMs(500);  
    9.      }  
    10. }  

              把程序下载到单片机后,上电后看见LED0按预期目标闪烁,没什么问题。但是实际中你看哪个单片机系统只是用来闪烁个LED灯来着,那也太浪费了吧!总要加点功能吧!比如要求LED0以1Hz闪烁,LED1以2Hz闪烁,怎么办?好办啊,我改下程序就是了:

    [cpp] view plain copy
    1. void main(void)  
    2. {  
    3.         While(1)   
    4.     {  
    5.         LED1 = ON;  
    6.         DelayMs(250);  
    7.         LED1 = OFF;  
    8.         DelayMs(250);  
    9.         LED0=ON;  
    10.   
    11.         LED1 = ON;  
    12.         DelayMs(250);  
    13.         LED1 = OFF;  
    14.         DelayMs(250);  
    15.         LED0=OFF;  
    16.      }  
    17. }  

           如果再加些需求,要求LED0以1Hz闪烁,LED1以2Hz闪烁,LED2以3Hz闪烁,LED4以50Hz闪烁,再加上检测每10ms检测一次按键,每20ms动态刷新一次数码管。怎么办?你还有信心改程序么?反正我是已经头大了,还是想想问题出在哪了!想来想去都觉得问题是出在延时DelayMs()上了。为了说明问题,我这里做个估算,假设一个12MHz的51单片机,执行一个单周期指令需要1us。那么10ms差不多就可以执行10000条指令了。10000条指令,这是个什么概念啊?10ms对人来说可能都感觉不到,可对于单片机来说,哪可是相当长的时间啊。如果这段时间让CPU无意义的死等。什么事也不干。这可比人死等在火炉边等水烧开要严重的多。如果人要都像单片机这种等法,那就是等死啊。总之一句话,死等=等死。

             思路决定出路,所以要换个思路来解决问题了。思考一下,要是不用DelayMs()的话,那CPU怎么样才知道每隔多久改变一次LED状态啊?这里又要联系到华罗庚的统筹方法了,这里面最重要的因素是什么?我觉得应该的时间,既然是时间那就离不开钟表。那么51单片机里面有没有钟表么?很幸运,还真有一个类似的,就是我们常说的定时器Timer。关于定时器网上描述一大堆,这里就不做详细说明了。简单的说定时器就像个闹铃,只要设置好每次闹铃的间隔时间,比如1ms的话,当启动定时器后,每隔1ms闹铃就会响一次(产生一次中断)。那么从现在开始设置闹铃,闹铃响10下,单片机就知道10ms到了;响了1000下,就知道经过了1s……。这样的话单片机就可以像人一样利用统筹法来工作了,比如LED0以1Hz闪烁的话(周期为1s,半个周期就是500ms),那么单片机可以在闹铃每响500下后,才去改变LED0 的状态。单片机在这段时间可以用来干其他事,完全不需要死等500ms。

       好了,说了一大堆,脑袋有点晕了,上程序:

    [cpp] view plain copy
    1. #include <reg52.h>  
    2.   
    3. sbit LED0 = P1^0;  
    4. sbit LED1 = P1^1;  
    5. sbit LED2 = P1^2;  
    6.   
    7. //定义软件计数器,用于对每个事件进行计数  
    8. unsigned int LED0_Counter;  
    9. unsigned int LED1_Counter;  
    10. unsigned int LED2_Counter;  
    11.   
    12. void Init(void)  
    13. {  
    14.     //设置软件计数器  
    15.     LED0_Counter=500;  
    16.     LED1_Counter=250;  
    17.     LED2_Counter=125;  
    18.   
    19.          //设置定时器T0   
    20.          TMOD&=0xf0;     
    21.          TMOD|=0x01;  
    22.          TH0=(65536-1000)/256; //定时1ms(晶振12MHz)  
    23.           TL0=(65536-1000)%256;  
    24.       
    25.          ET0=1;  //开定时器中断  
    26.           TR0=1; //开定时器  
    27.           EA=1; //开总中断  
    28.   
    29. }  
    30.   
    31. void main(void)  
    32. {  
    33.     Init();   
    34.     while(1){  
    35.   
    36.         if(LED0_Counter==0){  //判断时间是否到,如果到了则执行LED0Event(),否则绕行  
    37.             LED0_Counter=500;//LED0Event(),每500*1ms执行一次  
    38.             LED0Event();  
    39.         }  
    40.    
    41.         if(LED1_Counter==0){  
    42.             LED1_Counter=250;  
    43.             LED1Event();  //LED1Event(),每250*1ms执行一次      
    44.         }  
    45.   
    46.         if(LED1_Counter==0){  
    47.             LED2_Counter==125;  
    48.             LED2Event(); //LED2Event(),每125*1ms执行一次   
    49.         }  
    50.     }  
    51. }  
    52.   
    53.   
    54.   
    55. void Timer0_isr(void) interrupt 1   
    56. {  
    57.     TH0=(65536-1000)/256; //定时1ms  
    58.     TL0=(65536-1000)%256;  
    59.           //每次中断,所有软件定时器减一,如果定时器为0,表明其相应事件执行的时间到了  
    60.     if(LED0_Counter) LED0_Counter--;  
    61.     if(LED1_Counter) LED1_Counter--;  
    62.     if(LED2_Counter) LED2_Counter--;  
    63.   
    64. }  

          这里最关键的是定义了三个全局变量 LED0_Counter、LED1_Counter和LED2_Counter,相当于分别对LED0、LED1、LED2定义了一个软件计数器。软件定时器采用倒计时方式,只在Timer0中断中进行减一操作(相当于在每次闹铃的时候减一) ,只要减为0的话,就执行相应操作。

    展开全文
  • 后台系统:库存管理系统

    千次阅读 2019-01-04 16:07:26
    库存系统作为电商后台系统中不可或缺的一部分,虽然公司业务千差万别,但主要的元素却是相似的,文章主要是根据自己的经验来浅谈一下库存系统的设计。亦作为一个自我工作的总结和梳理。 库存管理系统,简单的说,...

    库存系统作为电商后台系统中不可或缺的一部分,虽然公司业务千差万别,但主要的元素却是相似的,文章主要是根据自己的经验来浅谈一下库存系统的设计。亦作为一个自我工作的总结和梳理。

    库存管理系统,简单的说,就是管理商品和数量之间的关系。在功能划分上,主要包括商品库存数量、入库、出库、盘点几个部分。

     

    一、商品库存

    即各个商品的数量管理。需要注意的是,在系统里需要保证商品的唯一性。在这里,涉及到以下几个数量:

    库存总数:目前仓库里还剩余的真实数量;可用数量:可以用于销售、调拨等的数量;锁定数量:指已经有了去处或者计划,但还未真正出库的数量(该部分在很多系统中,又会根据业务的不同而分成多个部分,在这里,暂且统一称之为锁定数量);

    当然,在实际设计过程中,还会涉及到其他的值和功能点,以下几点可作为参考:

    可以针对每个商品,做一个商品的历史变动明细、或根据不同维度做一些简单的数量统计;设置阈值,当数量小于某个值时,给出通知或提示,以告知相关人员进行采购;每个商品从采购到出库,每一个商品的流向进出都需要有所记录。在设计过程中,这一点是特别重要的,但却也是最复杂的,因为每一个业务通常都有很多的环节,会涉及到审批、物流、成本价等等,一个不小心,数据就会出现偏差;

    二、商品入库

    商品入库即商品数量增加的情形,主要包括采购、退货和调拨几种情形:

    采购:即仓库向供应商进行采购。(在不同的公司不同业务中,可能会涉及到很多的层采购,流程的复杂程度也随之各不相同);退货:从商城等的退货;调拨:从其他仓库调拨过来;虽然看起来入库包含的内容不是很多,但实际上是仓库系统的设计在后台系统中却是较为复杂的一部分。不同的业务,需要考虑的因素太多,每一个商品,每一个批次,每一个入库的类型,还有相关的物流信息等,都需要反复思考和优化。

    那么,下面就来介绍一下商品的入库流程及每一个环节中可以考虑的问题吧。

    1、采购

    采购单信息:作为一批货品的采购信息,需要保证商品信息的完善和准确性;预估功能:对各个商品的出库数量做一个周期性的预估,以便更好的进行库存控制;采购提醒:在系统中可以设计一个监控功能,当商品可用数量少于一定值时,提醒采购;快捷填写采购单:因为录入采购单比较耗时,所以是否可以考虑做一个快捷填充的入口,比如可以根据上次的采购数量生成一个基础的采购单,然后再在自动生成的采购单基础上进行修改。当然,此方法是否可行还要看具体业务流程,在此只是作为一个参考;采购审核:在一些业务流程中,填写采购单后,是需要相关负责人先进行审核的,审核通过后会根据审核结果生成一张真正的采购单。此时,要考虑在审核过程中允许操作的点,比如是否允许修改数量、规格等;采购状态:需要提供一个可以追踪的界面,以便知道申请进度;权限:还需要注意的是,每个状态下每个角色的操作权限。比如哪些人可以申请采购,哪些人可以审核等;2、退货

    在这里,所指的入库是已经从仓库里面发出,之后再进行退还的过程。此过程中需要考虑的点有:

    退货状态:虽然这是属于订单系统里面的内容,但在仓库系统里面最好也有所表示,这样有利于仓库管理者监控商品的动态;退货仓库:考虑在库存里,是否需要把退货部分单独统一管理;退货商品成本:在有的系统中,从供应商采购再到最后零售的过程,可能会存在断层的情况,而且每一批商品的成本价可能会有所不同,此时就需要直接从入库单开始,实行先买先卖的原则,当退货时,就需要保证退回的商品退到了原始批次上;这一操作,主要是为了财务对账做准备3、调拨

    这就涉及到两个仓库之间的数量变化了。基本上调拨中仓库之间的变化如下:

    如上图所示,若要从仓库A调拨50个某商品到仓库B,则:

    发货仓库:发起调拨后,发货仓库需要先锁定库存,此时,可用数量减少。发货后,锁定库存为0,,总库存减少;收货仓库:在真正入库之前,一切数量均不改变;

    三、商品出库

    商品出库主要包含:销售(经销商/零售/线上/线下/不同平台和渠道等)、调拨出库

    1、销售

    销售出库是最主要的一种出库形式,虽然销售的形式、渠道等都各不相同,但主要的逻辑都是相似的。

    举个简单的例子:仓库A里面目前还有可口可乐500箱,有100箱已经卖出去了,但买家要明天才来提货。那么这100箱就会被锁定,不可再用于其他地方,此时,可用的只有400箱。若买家又不想买了取消了订单,那么被锁定的100箱库存又被释放,可用数量再次变为500箱。

    2、调拨:如入库调拨所示。

    四、仓库盘点

    盘点主要是用于管理仓库实际值与系统值的差异的。理论上来说,若商品的各个环节数据都准确的话,实际值与系统值应该是一致的。但实际中可能会有一些系统检测不到的因素影响了真实的库存,这就需要仓库进行周期性的盘点了。

    盘点之后,若实际值与系统值不一致,就需要把系统值修改正确,这时,可以通过人工或者自动生成出入库单的形式去修改系统值,而且修改的这部分数据是需要做出标记的,以便于财务之后的对账。(当然,实际设计中如何处理这部分差异,还要看业务性质和需求)

    由于在实际的设计中,仓库系统涉及到的东西比较多,而且与业务的相关度比较高,不同的业务,设计出来的库存系统会有很大的差别,这里仅做了一个简单的概括和总结,若有其他的建议和方法,欢迎交流分享。

    展开全文
  • 基于Vue实现后台系统权限控制

    万次阅读 多人点赞 2018-03-01 19:09:19
    基于Vue.js 2.x系列 + Element UI 的后台系统权限控制 前言:关于vue权限路由的那些事儿…… 项目背景:现有一个后台管理系统,共存在两种类型的人员 ①超级管理员(称作admin),②普通用户(称作editor) 每...

    基于Vue.js 2.x系列 + Element UI 的后台系统权限控制

    前言:关于vue权限路由的那些事儿……

    项目背景:现有一个后台管理系统,共存在两种类型的人员

    ①超级管理员(称作admin),②普通用户(称作editor)

    每种类型的人看到的操作栏并不一样,可以进行的操作也不尽相同,于是就需要程序处理一下各个权限问题。

    过程说难不难,说简单不算简单

    vue权限

    【迷茫的前期】

    上百度、Google,狂搜了好多关于权限的问题,也许是仁者见仁智者见智吧,五花八门的介绍让自己更加迷茫不堪,真心不知道从哪里下手:

    1)让后端返回关于权限的json数据吧,但却不太懂这样的数据应该怎样处理;

    2)在前端路由那里处理,可是不明白应该怎样使用何种属性来实现这个功能;

    【最后】

    最后看到一篇文章 手摸手,带你用vue撸后台 系列二(登录权限篇)
    ,但是发现代码非常多权限功能是整合在框架里面的,伤心,我就想实现一个小小的权限功能,没办法还是仔细的研究了起来。

    具体实现思路

    1 创建vue实例的时候将vue-router挂载,但这个时候vue-router挂载一些登录或者不用权限的公用的页面。

    2 当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。

    3 调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由。

    4 使用vuex管理路由表,根据vuex中可访问的路由渲染侧边栏组件。

    是不是有点懵没关系下面我尽量用通俗点的话来讲每一步

    1在路由router.js里面声明权限为admin的路由(异步挂载的路由asyncRouterMap)

    // router.js
    import Vue from 'vue'
    import Router from 'vue-router'
    
    Vue.use(Router)
    
    export const constantRouterMap = [
      {
        path: '/',
        redirect: '/login',
        hidden: true
      },
      {
        path: '/login',
        name: '登录页面',
        hidden: true,
        component: resolve => require(['../views/login/Login.vue'], resolve)
      },
      {
        path: '/Readme',
        // name: 'Readmehome',
        index: 'Readme',
        meta: {
          title: 'Readme',
          icon: 'el-icon-menu'
        },
        component: resolve => require(['../components/common/Home.vue'], resolve),
        children: [
          {
            name: 'Readme',
            path: '/',
            meta: { title: 'Readme', icon: 'el-icon-menu' },
            component: resolve => require(['../components/page/Readme.vue'], resolve)
          }
        ]
      }
    ]
    
    export default new Router({
      routes: constantRouterMap
    })
    // 异步挂载的路由
    // 动态需要根据权限加载的路由表
    export const asyncRouterMap = [
      {
        path: '/permission',
        // name: 'permissionhome',
        meta: {
          title: 'permission',
          icon: 'el-icon-setting',
          roles: ['admin']
        },
        component: resolve => require(['../components/common/Home.vue'], resolve),
        children: [
          {
            name: 'permission',
            path: '/permission',
            meta: {
              title: 'permission', icon: 'el-icon-menu', roles: ['admin']
            },
            component: resolve => require(['../components/page/permission.vue'], resolve)
          }
        ]
      },
      { path: '*', redirect: '/404', hidden: true }
    ]
    

    这里我们根据 vue-router官方推荐 的方法通过meta标签来标示改页面能访问的权限有哪些。如meta: { role: [‘admin’,’super_editor’] }表示该页面只有admin和超级编辑才能有资格进入。

    注意事项:这里有一个需要非常注意的地方就是 404 页面一定要最后加载,如果放在constantRouterMap一同声明了404,后面的所以页面都会被拦截到404,详细的问题见addRoutes when you’ve got a wildcard route for 404s does not work

    2当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较,调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由,生成最终用户可访问的路由表。路由表存在vuex里面

    permission.js

    // permission.js
    import router from './router'
    import store from './store'
    import { Message } from 'element-ui'
    import { getToken } from '@/utils/auth' // 验权
    
    const whiteList = ['/login', '/authredirect'] // 不重定向白名单
    
    router.beforeEach((to, from, next) => {
      if (getToken()) { // 判断是否有token
        if (to.path === '/login') {
          next({ path: '/' })
        } else {
          if (store.getters.roles.length === 0) {
            console.log('roles====0')
            store.dispatch('GetInfo').then(res => { // 拉取用户信息
              const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop']
              console.log('roles?', roles)
              store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表
                console.log('addrouters', store.getters.addRouters)
                router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
                next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
              })
            }).catch(() => {
              store.dispatch('FedLogOut').then(() => {
                Message.error('验证失败,请重新登录')
                next({ path: '/login' })
              })
            })
          } else {
            console.log('====1')
            next() // 当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的全面会自动进入404页面
          }
        }
      } else {
        if (whiteList.indexOf(to.path) !== -1) {
          next()
        } else {
          next('/login')
        }
      }
    })
    
    

    3使用vuex管理路由表,根据vuex中可访问的路由渲染侧边栏组件(菜单)。

    sidebar

    最后预览链接

    vue实现的权限系统

    源码下载

    https://github.com/mgbq/vue-permission

    打赏 衷心的表示感谢

    打赏
    支付宝扫一扫

    最后如有不对的麻烦指正

    展开全文
  • vue 后台系统权限管理

    千次阅读 2018-12-07 09:07:18
    最近在做一个后台管理系统,一般的后台系统都有权限管理这块,下面我就分享下我实现权限管理这块的思路。 技术栈及实现思路 首先说下这个系统前端用到的技术栈,vue全家桶,element-ui,axios。首先,用户的权限是通过...
  • 前台系统和后台系统中异常的设计

    千次阅读 2017-04-24 10:21:53
    前台系统和后台系统中异常的设计 后台系统后台系统应将异常信息完整地、准确地返回给前台系统。 如: facade层 LoanFacadeImpl.java //申请贷款 try { String loanRecordNo = loanBiz.applyLoan...
  • 在写一个程序之前,需要在大脑中对整个系统有一...电子商城的后台系统,通常都会有权限管理模块,但权限管理模块,与商城的业务没有关系,所以权限管理就要放到框架中。而且,不单是电子商城,其它系统的后台,也可...
  • vue开发的一个后台系统

    千次阅读 2017-09-16 18:13:41
    vue开发的一个后台系统
  • 【代码分享】一套后台系统RuoYi

    千次阅读 2019-06-28 08:59:52
    于是利用空闲休息时间开始自己写了一套后台系统。如此有了若依。她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动...
  • 基础版的聊天后台系统

    千次阅读 2018-09-20 16:37:15
    基础版的聊天后台系统一、聊天功能二、 架构组成三、 流程图四、 会遇见的问题 一、聊天功能 一对一聊天 语音发送 表情发送 图片发送 聊天历史 聊天联系人 在线消息提醒 个人设置 下线消息通知 功能...
  • 基于SpringBoot 2的管理后台系统

    万次阅读 2018-04-24 20:02:50
    本文demo下载:http://www.wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId=1062一个基于SpringBoot 2 的管理后台系统,包含了用户管理,组织机构管理,角色管理,功能点管理,菜单管理,权限分配,数据...
  • 推荐一个Java后台系统Bootstrap模板

    千次阅读 2018-03-29 19:42:18
    推荐一个Java后台系统Bootstrap模板 这个模板是我们公司专门用来做后台系统的,简单,漂亮,不会前端的也可以做 下载地址:https://download.csdn.net/download/qq_21683643/10316430...
  • 案例五:网站后台系统页面

    千次阅读 多人点赞 2019-01-15 21:19:00
    案例五:网站后台系统页面 框架集标签: &amp;lt;frameset rows=&quot;&quot; cols=&quot;&quot;&amp;gt; &amp;lt;frame src=&quot;&quot;/&amp;gt; &amp;...
  • 微信红包后台系统可用性设计实践

    千次阅读 2017-07-04 21:28:48
    微信红包业务量级的高速发展,对后台系统架构的可用性要求越来越高。在保障微信红包业务体验的前提下,红包后台系统进行了一系列高可用方面的优化设计。本次演讲介绍了微信红包后台系统的高可用实践经验,主要包括...
  • 支付宝等大型支付系统交易额巨大,后台系统是如何对账和风控的呢?
  • TP5后台系统+小程序商城源码【前端+后台】开源完整版 扩展性 独立模块式开发,完善的注释,易扩展。 安全性 防止sql注入,代码高安全性。 轻量级,高性能 支持多数据库,读写分离,高并发,内置缓存机制。 后端基于...
  • 后台系统设计:工作流设计剖析

    千次阅读 2017-11-28 00:00:00
    一般在稍微复杂一些的后台系统中,工作流的设计是不可避免的一个重要部分。 设计好一个后台工作流,不仅可以使得后期使用系统的时候更加高效,同时也是十分考验产品经理的。 刚好最近自己在做这方面的工作...
  • 秒杀后台系统设计

    千次阅读 2016-10-20 18:07:39
    网上有很多介绍秒杀系统架构文章,写得不错,包括前后端的实现,这里只从后台服务系统架构阐述。 打开页面时查询商品数量和点击抢购按钮提交校验时都是从本地web进程内缓存读取,这里没有使用redis,是考虑到...
  • 10个细节提升后台系统的用户体验

    千次阅读 2017-01-08 09:57:02
    要说程序员最开心的事情是什么,不是钱多活少离家近...尽管它只是一个后台系统。 说到后台系统(管理系统、ERP系统),我也做过很多了,无一不难看、难用。因为这类系统不是给终端用户使用,对交互要求不高,也没有UI
  • 后台系统 (Foreground/Background System) 这种系统可称为前后台系统或超循环系统(Super-Loops)。应用程序是一个无限的循环,循环中调用相应的函数完成相应的操作,这部分可以看成后台行为(background)。中断...
  • ace admin 是一个非常好的后台系统ui。 集成了很多的好东西。非常的方便开发后天系统,而且能很漂亮。 上面有一堆的例子。非常的漂亮。 http://ace.jeka.by/ 之前还是收费的。后来在github 上面放了一个项目。...
  • 后台系统不好用?找不到结症在哪里?可能缺少了这样的交互设计——后台说明。 1.背景 这事要从自己经历的案例说起,最近忙活的一个后台管理系统上线了。吻合业务需求的功能,准确有效率的数据,好用的...
  • Ant Design Pro of Vue——蚂蚁金服中后台系统框架搭建 Ant Design Pro of Vue下载安装启动步骤: 一、Ant Design Pro of Vue 的介绍 Ant Design Pro 是一个企业级中后台前端/设计解决方案,我们秉承 Ant Design 的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 93,819
精华内容 37,527
关键字:

后台系统