疫情隔离区订餐系统的开发

简介: 本系统避免了大多数订餐平台在不能很好满足隔离区人们订餐的需求所造成的局限性,针对防疫处理有一定的针对性。具体功能如下:(1)为用户买卖物品提高效率、提供便捷。(2)为管理员提供了新增和删除用户,以及修改用户密码的功能。(3)为管理员提供了发布、查询、修改、浏览、购买商品的功能。(4)为管理员提供了添加和管理商品分类、疫情公告信息以及对用户信息进行管理的功能。(5) 为管理员提供了可以对疫情防控信息进行添加、修改和删除的功能。

 

目录

一,项目简介

二,环境介绍

        三,系统展示

3.1 后台管理功能模块的展示

3.1.1 前台用户浏览商品模块

3.1.2前台用户购物车模块

3.1.3 前台用户购买商品模块

3.1.4前台用户个人订单管理模块

3.1.5前台用户收货地址管理模块

3.1.6前台用户新闻查看模块

3.2后台功能模块的展示

3.2.1用户登录功能

3.2.2用户管理功能

3.2.3餐品分类管理功能

3.2.6 后台商品交易管理操作UI

3.2.7 后台商品销量统计管理操作UI

3.2.8 后台疫情新闻管理操作UI

3.2.9 后台员工健康管理操作UI

 四,核心代码展示

五,项目总结


一,项目简介

     由于疫情原因,各行各业都受到了严重的冲击,其中受影响最严重的莫过于传统餐饮行业,新冠病毒凭借其令人恐惧的突变速度在世界各地持续蔓延。在餐饮线下,来店里吃饭的人,从客观的来说,必须摘下口罩吃饭,如果不限制科客人的接待,不同的餐厅将不可避免地受到病毒传播的严重影响,各地出现疫情的政府也纷纷下令禁止疫情隔离区内出现堂食现象,严格控制餐馆。可是“民以食为天”,人作为自然界的生物进食是基本生存需求,而目前国内大型的订餐平台诸如饿了么,美团等注册流程复杂、界面看起来眼花缭乱,各种与订餐无关的旅游、酒店、订票等无关入口,而且缺乏些许针对疫情的协助管控措施。一个干净整洁、主题鲜明的疫情隔离区订餐系统的想法由此孕育而生。

  本项目的意义在于疫情之下降低对小餐饮行业的冲击,提高今后餐饮行业的生命活力以及对疫情的管控提供协助,降低人与人之间的接触,减少乃至阻断社会各个阶层在食、行方面的疫情传播。为未来餐饮行业该工作的重心,以及在疫情在餐饮行业的防治方式贡献一份力,自己的一份思想,以及如何在已有的各大平台中发扬自身的特点优势,提出一份思想自己平台能在众多餐饮平台中杀出重围的生存方式及特点。我需要指出本项目的重点意义在于如何挽救疫情之下小餐饮行业的发展力,为小型餐饮行业在疫情期间的发展方向提供重心指导,以及为疫情隔离区的民众提供一款简洁舒适的订餐网站,同时也为疫情管控提供帮助。  

      本系统避免了大多数订餐平台在不能很好满足隔离区人们订餐的需求所造成的局限性,针对防疫处理有一定的针对性。具体功能如下:

(1)为用户买卖物品提高效率、提供便捷。

(2)为管理员提供了新增和删除用户,以及修改用户密码的功能。

(3)为管理员提供了发布、查询、修改、浏览、购买商品的功能。

(4)为管理员提供了添加和管理商品分类、疫情公告信息以及对用户信息进行管理的功能。

(5) 为管理员提供了可以对疫情防控信息进行添加、修改和删除的功能。

二,环境介绍

语言环境:Java:  jdk1.8

数据库:Mysql: mysql5.7

应用服务器:Tomcat:  tomcat8.5.31

开发工具:IDEA或eclipse

开发技术:SSM框架+前端框架开发jquery相关插件

三,系统展示

3.1 后台管理功能模块的展示

3.1.1 前台用户浏览商品模块

  疫情隔离区订餐系统前台用户进入商城后无须注册登陆即可查看商城相应的餐品信息。具体操作界面展示如下图3-1所示。

image.gif编辑

图3-1 前台用户浏览商品界面

3.1.2前台用户购物车模块

   疫情隔离区订餐系统前台用户登陆系统后可以将需要购买的餐品添加到商城购物车。添加购物车操作界面如下图3-2所示。

image.gif编辑

图3-2 前台用户添加购物车操作界面

3.1.3 前台用户购买商品模块

   前台用户登陆商城系统后,可以将喜欢的餐品品添加到购物车后提交订单并进行支付购买。完成产品购买后可以对系统的服务、物流的速度、客服的态度等进行评分,并对产品进行相关的评论。相关管理操作界面如下图3-3、3-4所示。

image.gif编辑

图3-3前台用户购物操作界面

image.gif编辑

图3-4前台用户支付操作界面

3.1.4前台用户个人订单管理模块

   疫情隔离区订餐系统前台用户登陆系统后可以在个人订单管理模块中管理个人的订单信息。前端用户个人订单管理操作界面如下图3-5所示。

image.gif编辑

图3-5前台用户个人订单管理操作界面

3.1.5前台用户收货地址管理模块

   疫情隔离区订餐系统前台用户登陆系统后可以在用户中心管理模块中管理个人的收货地址信息。前端用户个人收货地址管理操作界面如下图3-6所示。

image.gif编辑

图3-6前台用户个人收货地址管理界面

3.1.6前台用户新闻查看模块

   疫情隔离区订餐系统前台用户登陆系统后可以在疫情新闻中浏览发布的最新新闻信息。前端用户个人新闻查看操作界面如下图3-7所示。

image.gif编辑

图3-7前台用户新闻信息查看界面

3.2后台功能模块的展示

3.2.1用户登录功能

   疫情隔离区订餐系统后台用户如果想要对订餐系统的相关信息进行管理操作,首先要登录系统,才可展开相关的操作。用户登陆界面如下图3-8所示。

image.gif编辑

图3-8后台用户登录操作界面

3.2.2用户管理功能

   疫情隔离区订餐系统管理员用户登陆系统后,可以进入用户管理菜单进行相应的用户信息管理,主要实现对用户的添加、修改、删除、查询、停用、启用操作。用户管理操作界面如下图3-9所示:

image.gif编辑

图3-9后台用户管理功能界面图

3.2.3餐品分类管理功能

   疫情隔离区订餐系统管理员用户登陆系统后,可以进入产品管理菜单中的二级菜单进行相应的餐品分类信息管理。餐品分类操作界面如下图3-10所示。

image.gif编辑

图3-10餐品分类管理功能UI界面

3.2.6 后台商品交易管理操作UI

   疫情隔离区订餐系统管理员用户登陆系统后,可以进入商品交易管理菜单进行相应的商品交易信息管理和交易统计查询管理。其中主要包含商品订单的查询、订单明细的查看、订单发货等,添加商品时可以上传商品的图片进行展示。商品交易管理操作界面如下图3-13所示。

image.gif编辑

图3-13后台商品交易管理功能UI界面

3.2.7 后台商品销量统计管理操作UI

   疫情隔离区订餐系统管理员用户登陆系统后,可以进入销量统计管理菜单进行相应的商品销售情况进行统计查看。主要以图形报表方式进行展示,其中主要以饼状图和柱状态图的形态进行展示。展示的图片可以下载也可以在线打印操作。商品信息统计查看界面如下图3-14所示。

image.gif编辑

图3-14后台商品交易统计功能UI界面

3.2.8 后台疫情新闻管理操作UI

   疫情隔离区订餐系统管理员用户登陆系统后,可以进入疫情新闻管理菜单进行相应的疫情最新动态的发布和管理操作。疫情新闻管理界面如下图3-15所示。

image.gif编辑

图3-15疫情新闻管理功能UI界面

3.2.9 后台员工健康管理操作UI

   疫情隔离区订餐系统管理员用户登陆系统后,录入每天餐厅员的健康状态和体温信息,以便于卫生系统人员进行定期检查,也确保员工的健康状态。疫情期间员工健康状态管理界面如下图3-16所示。

image.gif编辑

图3-16疫情新闻管理功能UI界面

四,核心代码展示

package com.ms.diancan.controller.web;
import com.ms.diancan.entity.Role;
import com.ms.diancan.entity.User;
import com.ms.diancan.service.RoleService;
import com.ms.diancan.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
import java.util.List;
/**
 * @author me
 * @createTime 2022-03-17 09:33
 */
@Controller
public class Back_AdminController {
    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @GetMapping("/admin/toMemberList")
    public String toMemberList(Model model){
        List<User> allUser = userService.findAllUser();
        List<Role> roleList  = roleService.findRoleList();
        model.addAttribute("userList",allUser);
        model.addAttribute("roleList", roleList);
        return "/admin/member/member_list";
    }
    /**
     *
     * @param session  前台传过来的管理员的session,里面存有user对象
     */
    @GetMapping("/admin/personalInfo")
    public String findUserByUserId(HttpSession session, Model model){
        Integer userId = ((User) session.getAttribute("user")).getId();
        User user = userService.findUserInfo(userId);
        model.addAttribute("user", user);
        return "/admin/member/personal_info";
    }
    @PostMapping("/admin/updateAdmin")
    public String updateUser(User user) {
        userService.updateUser(user);
        return "redirect:/admin/personalInfo";
    }
    @GetMapping("/admin/updateAdminById")
    public String updateUserById(User user) {
        userService.updateUserById(user);
        return "redirect:/admin/personalInfo";
    }
    @RequestMapping("/admin/check")
    @ResponseBody
    public String checkPassowrd(User user, String npassword){
        User myuser = userService.findUserId(user.getId());
        String password = myuser.getPassword();
        if(user.getPassword().equals(password)){
            user.setPassword(npassword);
            userService.updateUserPassword(user);
            return "true";
        }else{
            return "false";
        }
    }
    @RequestMapping("/addAdmin")
    public String addAdmin(User user) {
        userService.addUser(user);
        return "redirect:/admin/toMemberList";
    }
    @RequestMapping("/deleteAdmins")
    public String deleteAdmins(@RequestParam("userId") String[] userIds){
        userService.deleteAdminsById(userIds);
        return "redirect:/admin/toMemberList";
    }
    @RequestMapping("/admin/selectUser")
    public String selectUser(String userName, Model model){
        List<User> dbUserList = userService.findUserByUsername(userName);
        model.addAttribute("userList", dbUserList);
        return "/admin/member/member_list";
    }
    @PostMapping("/admin/stopStatus")
    public void stopState(String userId) {
        int status = 0;
        userService.updateUserStatus(userId, status);
    }
    @PostMapping("/admin/startStatus")
    public void startState(String userId) {
        int status = 1;
        userService.updateUserStatus(userId, status);
    }
    @RequestMapping("/admin/deleteUserById")
    public void deleteUserById(String userId) {
        userService.deleteUserById(userId);
    }
    @RequestMapping("/adminCompetence")
    public String adminCompetence(Model model) {
        List<User> userList = userService.findAdminList();
        String pRoleName = "";
        String sRoleName = "";
        int pCount = 0;
        int sCount = 0;
        for(User user : userList){
        /*    if("超级管理员".equals(user.getRole().getRoleName())){
                sRoleName = sRoleName + " " + user.getUsername();
                sCount = sCount + 1;
            }else{
                pRoleName = pRoleName + "  " + user.getUsername();
                pCount = pCount + 1;
            }*/
        }
        model.addAttribute("pRoleName", pRoleName);
        model.addAttribute("pCount", pCount);
        model.addAttribute("sRoleName" , sRoleName);
        model.addAttribute("sCount", sCount);
        return "/admin/member/admin_competence";
    }
}

image.gif

package com.ms.diancan.controller.web;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ms.diancan.entity.Product;
import com.ms.diancan.entity.vo.OrderAddressCountVO;
import com.ms.diancan.entity.vo.OrderStatusCountVO;
import com.ms.diancan.service.EchartsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.sql.SQLOutput;
import java.util.List;
/**
 * @author me    hart图表展示相关controller
 * @createTime 2022-02-14 09:39
 */
@RestController
public class Back_EchartsController {
    @Autowired
    private EchartsService echartsService;
    @PostMapping("/admin/buyCount")
    public Object getBuyCount() throws JsonProcessingException {
        List<Product> proList= echartsService.findAll();
        ObjectMapper mapper=new ObjectMapper();
        String json=mapper.writeValueAsString(proList);
        System.out.println(json);
        return json;
    }
   @PostMapping("/admin/buyAddr")
    public Object getBuyAddr() throws JsonProcessingException {
        List<OrderAddressCountVO> ordList=echartsService.findAllAddr();
        ObjectMapper mapper=new ObjectMapper();
        String json=mapper.writeValueAsString(ordList);
        return json;
    }
    @PostMapping("/admin/orderStatus")
    public Object getOrderStatus() throws JsonProcessingException {
        List<OrderStatusCountVO> orderStatusList = echartsService.findAllOrderStatus();
        orderStatusList.forEach((OrderStatusCountVO)->{
            System.out.println(OrderStatusCountVO.getOrderStatus());
            System.out.println(OrderStatusCountVO.getCount());
        });
        ObjectMapper mapper=new ObjectMapper();
        String json=mapper.writeValueAsString(orderStatusList);
        return json;
    }
}

image.gif

package com.ms.diancan.controller.web;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ms.diancan.entity.Emp;
import com.ms.diancan.entity.News;
import com.ms.diancan.entity.dto.EmpDTO;
import com.ms.diancan.service.EmpService;
import com.ms.diancan.service.NewsService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoProperties;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/**
 * @author me  员工健康管理相关controller
 * @createTime 2022-04-15 19:11
 */
@Controller
public class Back_EmpController {
    @Autowired
    private EmpService empService;
    /**
     * 展示每日员工健康管理列表
     * @return
     */
    @RequestMapping("/admin/toEmpList")
    public String sortAds(@RequestParam(required = true, defaultValue = "1") Integer page,Model model) {
        PageHelper.startPage(page, 10);
        List<Emp> empList = empService.findAll();
        model.addAttribute("empList",empList);
        PageInfo<Emp> p = new PageInfo<Emp>(empList);
        model.addAttribute("page", p);
        return "/admin/emp/emp_list";
    }
    /**
     * 创建员工健康
     */
    @RequestMapping("/admin/createEmp")
    public  String createEmp(Emp emp){
        empService.addEmp(emp);
        return "redirect:/admin/toEmpList";
    }
    /**
     * 修改员工健康
     */
    @RequestMapping("/admin/updateEmp")
    public  String updateEmp(EmpDTO empDto){
        Emp emp = new Emp();
        emp.setId(empDto.getId());
        emp.setEname(empDto.getUename());
        emp.setEcard(empDto.getUecard());
        emp.setEsex(empDto.getUesex());
        emp.setEphone(empDto.getUephone());
        emp.setEdesc(empDto.getUedesc());
        emp.setEtiwen(empDto.getUetiwen());
        emp.setEstatus(empDto.getUestatus());
        emp.setEdate(empDto.getUedate());
        empService.updateEmp(emp);
        return "redirect:/admin/toEmpList";
    }
    /**
     * 批量删除员工健康
     */
    @RequestMapping("/admin/deleteBatchEmps")
    public  String deleteBatchEmps(@RequestParam("id") Integer[] id){
        empService.deleteEmps(id);
        return "redirect:/admin/toEmpList";
    }
    /**
     * 批量显示员工健康--未启用
     */
    @RequestMapping("/admin/showBatchEmps")
    public  String showBatchEmps(@RequestParam("id") Integer[] id){
        empService.showEmps(id);
        return "redirect:/admin/toEmpList";
    }
    /**
     * 批量关闭员工健康--未启用
     */
    @RequestMapping("/admin/closeBatchEmp")
    public  String closeBatchEmp(@RequestParam("id") Integer[] id){
        empService.closeEmps(id);
        return "redirect:/admin/toEmpList";
    }
}

image.gif

package com.ms.diancan.controller.web;
import com.ms.diancan.entity.Leave;
import com.ms.diancan.entity.User;
import com.ms.diancan.enums.UserRoleEnum;
import com.ms.diancan.mapper.LeaveMapper;
import com.ms.diancan.mapper.UserMapper;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.jws.WebParam;
import javax.servlet.http.HttpSession;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
 * @author me
 * @createTime 2022-03-18 13:45
 */
@Controller
public class Back_HomeController {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private LeaveMapper leaveMapper;
    /**
     * 去后台登录系统
     */
    @RequestMapping("/admin/index")
    public String adminLogin() {
       return "/admin/common/login";
    }
    @PostMapping("/admin/backLogin")
    public String backLogin(User user, Model model){
        User dbUser = userMapper.findUserByU_P(user.getUserName(),user.getPassword());
        //1-如果登录账号不存在 直接返回
        if(dbUser == null){
            model.addAttribute("errorInfo","用户名或密码错误~~");
            return "/admin/common/login";
        }
        //2-如果登录账号存在  判断是否为管理员身份
        if(dbUser.getRoleId() == 1){
            Subject subject = SecurityUtils.getSubject();
            //2-1-查询未读的留言
            List<Leave> messageList = leaveMapper.findAllLeaveList()
                    .stream().filter(leave -> leave.getStatus() == 0).collect(Collectors.toList());
            model.addAttribute("messageNum",messageList.size());
            subject.getSession().setAttribute("user",dbUser);
            return "/admin/common/index";
        }
        //3-如果不是管理员  返回登录界面
        model.addAttribute("errorInfo","非管理员不能登录后台管理系统~~");
        return "/admin/common/login";
    }
    @RequestMapping("/admin/home")
    public String home() {
        //跳转回用户列表页面
        return "/admin/common/home";
    }
    @RequestMapping("/brandManage")
    public String brandManage() {
        return "/admin/Brand_Manage";
    }
    @RequestMapping("toProduct_category_add")
    public String toProduct_category_add(){
        return "/admin/product-category-add";
    }
    @RequestMapping("/advertising")
    public String advertising() {
        return "/admin/advertising";
    }
    @RequestMapping("/admin/transaction")
    public String transaction() {
        return "/admin/order/transaction";
    }
    @RequestMapping("/amounts")
    public String amounts() {
        return "/admin/Amounts";
    }
    @RequestMapping("/orderHandling")
    public String orderHandling() {
        return "/admin/Order_handling";
    }
    @RequestMapping("/coverManagement")
    public String coverManagement() {
        return "/admin/Cover_management";
    }
    @RequestMapping("/userList")
    public String userList() {
        return "/admin/user_list";
    }
    @RequestMapping("/memberGrading")
    public String memberGrading() {
        return "/admin/member-Grading";
    }
    @RequestMapping("/integration")
    public String integration() {
        return "/admin/integration";
    }
    @RequestMapping("/feedBack")
    public String feedBack() {
        return "/admin/Feedback";
    }
    @RequestMapping("/systems")
    public String systems() {
        return "/admin/Systems";
    }
    /*  @RequestMapping("/menuManage")
      public String menuManage() {
          return "/admin/test";
      }*/
    @RequestMapping("/userManage")
    public String userManage() {
        return "/admin/test";
    }
    /* */
    @RequestMapping("/systemSet")
    public String systemSet() {
        return "/admin/Systems";
    }
}

image.gif

五,项目总结

 SSM疫情隔离区订餐系统的使用者主要包含两种用户角色,其一是管理员角色,其二是前台用户角色,这两个角色的具体功能如下:

   管理员角色:管理员登录疫情隔离区订餐系统后可以进行相应的管理操作,主要包含:用户管理、餐品管理、订单管理、分类管理、公告管理、新闻管理、员工健康管理、留言管理等操作;

前台用户角色:前台用户登录疫情隔离区订餐系统后可以进行餐品浏览、添加餐品购物车、购物车管理、购买餐品、个人订单管理、个人中心管理等操作。

image.gif编辑

相关文章
|
开发框架 小程序 前端开发
东郊到家丨家政服务丨预约上门丨系统开发稳定版,家政服务丨预约上门丨东郊到家系统开发(开发案例)及源码技术
 随着人们对家政服务的需求不断增加,家政服务行业也变得越来越受欢迎。家政服务小程序和家政服务系统是两种常见的家政服务解决方案,它们可以为人们提供更加便捷和高效的家政服务体验。下面将详细介绍这两种解决方案。
预约按摩小程序开发,为什么很多上门按摩平台根本招聘不到优秀技师?
上门按摩平台面临招不到优秀技师的问题,主要原因是平台众多,技师选择多样。为解决此问题,平台可引入技师等级制度,根据订单数量和好评率划分高、低等级技师。高等级技师可享受70%-90%的高提成及首页推荐,这不仅能激励技师的积极性,还能帮助平台筛选出优质技师,提升服务质量和口碑,形成良性循环。
家政维修系统开发,2000的全行业预约到家系统,究竟怎么样?
在这个创业热潮中,一款全行业预约到家系统脱颖而出,仅需 2000 元即可拥有。无论是上门做饭、家政、维修等各类服务,它都能一站式解决。系统支持多种运营模式,配备后台管理、用户小程序和师傅 APP,功能完善,从准备到上线只需 3-5 天,助你快速抢占市场先机。
家政服务小程序APP开发,做好上门家政最快的方法是什么?
在家政服务领域,打造成功的平台并非易事。本文分享了三个关键步骤:避免初期盲目投入、采用低成本获客方式、建立有效的阿姨筛选机制。遵循这些方法,可助你避开常见陷阱,成为行业头部平台。
|
5月前
|
供应链 安全 数据挖掘
外卖跑腿系统开发详情丨校园外卖跑腿系统开发指南
开发外卖跑腿系统旨在服务于外卖平台和跑腿服务商,实现用户下单、骑手接单及订单管理等功能。系统包括用户端应用(注册、下单、支付等)、商家管理(菜单更新、订单处理)、骑手端应用(任务接收、配送)以及实时订单管理。此外,系统支持多种支付方式、订单结算、评价反馈机制、数据统计报表和客户服务,确保交易安全、提升效率并优化用户体验。
|
5月前
|
SQL JSON 算法
技术经验解读:元旦三天假期,实现一个电商退单管理系统【三】
技术经验解读:元旦三天假期,实现一个电商退单管理系统【三】
32 0
|
6月前
|
开发框架 前端开发 JavaScript
【校园快递信息系统—(1),36岁老码农现身说法
【校园快递信息系统—(1),36岁老码农现身说法
|
6月前
|
小程序 安全 搜索推荐
​ C#掌上医院预约挂号系统源码 一套成熟的医院/诊所预约挂号小程序需要了解哪些方面?
"互联网+医院"服务水平不断的提升,各类门诊部、中医馆、诊所、乡镇卫生院、社区卫生服务中心等医疗机构服务形式开始拓展互联网线上渠道。利用微信小程序、公众号(服务号)+网页端等形式进行开发,其中,预约小程序,门诊预约挂号系统的稳定性强、运行维护方便、是被众多医院机构信任与选择的。
90 1
|
6月前
|
前端开发 安全 JavaScript
疫情隔离区订餐系统的开发
疫情隔离区订餐系统的开发
|
6月前
|
前端开发 Java 关系型数据库
基于SSM开发实现校园疫情防控管理系统
基于SSM开发实现校园疫情防控管理系统
下一篇
无影云桌面