首页 > 其他分享 >超市订单管理系统的设计与实现

超市订单管理系统的设计与实现

时间:2022-12-22 10:55:43浏览次数:49  
标签:return 管理系统 value 超市 订单 import response data public


管理系统实战-前后端分离

第一章 项目需求分析和技术架构

1.1 项目需求

订单管理系统采用数据化管理订单、管理商品进销、供应商信息维护、员工管理等加快对店铺运营效率。 项目涉及功能模块:订单管理、供应商管理、商品管理、员工管理。

1.2 什么是前后端分离开发

传统系统架构:

  1. 前端工程师负责编写HTML页面,完成前端页面设计。

  2. 后端工程师使用模板技术将HTML页面代码转换为 JSP 页面,同时内嵌后端代码 (如Java);

前后端强依赖,后端必须要等前端的HTML开发好才能套转换成JSP。如果需求变更,前端HTML要改,后端

JSP也要跟着变, 这是件紧紧牵绊的事,使得开发效率降低。

  1. 产品交付时,要将前后端代码全部进行打包,部署到同一服务器上,或者进行简单的动静态分离部署。

前后端分离架构:

  1. 前后端约定好API接口&数据&参数

  2. 前后端并行开发

前端工程师只需要编写HTML页面,通过HTTP请求调用后端提供的接口服务即可。后端只要愉快的开发接口就行了。

无强依赖,如果需求变更,只要接口和参数不变,就不用两边都修改代码,开发效率高。

  1. 除了开发阶段分离、在运行期前后端资源也会进行分离部署。

 

前后端分离已成为互联网项目开发的业界标准使用方式。传统的前后端混合开发模式,虽然久经考验,到现在依然 还是能够支撑起应用的开发。但是放眼未来,社会分工更加精细化,前后端分离开发的精细化也是必然趋势。并且 前后端分离会为以后的大型分布式架构、微服务架构、多端化服务(多种客户端,例如:浏览器,安卓,IOS, 车载终端等等)打下坚实的基础。

 

1.3 项目前端技术架构

 

第二章 项目环境搭建

2.1 基于 Vue-CLI 3.x 创建项目

2.1.1 Vue CLI 安装

  1. 全局安装 Vue-CLI

npm install -g  vue 
npm install -g @vue/[email protected]
  1. 安装成功后,在命令行可以使用 vue 命令, 比如 查看版本

# 大写V
vue -V

2.1.2 Vue CLI 创建项目

创建项目命令:vue create 项目名称 会员管理系统 (Member Management System, 简称 MMS )

vue   create  ssm

 

2.1.3 启动项目测试

启动项目:

npm run serve

 

2.2 初始化项目

2.2.1 更改标题

找到 public\index.html 页面,修改 title 内容: 超市订单管理系统

 

 

2.2.2 配置 vue.config.js

在 ssm 根目录下创建 vue.config.js ,添加如下配置:

module.exports = {
    devServer: {
        port: 8888, // 端口号,如果端口号被占用,会自动提升1
        host: "localhost", //主机名, 127.0.0.1,  真机 0.0.0.0
        https: false, //协议
        open: true, //启动服务时自动打开浏览器访问
        proxy: { // 开发环境代理配置
            // '/dev-api': {
            [process.env.VUE_APP_BASE_API]: {
                // 目标服务器地址,代理访问 http://localhost:8001
                target: process.env.VUE_APP_SERVICE_URL,
                changeOrigin: true, // 开启代理服务器,
                pathRewrite: {
                    // /dev-api/db.json 最终会发送 http://localhost:8001/db.json
                    // 将 请求地址前缀 /dev-api 替换为 空的,
                    // '^/dev-api': '',
                    ['^' + process.env.VUE_APP_BASE_API]: ''
                }
            }
        }
    },
​
    lintOnSave: false, // 关闭格式检查
    productionSourceMap: false, // 打包时不会生成 .map 文件,加快打包速度 
}

 

添加配置环境

 

.env.development内容如下

# 只有以 VUE_APP_ 开头的变量会被 webpack 静态嵌入到项目中进行使用 process.env.VUE_APP_xxxxxx


# 目标服务接口地址,这个地址是按照你自已环境来的, 添加 或者更改配置后,需要重启服务
VUE_APP_SERVICE_URL = 'http://localhost:8081'

# 开发环境的前缀
VUE_APP_BASE_API = '/dev-api'
    
.env.production内容差不多,将dev改成pro就考研了

2.2.3 整合第三方库

  1. 安装 axios , 处理异步请求

npm i -S axios

2.3 整合 ElementUI

2.3.1 ElementUI 简介

Element 是饿了么平台推出的一套基于 Vue.js 开发的后台页面组件库。 官网:Element - The world's most popular Vue UI framework

 

2.3.2 ElementUI 安装

将 element-ui 模块通过本地安装为生产依赖。在 ssm 目录下的命令行窗口,输入以下命令:

npm i -S element-ui

2.3.3 完整引入 ElementUI

在 ssm\src\main.js 中导入 element-ui 和 element-ui/lib/theme-chalk/index.css , 使用 Vue.use(ElementUI)

import Vue from "vue";
import ElementUI from 'element-ui'; // 组件库
import 'element-ui/lib/theme-chalk/index.css'; // 样式
import App from "./App.vue";
// 使用 ElementUI
Vue.use(ElementUI);

Vue.config.productionTip = false

new Vue({
    router,
    render: h => h(App),
}).$mount('#app')

 

第三章 项目布局

 

3.1 在component添加三个文件夹分别是:AppHeader,AppMain,AppNavbar和一个Layout.vue

 

3.1.1 AppHeader内容是

<template>
    <div class="header">
        <a href="#/">
            <img class="logo" src="@/assets/logo.png" width="25px">
            <span class="company">超市订单管理系统</span>
        </a>   
        <el-dropdown>
             <div class="avatar-wrapper">
              <img src="https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif" class="user-avatar">
           <i class="el-icon-caret-bottom"/>
      </div>
            <el-dropdown-menu slot="dropdown">
                <el-dropdown-item icon="el-icon-edit" command="a">修改密码</el-dropdown-item>
                <el-dropdown-item icon="el-icon-s-fold" command="b">退出系统</el-dropdown-item>
            </el-dropdown-menu>
        </el-dropdown>

  
    </div>
</template>

<script>


export default {

}
</script>

<style scoped>
.user-avatar {
        vertical-align:middle;
        width: 40px;
        height:40px;
        border-radius: 10px;
      }
.logo{
    vertical-align: middle;
    padding: 0px 10px 0 40px;
}
.company {
    position: absolute;
    color: white;
}

/* 下拉菜单  */
.el-dropdown {
    float: right;
    margin-right: 40px;
}
.el-dropdown-link {
    color: white;
    cursor: pointer;
}
</style>

3.1.2 AppMain内容是

<template>
   <div class="main">
         这里是展示数据地方
      <!-- <router-view></router-view> -->
   </div>
</template>

<script>

</script>

3.1.3 AppNavbar内容是

<template>
    <div class="navbar">

        <!--  default-active : 默认选中的菜单
            :router="true" true表示开启路由模式,开启之后, index值代表的就是路由地址
          -->
     <el-menu
      :router="true"
      :default-active="$route.path"
      class="el-menu-vertical-demo"
      background-color="#545c64"
      text-color="#fff"
      active-text-color="#ffd04b">
      <el-menu-item index="/home">
        <i class="el-icon-s-home"></i>
        <span slot="title">首页</span>
      </el-menu-item>
      <el-menu-item index="/bill/">
        <i class="el-icon-user-solid"></i>
        <span slot="title">订单管理</span>
      </el-menu-item>
      <el-menu-item index="/provider/">
        <i class="el-icon-s-cooperation"></i>
        <span slot="title">供应商管理</span>
      </el-menu-item>
      <el-menu-item index="/user/">
        <i class="el-icon-s-goods"></i>
        <span slot="title">用户管理</span>
      </el-menu-item>
      <el-menu-item index="/user-update/">
        <i class="el-icon-user"></i>
        <span slot="title">密码修改</span>
      </el-menu-item>
    </el-menu>

    </div>
</template>


<style scoped>
.el-menu {
    border-right: none;
       
}

</style>

3.2 布局layout.vue的内容

<template>
  <div>
    <app-header></app-header>
    <app-navbar></app-navbar>
    <app-main></app-main>
  </div>
</template>
<script>
// 会导入 ./AppHeader 下面的 index.vue组件
import AppHeader from './AppHeader'
import AppNavbar from './AppNavbar'
import AppMain from './AppMain'

export default {
    components: {AppHeader, AppNavbar, AppMain}
}
</script>
<style scoped>
/* 头部区域 */
.header {
    position: absolute;
    line-height: 50px;
    top: 0px;
    left: 0px;
    right: 0px;
    background-color: #2d3a4b
}
.navbar {
    position: absolute;
    width: 230px;
    top: 50px;
    left: 0px;
    bottom: 0px;
    overflow-y: auto;
    background-color: #545c64;
}
.main {
    position: absolute;
    top: 50px;
    left: 230px;
    right: 0px;
    bottom: 0px;
    padding: 10px;
    overflow-y: auto;
    /* background-color: red; */
}
</style>

3.3 添加路由 在src下创建router.js

安装 vue-router

import Vue from "vue";
import Router from "vue-router";
import Layout from '@/components/Layout.vue'

Vue.use(Router);

export default new Router({
    routes: [{
            path: '/',
            name: 'layout', //路由名称
            component: Layout, //组件对象

        },

    ]
})

3.4加入管理

 

3.5启动测试

 

第四章 Axios 封装和跨域问题

4.1 封装 Axios 对象

因为项目中很多组件中要通过 Axios 发送异步请求,所以封装一个 Axios 对象。自已封装的 Axios 在后续可以使用

安装

npm install axios

axios 中提供的拦截器。

  1. 在 src 目录下创建 utils 目录及 utils 下面创建 request.js 文件

 

内容如下

import axios from 'axios'
import { Loading, Message } from 'element-ui';


const loading = {
    loadingInstance: null, // Loading 实例
    // 打开加载
    open: function() {
        // 创建实例,而且会打开加载 窗口
        if (this.loadingInstance === null) {
            this.loadingInstance = Loading.service({
                target: '.main',
                text: '系统加载中...',
                background: 'rgba(0, 0, 0, 0.5)'
            })
        }

    },
    // 关闭加载
    close: function() {
        // 不为空时, 则关闭加载窗口
        if (this.loadingInstance !== null) {
            this.loadingInstance.close()
        }
        this.loadingInstance = null

    }
}


const request = axios.create({

    // baseURL: '/dev-api', 
    baseURL: process.env.VUE_APP_BASE_API,
    // baseURL: '/',
    timeout: 5000 // 请求超时,5000毫秒
})

// 请求拦截器
request.interceptors.request.use(config => {
    // 打开加载窗口
    loading.open()

    return config
}, error => {
    // 关闭加载窗口
    loading.close()
        // 出现异常
    return Promise.reject(error);
})

// 响应拦截器
request.interceptors.response.use(response => {
    // 关闭加载窗口
    loading.close()
    const resp = response.data

    // 后台正常响应的状态,如果不是 2000, 说明后台处理有问题
    if (resp.code !== 2000) {
        Message({
            message: resp.message || '系统异常',
            type: 'warning',
            duration: 5 * 1000
        })
    }

    // return response.data // 可以在这里统一的获取后台响应的数据进行返回,而这里面就没有请求头那些
    return response
}, error => {
    // 关闭加载窗口
    loading.close()
    Message({
        message: error.message,
        type: 'error',
        duration: 5 * 1000
    })
    return Promise.reject(error);
})



export default request // 导出自定义创建 axios 对象

2 在 src 目录下创建 utils 目录及 utils 下面创建 auth.js 文件

 

const TOKEN_KEY = 'ssm-token'
const USER_KEY = 'ssm-user'

// 获取 token
export function getToken() {
    return localStorage.getItem(TOKEN_KEY)
}
// 保存 token
export function setToken(token) {
    return localStorage.setItem(TOKEN_KEY, token)
}
// 获取用户信息
export function getUser() {
    return JSON.parse(localStorage.getItem(USER_KEY))
}
//保存用户信息
export function setUser(user) {
    localStorage.setItem(USER_KEY, JSON.stringify(user))
}
//移除用户信息
export function removeToken() {
    localStorage.removeItem(TOKEN_KEY)
    localStorage.removeItem(USER_KEY)
}

4.2添加状态管理

4.2.1创建用户信息管理

 

4.2.2 在main.js添加

 

4.2.3 安装vues

npm install vuex --save

4.2.4 index.js的内容

import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'

Vue.use(Vuex)

const store = new Vuex.Store({
    modules: {
        user
    }
})

export default store

 

4.2.4 user.js信息内容

import {getToken, setToken, setUser, getUser, removeToken} from '@/utils/auth'
import { login, getUserInfo, logout} from '@/api/login'


const user = {
    state: {
        token: getToken(), // getToken() 作为token初始值,解决刷新页面之后token为null
        user: getUser()
    },

    mutations: {
        SET_TOKEN(state, token) {
            state.token = token
            setToken(token)
        },
        SET_USER(state, user) {
            state.user = user
            setUser(user)
        }
    },

    actions: {
        // 登录获取token
        Login({commit}, form) {
            // resolve 触发成功处理, reject 触发异常处理
            return new Promise((resolve, reject) => {

                login(form.username.trim(), form.password).then(response => {
                    const resp = response.data // 获取到的就是响应的data数据
                    commit('SET_TOKEN', resp.data.token)
                    // 通过组件已经将token更新成功
                    resolve(resp)
                }).catch(error => {
                    reject(error)
                })

            })
            
        },

        // 通过token获取用户信息
        GetUserInfo({commit, state}) {
            
            return new Promise((resolve, reject) => {
                getUserInfo(state.token).then(response => {
                    const respUser = response.data
                    commit('SET_USER', respUser.data)
                    resolve(respUser)
                }).catch(error => {
                    reject(error)
                })
            })
            
        },

        // 退出
        Logout({commit, state}) {
            return new Promise((resolve, reject) => {
                logout(state.token).then(response => {
                    const resp = response.data
                    commit('SET_TOKEN', '')
                    commit('SET_USER', null) 
                    removeToken()
                    resolve(resp)
                }).catch(error => {
                    reject(error)
                })
            })
        }
    }


}

export default user

4.3添加登录功能测试

 

4.3.1 login.js内容

import request from '@/utils/request'


export function login(username, password) {
    return request({ // Promise
        url: '/user/login',
        method: 'post',
        data: {
            username, // username: username
            password
        }
    })
}

export function getUserInfo(token) {
    return request({
        url: `/user/info/${token}`,
        method: 'get'
    })
}

export function logout(token) {
    return request({
        url: `/user/logout`,
        method: 'post',
        data: {
            token //token: token
        }
    })
}

4.3.2 在compoments 下面的AppMain下面的inde.vue测试

 

 

内容如下:

<template>
   <div class="main">
       <p><button @click="log">add</button></p>
      <router-view></router-view>
   </div>
</template>

<script>
export default {
   data(){
        return {
          loading,
      loginForm: {
        username: 'admin',
        password: 'admin'
      },    
   }
  }
  ,
    methods: {
      // 提交登录
    log:function () {
        //调用登录方法
     this.$store.dispatch('GetUserInfo', this.loginForm).then((resp) => {
           this.loading=true
           console.log("-------",resp.data)
          }).catch(() => {
            this.loading = false
          })
      },

    }
}
</script>

第五章 系统登录和退出管理

5.1 需求分析

开发登录页面,当输入帐号和密码验证通过后,才允许进行到首页。效果图如下

5.2 路由配置

  1. 在 src\views 目录下新建 login 目录及此目录下新建文件 index.vue 说明:通过 import Login from './views/login' 导入组件,当前只指定了组件路径,默认导入的就是指定路径 下的 index.vue 组件

 

  1. 在 src\router.js 中配置路由(把原有的路由配置删除),如下:

 

import Vue from "vue";
import Router from "vue-router";
import Layout from '@/components/Layout.vue'
import Login from '@/views/login'
Vue.use(Router);

export default new Router({
    routes: [

        {
            // 登录页
            path: '/login',
            name: 'login', //路由名称
            component: Login
        },
        {

            path: '/',
            name: 'layout', //路由名称
            component: Layout, //组件对象

        },

    ]
})

5.3 登录页面

<template>
  <div class="login-container">
      <el-form ref="form" :rules="rules" :model="form" label-width="80px" class="login-form">
          <h2 class="login-title">超市订单管理系统</h2>
          <el-form-item label="帐号" prop="username">
              <el-input v-model="form.username"></el-input>
          </el-form-item>
          <el-form-item label="密码" prop="password">
              <el-input v-model="form.password" type="password"></el-input>
          </el-form-item>
          <el-form-item>
              <el-button type="primary" @click="submitForm('form')">登录</el-button>
          </el-form-item>
      </el-form>
  </div>
</template>

<script>
import {login, getUserInfo} from '@/api/login'

  export default {
    data() {
      return {
        form: {
            username: '',
            password: ''
         },
         rules: {
             username: [
                  {required: true, message: '帐号不能为空', trigger: 'blur' },
             ],
             password: [
                 {required: true, message: '密码不能为空', trigger: 'blur' },
             ]
         }
      }
    },
    methods: {
        // 注意:按钮上调用的函数名要一致 submitForm
      submitForm(formName) {
        this.$refs[formName].validate((valid) => {
          if (valid) {
            // 验证帐号和密码是否通过,
            login(this.form.username, this.form.password).then(response => {
              const resp = response.data
              console.log(resp.code, resp.message, resp.data.token, resp.code === 2000)
              if (resp.message) {
                // 通过,获取用户信息 异步请求
                getUserInfo(resp.data.token).then(response => {
                  // 存入session中
                  const respUser = response.data
                  if (respUser.message) {
                    // 将信息保存到浏览器的 localStorage 中
                    localStorage.setItem('ssm-user', JSON.stringify(respUser.data))
                    // 方便后面重新验证
                    localStorage.setItem('ssm-token', resp.data.token)
                    
                    // 前往首页
                    this.$router.push("/")
                 }else {
                    // 获取信息失败, 弹出警告
                        this.$message({
                            message: response.message,
                            type: 'waring'
                        })
                    }
                }).catch(error => {

                })
            }else{
                console.log('验证失败')
                return false
            }
        })
        
    }
        })
      }
  }
}
</script>

<style scoped>
.login-form {
    width: 350px;
    /* 上下间隙 160px, 左右自动居中 */
    margin: 160px auto;
    background-color: rgb(255,255,255,0.8);
    padding: 28px;
    border-radius: 20px;
}
.login-container {
    position: absolute;
    width: 100%;
    height: 100%;
    background: url('../../assets/login.jpg')
}
.login-title {
    color: #303133;
    text-align: center;
}
</style>

用户校验工具匹对校验

 

// 校验用户名是否合法 只允许4-30位中文、数字、字母和下划线
export function isvalidUsername(str) {
  const valid_map = /^[a-zA-Z0-9_\u4e00-\u9fa5]{4,30}$/
  return valid_map.test(str)
}

// 校验手机号是否合法
export function isvalidMobile(str) {
  const valid_map = 11 && /^1(3|4|5|6|7|8|9)\d{9}$/
  return valid_map.test(str)
}
  
// 校验邮箱是否合法
export function isvalidEmail(str) {
  const valid_map = /^[A-Za-z0-9_.-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/
  return valid_map.test(str)
}

/* 合法uri*/
export function validateURL(textval) {
  const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
  return urlregex.test(textval)
}

第二个登录页面

<template>
  <div class="login_page">
    <div class="login_box">
      <div class="center_box">
        <!-- 登录&注册-->
        <div :class="{login_form: true, rotate: tab == 2}">
          <div :class="{tabs: true, r180: reverse == 2}">
            <div class="fl tab" @click="changetab(1)">
              <span :class="{on: tab == 1}">登录</span>
            </div>
            <div class="fl tab" @click="changetab(2)">
              <span :class="{on: tab == 2}">注册</span>
            </div>
          </div>
          
          <!-- 登录 -->
          <div class="form_body" v-if="reverse == 1">
            <!-- submit.prevent 阻止默认表单事件提交,采用loginSubmit -->
            <form  @submit.prevent="loginSubmit">
              <input type="text" v-model="loginData.username" placeholder="请输入用户名" autocomplete="off">
              <input type="password" v-model="loginData.password" placeholder="请输入密码" autocomplete="off">
              <div class="error_msg">{{loginMessage}}</div>
              <input type="submit" v-if="subState" disabled="disabled" value="登录中···" class="btn" />
              <input type="submit" v-else value="登录" @submit="loginSubmit" class="btn" />
            </form>
          </div>


          <!-- 注册 -->
          <div class="form_body r180" v-if="reverse == 2">
            <form @submit.prevent="regSubmit">
              <input type="text" v-model="registerData.username" placeholder="请输入用户名" autocomplete="off">
              <input type="password" v-model="registerData.password" placeholder="6-30位密码,可用数字/字母/符号组合" autocomplete="off">
              <input type="password" v-model="registerData.repPassword" placeholder="确认密码" >
              <div class="error_msg">{{regMessage}}</div>
              <div class="agree">
                <input type="checkbox" id="tonyi" v-model="registerData.check">
                <label for="tonyi">我已经阅读并同意</label><a href="jvascript:;"  @click="xieyi = true">《用户协议》</a>
              </div>
              <input type="submit" v-if="subState" disabled="disabled" value="提交中···" class="btn">
              <input type="submit" v-else value="注册" class="btn">
            </form>
          </div>
        </div>
      </div>
    </div>

    <!-- 用户协议 -->
    <div class="xieyi" v-if="xieyi" @click.self="xieyi = false">
      <div class="xieyi_content">
        <div class="xieyi_title">请认真阅读用户协议</div>
        <div class="xieyi_body">123
        </div>
        <input type="button" class="xieyi_btn" value="确定" @click="xieyi = false">
      </div>
    </div>
  </div>
</template>
<script >
import  {isvalidUsername} from  '@/utils/validate'
import  {getXieyi,getUserUsername,register} from '@/api/login'
export default {

    data () {
      return {
        tab:  1, // 高亮当前标签名
        reverse:  1, // 旋转 1 登录,2 注册
        loginMessage: '', //登录错误提示信息
        regMessage: '', //注册错误提示信息
        subState: false, //提交状态
        xieyi: false, // 显示隐藏协议内容
        xieyiContent: null, // 协议内容
        redirectURL: '//www.zpark.com.cn', // 登录成功后重写向地址


        loginData: { // 登录表单数据
            username: '',
            password: ''
        },

        registerData: { // 注册表单数据
            username: '',
            password: '',
            repPassword: '',
            check: false
        },
      }
    },


async  created () {
    if(this.$route.query.redirectURL){
      this.redirectURL = this.$route.query.redirectURL
    }
    //获取协议
  this.xieyiContent= await  getXieyi();
 

},
    methods: {

      // 切换标签
      changetab (int) {
          this.tab = int;
          let _that = this;
          setTimeout(() => {
            this.reverse = int
          }, 200)
      },

      // 提交登录
      loginSubmit() {

        if(!isvalidUsername(this.loginData.username)){
           this.loginMessage= '用户名必须是4-30位中文、数字、字母和下划'
           return  false
        }

        if(this.loginData.password.length < 6){
           this.loginMessage= '密码必须是大于6位数'
           return  false
        }

        //调用登录方法
       this.$store.dispatch("UserLogin",this.loginData).then(response=>{
           const {code,message} = response
           if(code ==  20000){
             window.location.href=this.redirectURL
           }else{
              this.loginMessage= message
           }
           this.subState= false //提交完成
            
       }).catch(error=>{
         this.subState =  false
         this.loginMessage= '系统繁忙'
       })
      

      },

      // 提交注册
   async   regSubmit() {
     //判断是否别注册
       const {code,message,data}=  await  getUserUsername(this.registerData.username);
       if(code !== 20000){
         this.regMessage=message
         return  false
       }
     if(data){
       this.regMessage='该用户已经被注册,请换一个'
       return  false
     }
     //判断用户名
     if(!isvalidUsername(this.registerData.username)){
           this.regMessage= '用户名必须是4-30位中文、数字、字母和下划'
           return  false
        }
//判断密码
    if(this.registerData.password.length < 6 || this.registerData.password.length >30){
           this.regMessage= '密码必须是大于6位,并且小于30位'
           return  false
        }
    
//判断两次密码是否相同
    if(this.registerData.password  !== this.registerData.repPassword){
           this.regMessage= '你输入的两次密码不相同'
           return  false
        }

    if(!this.registerData.check){
        this.regMessage='请选择协议'
         return  false
    }

   this.subState= true  //注册中
   //调用注册的方法进行注册
       register(this.registerData).then(response=>{
         this.subState= false
         const  {code,message} =response
         if(code ==  20000){
            this.changetab(1)
         }else{
             this.regMessage=message
         }
       }).catch(errpr=>{
         this.subState =false
         this.regMessage = '系统繁忙'
       })
   



      }

    },
}
</script>
<style scoped>
@import '../../assets/style/login.css'; 
</style>

 

 

 

5.4 权限&退出

<template>
    <div class="header">
        <a href="#/">
            <img class="logo" src="@/assets/logo.png" width="25px">
            <span class="company">超市订单管理系统</span>
        </a>
        

        <el-dropdown @command="handleCommand">
             <div class="avatar-wrapper">
                <img :src="user.avatar+'?imageView2/1/w/70/h/70'" class="user-avatar"> 
           <i class="el-icon-caret-bottom"/>
      </div>
            <el-dropdown-menu slot="dropdown">
                <el-dropdown-item icon="el-icon-edit" command="a">修改密码</el-dropdown-item>
                <el-dropdown-item icon="el-icon-s-fold" command="b">退出系统</el-dropdown-item>
            </el-dropdown-menu>
        </el-dropdown>

        <!-- 修改密码 -->
        <el-dialog title="修改密码" :visible.sync="dialogFormVisible" width="400px">
            <el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" style="width: 300px">
                <el-form-item label="原密码" prop="oldPass">
                    <el-input type="password" v-model="ruleForm.oldPass" autocomplete="off"></el-input>
                </el-form-item>
                <el-form-item label="新密码" prop="pass">
                    <el-input type="password" v-model="ruleForm.pass" autocomplete="off"></el-input>
                </el-form-item>
                <el-form-item label="确认密码" prop="checkPass">
                    <el-input type="password" v-model="ruleForm.checkPass" autocomplete="off"></el-input>
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
                    <el-button @click="$refs['ruleForm'].resetFields()">重置</el-button>
                </el-form-item>
            </el-form>
        </el-dialog>
    </div>
</template>

<script>
import {logout} from '@/api/login'
// import passwordApi from '@/api/password'


export default {

    data() {

        // 在return 上面进行申明自定校验
        const validateOldPass = (rule, value, callback) => {
            // console.log(this.user.id)
            passwordApi.checkPwd(this.user.id, value).then(response => {
                const resp = response.data
                if(resp.flag) {
                    // 验证通过
                    callback()
                }else {
                    callback(new Error( resp.message ))
                }
            })
        }

        // 校验确认密码是否一致
        const validatePass = (rule, value, callback) => {
            // value 代表 checkPass
            if(value !== this.ruleForm.pass) {
                callback(new Error('两次输入的密码不一致'))
            }else {
                // 相等,则通过
                callback()
            }
        }

        // 注意:在 return 上面,而上面不能使用 逗号 , 结束 
        return {
            user: this.$store.state.user.user,
            dialogFormVisible: false,
            ruleForm: {
                oldPass: '',
                pass: '',
                checkPass: ''
            },
            rules: {
                oldPass: [
                    {required: true, message: '原密码不能为空', trigger: "blur"},
                    { validator: validateOldPass, trigger: 'blur' }
                ],
                pass: [
                    {required: true, message: '新密码不能为空', trigger: "blur"}
                ],
                checkPass: [
                    {required: true, message: '确认密码不能为空', trigger: "blur"},
                    { validator: validatePass, trigger: 'change' }
                ]
            }
        }
    },

    methods: {
        handleCommand(command) {
            switch (command) {
                case 'a':
                    // 打开修改密码窗口
                    this.handlePwd()
                    break;
                case 'b':
                    // 退出系统
                    this.handleLogout()
                    break;
                default:
                    break;
            }
        },

        // 退出系统
        handleLogout() {
            this.$store.dispatch('Logout').then(response => {
                if(response.message) {
                    // 退出成功
                    // 回到登录页面
                    this.$router.push('/login')
                }else {
                    this.$message({
                        message: resp.message,
                        type: 'warning',
                        duration: 500 // 弹出停留时间
                    });
                }
            })
            
        },

        // 打开修改密码窗口
        handlePwd(){
            this.dialogFormVisible = true
            this.$nextTick(() => {
                this.$refs['ruleForm'].resetFields()
            })
        },
        // 修改密码
        submitForm(formName) {
            this.$refs[formName].validate(valid => {
                if(valid) {
                   console.log('校验成功')  
                   passwordApi.updatePwd(this.user.id, this.ruleForm.checkPass).then(response => {
                       const resp = response.data
                       // 不管失败还是成功,都进行提醒
                       this.$message({
                           message: resp.message,
                           type: resp.flag ? 'success': 'warning'
                       })

                       if(resp.flag) {
                           // 更新成功, 退出系统,回到登录页面
                           this.handleLogout()
                           // 关闭窗口
                           this.dialogFormVisible = false
                       }
                   })  
                }else {
                    return false
                }
            })
        }
    }
}
</script>

<style scoped>
.user-avatar {
        vertical-align:middle;
        width: 40px;
        height:40px;
        border-radius: 10px;
      }
.logo{
    vertical-align: middle;
    padding: 0px 10px 0 40px;
}
.company {
    position: absolute;
    color: white;
}

/* 下拉菜单  */
.el-dropdown {
    float: right;
    margin-right: 40px;
}
.el-dropdown-link {
    color: white;
    cursor: pointer;
}
</style>

权限一个 permission.js

 

代码如下

/**
 * 权限校验:
 *  Vue Router中的 前置钩子函数 beforeEach(to, from, next)
 * 当进行路由跳转之前,进行判断 是否已经登录 过,登录过则允许访问非登录页面,否则 回到登录页
 * 
 * to:  即将要进入的目标路由对象
 * from: 即将要离开的路由对象
 * next: 是一个方法,可以指定路由地址,进行路由跳转
 */

import router from './router'

router.beforeEach((to, from, next) => {
    // 1. 获取token
    const token = localStorage.getItem('ssm-token')
        // const token = store.state.user.token
    console.log('token', token)

    if (!token) {
        // 1.1 如果没有获取到,
        // 要访问非登录页面,则不让访问,加到登录页面 /login
        if (to.path !== '/login') {
            next({ path: '/login' })
        } else {
            // 请求登录页面 /login
            next()
        }
    } else {
        // 1.2 获取到token, 
        // 1.2.1 请求路由 /login ,那就去目标路由
        if (to.path === '/login') {
            next()
        } else {
            // 1.2.2 请求路由非登录页面,先在本地查看是否有用户信息,
            const userInfo = localStorage.getItem('ssm-user')
                //   const userInfo = store.state.user.user
            console.log('userInfo', userInfo)
            if (userInfo) {
                // 本地获取到,则直接让它去目标路由
                next()
            } else {
                console.log('获取用户信息')
                    // 如果本地没有用户信息, 就通过token去获取用户信息,
                store.dispatch('GetUserInfo').then(response => {
                    if (response.flag) {
                        next()
                    } else {
                        next({ path: '/login' })
                    }
                }).catch(error => {

                })
            }

        }
    }


})

在main.js中进行拦截

 

  1. 在未登录情况下访问 http://localhost:8888/#/home ,会回到 登录

  2. 只有登录后,才可以访问首页

第六章 订单管理

6.1 列表查询

 

6.1.1 需求分析

订单管理主要针对订单信息进行管理,首先开发订单管理模块中的列表功能,包含条件查询、下拉框、日期功能、 数据列表、分页

6.1.2后端实现

 

6.1.2.1 创建ssm项目导入相关依赖

    <dependencies>
          <!--web启动依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring整合mybatis依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <!--mybatis-plus--> <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.5</version>
      </dependency>
        <!--mysql-->
        <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- Swagger -->
        <dependency>
            <groupId>com.spring4all</groupId>
            <artifactId>swagger-spring-boot-starter</artifactId>
            <version>1.9.1.RELEASE</version>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--tomcat依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

6.1.2.2 编写配置类 application.properties

#mysql 数据库连接
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/smbms?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#FieldStrategy 有三种策略:
#IGNORED:0 忽略
#NOT_NULL:1 非 NULL,默认策略
#NOT_EMPTY:2 非空
#mybatis-plus.global-config.db-config.field-strategy=ignored
#此为默认值,如果你的默认值和 mp 默认的一样,该配置可无
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
#环境设置:dev、test、prod
#全局设置主键生成策略
#mybatis-plus.global-config.db-config.id-type=auto
mybatis-plus.configuration.map-underscore-to-camel-case=false
#扫描包:别名
mybatis-plus.type-aliases-package=cn.zpark.pojo
#加载xml
mybatis-plus.mapper-locations=classpath:cn/zpark/mapper/*Mapper.xml

 

6.1.2.3 创建相关配置类

正在上传…重新上传取消

MpConfig

@EnableTransactionManagement
@Configuration
@MapperScan("cn.zpark.mapper")
public class MpConfig {
    //乐观锁插件
    @Bean
    public OptimisticLockerInterceptor  optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }



    /*分页插件*/
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    //逻辑删除插件
    @Bean
    public ISqlInjector sqlInjector() {
        return new LogicSqlInjector();
    }
}

SwaggerConfig

@Configuration//配置类
@EnableSwagger2 //swagger注解
public class SwaggerConfig {
@Bean
public Docket webApiConfig(){
    return new Docket(DocumentationType.SWAGGER_2)
        .groupName("webApi")
        .apiInfo(webApiInfo())
        .select()
        .paths(Predicates.not(PathSelectors.regex("/admin/.*")))
        .paths(Predicates.not(PathSelectors.regex("/error.*")))
        .build();
        }
    private ApiInfo webApiInfo(){
    return new ApiInfoBuilder()
        .title("API文档介绍")
        .description("本文档描述接口定义 ")
        .contact(new Contact("java", "http://ssm.com", "[email protected]"))
        .build();
        }
    }

创建 MyMetaObjectHandler

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    //使用mp实现添加操作,这个方法执行
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("creationDate", new Date(), metaObject);
        this.setFieldValByName("modifyDate", new  Date(), metaObject);
        this.setFieldValByName("version",1,metaObject);
        this.setFieldValByName("deleted", 0, metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {

        this.setFieldValByName("modifyDate",new Date(),metaObject);
    }
}

编写实体

@Data
@TableName("smbms_bill")
public class Bill {
    @ApiModelProperty(value = "主键ID")
    private Integer id;// '主键ID',
    @ApiModelProperty(value = "账单编码")
    private String billCode;// '账单编码',
    @ApiModelProperty(value = "商品名称")
    private String productName;//'商品名称',
    @ApiModelProperty(value = "商品描述")
    private String productDesc;//'商品描述',
    @ApiModelProperty(value = "商品单位")
    private String productUnit;//'商品单位',
    @ApiModelProperty(value = "商品数量")
    private Double productCount;// '商品数量',
    @ApiModelProperty(value = "商品总额")
    private Double totalPrice;// '商品总额',
    @ApiModelProperty(value = "是否支付(1:未支付 2:已支付)")
    private Integer isPayment;// '是否支付(1:未支付 2:已支付)',,
    @ApiModelProperty(value = "创建时间")
    private Date creationDate;// COMMENT '创建时间',
    @ApiModelProperty(value = "更新者(userId)")
    private Integer modifyBy;// '更新者(userId)',
    @ApiModelProperty(value = "供应商ID")
    private Integer providerId;//'供应商ID',
    @ApiModelProperty(value = "供应商名称")
    private String proName;//供应商名称
    
    @ApiModelProperty(value = "创建时间",hidden = true)
    @TableField(value = "createdBy",fill = FieldFill.INSERT)
    private Integer createdBy;//'创建者(userId)'
    @ApiModelProperty(value = "修改时间",hidden = true)
    @TableField(value = "modifyDate",fill = FieldFill.INSERT_UPDATE)
    private Date modifyDate;// '更新时间',
    @Version
    @TableField(fill = FieldFill.UPDATE)
    @ApiModelProperty(value = "乐观锁的字段",hidden = true)
    private Integer version;//版本号
    @TableLogic
    @TableField(fill = FieldFill.INSERT)
    @ApiModelProperty(value = "逻辑删除字段",hidden = true)
    private Integer deleted;


}

编写条件封装类BillQuery去基础BaseRequest类

@Data
public class BillQuery   extends BaseRequest<Bill> {

    @ApiModelProperty(value = "商品名称,模糊查询")
    private String productName;

    @ApiModelProperty(value = "是否付款")
    private Integer isPayment;

    @ApiModelProperty(value = "供应商名称")
    private String proName;

}

编写BaseRequest类,做分页条件

@Accessors(chain = true)
@Data
public class BaseRequest<T> implements Serializable {
    @ApiModelProperty(value = "页码", required = true)
    private long current;
    @ApiModelProperty(value = "每页显示多少条", required = true)
    private long size;
    /**
     * 封装分页对象
     * @return
     */
    @ApiModelProperty(hidden = true) // 不在swagger接口文档中显示
    public IPage<T> getPage() {

        return new Page<T>().setCurrent(this.current).setSize(this.size);
    }

}

编写ResultCode类封装

public interface ResultCode {

    public static Integer SUCCESS = 2000; //成功

    public static Integer ERROR = 2001; //失败
}

创建R类返回结果

//统一返回结果的类
@Data
public class R {
    @ApiModelProperty(value = "是否成功")
    private Boolean success;
    @ApiModelProperty(value = "返回码")
    private Integer code;
    @ApiModelProperty(value = "返回消息")
    private String message;
    @ApiModelProperty(value = "返回数据")
    private Map<String, Object> data = new HashMap<String, Object>();
    //把构造方法私有
    private R() {}
    //成功静态方法
    public static R ok() {
        R r = new R();
        r.setSuccess(true);
        r.setCode(ResultCode.SUCCESS);
        r.setMessage("成功");
        return r;
    }

    //失败静态方法
    public static R error() {
        R r = new R();
        r.setSuccess(false);
        r.setCode(ResultCode.ERROR);
        r.setMessage("失败");
        return r;
    }

    public R success(Boolean success){
        this.setSuccess(success);
        return this;
    }

    public R message(String message){
        this.setMessage(message);
        return this;
    }

    public R code(Integer code){
        this.setCode(code);
        return this;
    }

    public R data(String key, Object value){
        this.data.put(key, value);
        return this;
    }

    public R data(Map<String, Object> map){
        this.setData(map);
        return this;
    }
}

 

编写接口BillMapper

public interface BillMapper extends BaseMapper<Bill> {

    public List<Bill> pageAll(BillQuery billQuery);
}

编写xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zpark.mapper.BillMapper">
    <select id="pageAll" resultType="bill" parameterType="billQuery">
        SELECT  *,p.proName  FROM `smbms_bill` b,`smbms_provider` p  WHERE  b.`providerId`=p.id
        <if test="proName!=null">
            and  p.proName=#{proName}
        </if>

        <if test="isPayment!=0  and  isPayment!=null">
            and  b.isPayment=#{isPayment}
        </if>

        <if test="productName!=null and productName!=''">
            and  b.productName like ("%"#{productName}"%")
        </if>
           limit #{current},#{size}




    </select>

    <insert id="save" parameterType="bill">
        INSERT INTO `smbms_bill`(billCode,productName,productUnit,productCount,totalPrice,isPayment,providerId)
        VALUES (#{billCode},#{productName},#{productUnit},#{productCount},#{totalPrice},#{isPayment},#{providerId})
    </insert>
     <select id="getById" resultType="bill">
         SELECT  *,p.proName   FROM `smbms_bill` b,`smbms_provider` p  WHERE  b.`providerId`=p.id  and b.id=#{id}

     </select>

<update id="update" parameterType="bill">
     update    `smbms_bill`  set   billCode=#{billCode},productName=#{productName},productUnit=#{productUnit},totalPrice=#{totalPrice},isPayment=#{isPayment},providerId=#{providerId}  where  id=#{id}
</update>
</mapper>

 

 

编写BillService接口

public interface BillService   extends IService<Bill> {
    public List<Bill> pageAll(BillQuery billQuery);
    public Integer selectCount();
}

编写实现类BillServiceImpl

//订单接口的实现类
@Service
public class BillServiceImpl   extends ServiceImpl<BillMapper,Bill>  implements BillService {
   //查询所有
    @Override
    public  Page<Bill> pageAll(BillQuery billQuery) {
        //创建page对象 ,设置当前页和每页显示多少条
        Page<Bill> pageBill = new Page<>(billQuery.getCurrent(),billQuery.getSize());
        //创建封装条件的对象
        QueryWrapper  queryWrapper=new QueryWrapper();
        //获取是否已付款的信息
        Integer isPayment = billQuery.getIsPayment();
        //获取商品名称
        String productName = billQuery.getProductName();
        //获取供应商名称
        String proName = billQuery.getProName();

        if (!StringUtils.isEmpty(productName)){
            queryWrapper.eq("productName",isPayment);
        }
        if (!StringUtils.isEmpty(isPayment)){
            queryWrapper.eq("isPayment",productName);
        }

        if (!StringUtils.isEmpty(proName)){
            queryWrapper.eq("proName",proName);
        }


        //排序
        queryWrapper.orderByDesc("billCode");


        /*
        0,10     1     1-1*10
        10,20    2     2-1*10

         */


      //设置分页条件,通过第几页设置确定要查询从那条数据开始
        billQuery.setCurrent(billQuery.getSize()*(billQuery.getCurrent()-1));
        //获取总条数
        pageBill.setTotal(selectCount());
       //调用查询方法进行查询
        List<Bill> all = baseMapper.findAll(billQuery);
        //将数据存储到分页中
        pageBill.setRecords(all);

        return pageBill;

    }
    //查询订单总条数
    @Override
    public Integer selectCount() {

        return baseMapper.selectCount(null);
    }
}

编写控制器

@RestController
@Api(description="用户登录")
@RequestMapping("/bill")
@CrossOrigin//解决跨域
public class BillController {
    @Autowired
    private BillService billService;

    @ApiOperation(value = "用户登录")
    @PostMapping("list/{current}/{limit}")
    public R findAll(@RequestBody(required = false) BillQuery billQuery){
        //查询所有的数据,七张包含总条数,每一页显示的数据
        Page<Bill> billPage = billService.pageAll(billQuery);
        //获取每一页显示的数据
        List<Bill> records = billPage.getRecords();
        //获取总条数
        long total = billPage.getTotal();
        return R.ok().data("total",total).data("rows",records);

    }

}

 

6.1.3 前端实现

 

6.1.3.1编写api接口 bill.js

import request from "@/utils/request"

//export function getList(current, limit, billQuery) {
export function getList(billQuery) {
    return request({
        // url: `/bill/list/${current}/${limit}`,
        url: `/bill/list`,
        method: 'post',
        data: billQuery
    })
}

6.1.3.2 编写内容

<template>
 <div class="app-container">
    供应商列表
  <!--查询表单-->
    <el-form :inline="true" class="demo-form-inline">
      <el-form-item>
        <el-input v-model="billQuery.productName" placeholder="商品名称"/>
      </el-form-item>
  <el-form-item label="供应商名称">
    <el-select v-model="billQuery.proName" placeholder="请选择供应商">
      <el-option label="区域一" value="shanghai"></el-option>
      <el-option label="区域二" value="beijing"></el-option>
    </el-select>
  </el-form-item>
 
      <el-form-item label="是否付款">
    <el-select v-model="billQuery.isPayment" placeholder="请选择是否付款">
      <el-option label="已付款" value="1"></el-option>
      <el-option label="未付款" value="2"></el-option>
    </el-select>
  </el-form-item>
      <el-button type="primary" icon="el-icon-search" @click="bill()" >查询</el-button>
      <el-button type="default" @click="resetData()">清空</el-button>
    </el-form>

    <!-- 表格 -->


  <el-table
    :data="list"
    border
    style="width: 100%">
    <el-table-column
      fixed
      prop="billCode"
      label="订单编号"
      width="120">
    </el-table-column>
    <el-table-column
      prop="productName"
      label="商品名称"
      width="140">
    </el-table-column>
     <!-- <el-table-column
      prop="proName"
      label="供应商名称"
      width="220">
    </el-table-column> -->
    <el-table-column
      prop="totalPrice"
      label="订单金额"
      width="120">
    </el-table-column>

    <el-table-column
      label="是否付款"
      width="100">
      
         <template slot-scope="scope">
          {{ scope.row.isPayment===1?'已付款':'未付款' }}
        </template>
    </el-table-column>
    <el-table-column
      prop="creationDate"
      label="创建时间"
      width="250">
    </el-table-column>
  
   <el-table-column
      fixed="right"
      label="操作"
      width="180">
      <template slot-scope="scope">
         <router-link :to="'/teacher/edit/'+scope.row.id">
            <el-button type="text"  size="small" icon="el-icon-share">查询</el-button>
          </router-link>
         <router-link :to="'/teacher/edit/'+scope.row.id">
            <el-button type="text"  size="small" icon="el-icon-edit">修改</el-button>
          </router-link>
          <el-button  type="danger" size="small" icon="el-icon-delete" @click="removeDataById(scope.row.id)">删除</el-button>
      </template>
    </el-table-column>
  </el-table>


  <!-- 分页 -->
     <el-pagination
       :current-page="billQuery.current"
      :page-size="billQuery.size"
      :total="total"
      style="padding: 30px 0; text-align: center;"
      layout="total, prev, pager, next, jumper"
      @current-change="bill"
    />
 </div>
</template>


<script>
import { getList } from '@/api/bill'

  export default {
    data() {
      return {
       list:null,
          // page:1,//当前页
          // limit:10,//每页记录数
           total:0,//总记录数
          billQuery:{
            current:1,//当前页
            size:10,//每页记录数
          } //条件封装对象
      }
    },
    created(){

        this.bill()
    }
    
    ,
    methods: {
      bill(current=1){
                this.billQuery.current=current
           //getList(this.page,this.limit,this.billQuery).then(resp => {
              getList(this.billQuery).then(resp => {
                console.log("------------",resp)
               this.list=resp.data.rows;
              this.total = resp.data.total


     }) 
      }
      ,
       resetData() {//清空的方法
            //表单输入项数据清空
            this.billQuery = {}
            //查询所有订单数据
            this.getList()
        },
    
    }
  }
</script>

6.1.3.3路由配置

 

    {
            path: '/bill',
            component: Layout,
            children: [{
                path: '/',
                component: Bill,
                meta: { title: '订单管理' }
            }]
        },

在AppMain中添加视图渲染

 

<template>
   <div class="main">
         这里是展示数据地方
      <router-view></router-view>
   </div>
</template>

<script>

</script>

6.2添加修改订单

6.2.1后端实现

6.2.1.1

在接口中添加修改的方法

   //修改订单
    public void update(Bill bill);

xml编写

    <update id="update" parameterType="bill">
        update    `smbms_bill`  set   billCode=#{billCode},productName=#{productName},productUnit=#{productUnit},totalPrice=#{totalPrice},isPayment=#{isPayment},providerId=#{providerId}  where  id=#{id}
    </update>

义务口的编写

 //修改订单
    public void update(Bill bill);

实现的类

 @Override
    public void update(Bill bill) {
        baseMapper.update(bill);
    }

控制器的编写

    @ApiOperation(value = "订单的修改")
    @GetMapping("update")
    public R update(@PathVariable Bill  bill){
         billService.update(bill);
        return R.ok();

    }

6.2.前端实现

 

 
     <el-table-column label="操作" width="150">
                <template slot-scope="scope">
                    <el-button
                    size="mini"
                    @click="handleEdit(scope.row.id)">编辑</el-button>
                    <el-button
                    size="mini"
                    type="danger"
                    @click="handleDelete(scope.row.id)">删除</el-button>
                </template>
             </el-table-column>
  
    <!-- 弹出新增窗口 
        title 窗口的标题
        :visible.sync 当它true的时候,窗口会被弹出
        -->
        <el-dialog title="订单编辑" :visible.sync="dialogFormVisible" width="500px">
            <el-form 
            ref="billForm"
            label-width="100px"
            label-position="right"
            style="width: 400px;"
            :model="eidtBill">
                <el-form-item label="商品名称" prop="productName" >
                <el-input v-model="eidtBill.productName" ></el-input>
                </el-form-item>

                 <el-form-item label="供应商名称">
                    <el-select v-model="eidtBill.providerId" placeholder="供应商名称">
                        <!-- 不要忘记 payTypeOptions 绑定到data中 -->
                        <el-option v-for="option in prolist" 
                        :key="option.id"
                        :label="option.proName"
                        :value="option.id"
                        ></el-option>
                    </el-select>
                </el-form-item>

                <!-- <el-form-item label="供应商名称" prop="proName" >
                <el-input v-model="eidtBill.proName" ></el-input>
                </el-form-item> -->
             
                <el-form-item label="订单金额" prop="totalPrice" >
                <el-input v-model="eidtBill.totalPrice" ></el-input>
                </el-form-item>                         
                  <el-form-item label="是否付款">
                     <el-select v-model="eidtBill.isPayment==1?'已付款':'未付款'" placeholder="请选择是否付款">
                      <el-option label="已付款" value="1"></el-option>
                      <el-option label="未付款" value="2"></el-option>
                    </el-select> 
                  </el-form-item>
            </el-form>
            <div slot="footer" class="dialog-footer">
                <el-button @click="dialogFormVisible = false">取 消</el-button>
                 <el-button type="primary" @click="updateData()">确 定</el-button>
            </div>
        </el-dialog>

 

 

 

 

第七章阿里云-对象存储OSS

官方参考文档:对象存储 OSS-阿里云帮助中心

对象存储服务(Object Storage Service,OSS)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任

意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。

开通对象存储OSS服务

  1. 访问阿里云 https://www.aliyun.com/ ,在右上角点击登录。

  2. 登录后,进入控制台

 

  1. 搜索 OSS , 找到 对象存储OSS

 

  1. 点击 立即开通

 

  1. 勾选,点击 立即开通

 

  1. 开通成功, 点击 管理控制台

 

购买 OSS 资源包

注意 开通 OSS 服务后,默认的计费方式是按量付费。如果想降低 OSS 费用,建议您购买资源包 。

  1. 点击 购买资源包

 

  1. 选择自己需要的资源包,学习按下图买即可。

 

  1. 前往支付

 

  1. 支付成功,回到管理控制台,查看得到购买记录

 

创建与删除存储空间 ( Bucket )

存储空间(Bucket)是用于存储对象(Object)的容器,可以存储若干文件。所以在上传任何文件到OSS之前,您必

须先创建存储空间。

创建 Bucket

进入 对象存储OSS 管理中心,阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台

  1. 打开创建Bucket对话框。单击Bucket列表,之后单击创建Bucket。

 

  1. 输入 Bucket 名称、和区域、存储类型:标签存储,其他默认就行,详细可见官方文档

注意:Bucket 创建成功后,您所选择的 存储类型、区域 不支持变更。

 

  1. 创建成功

 

  1. 查看 Bucket 相关信息,后面会使用到。

Endpoint:上传文件时使用

Bucket域名:查看文件时使用

 

删除 Bucket

  1. 如果需要删除 Bucket ,在 Bucket列表页点击 要删除的Bucket名称,进入详情页。

 

 

设置 Bucket 公共读权限

在页面上传文件

  1. 如下图,进入 Bucket ,找到 文件管理 ,点击 上传文件

 

设置 Bucket 公共读权限

  1. 点击图片文件名 Logo.png ,打开的右侧有个 URL,URL带上签名参数可以浏览器直接访问,会自动下载。

但是如果不带签名无法访问,提示如下没有权限:

 

 

 

 

创建 RAM 账号-AccessKey

阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常

运维,请登录 阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台 创建RAM账号。

创建用户

  1. 访问 阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台 ,输入登录名称、显示名称,勾选 编程访问 ,点击 确定

 

 

  1. 创建成功后,将查看到的 AccessKeyID 、AccessKeySecret **复制保存下来,在Java代码中要使用它操作

OSS,不然页面关闭后将无法再次获取到此信息。如果没有记录下来, 创建新的AccessKey

 

 

用户授权

对指定用户添加管理对象存储服务(OSS)服务。

  1. 访问 阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台 点击 添加权限

 

 

OSS 上传与删除图片文件

参考官方提供的 SDK: OSSJavaSDK兼容性和示例代码_对象存储-阿里云帮助中心

3758a8YW2zNr

JDK (Java Development Kit):Java 语言的软件开发工具包

SDK (Software Development Kit):为第三方开发者提供特定的软件开发工具包

添加 OSS SDK 依赖

<!-- aliyun oss -->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.8.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.5</version>
        </dependency>

 

创建OSS相关配置类 Properties

  1. 在 util 下创建一个 Properties 类,

统一管理整个项目在 application.yml 中自定义配置信息,比如:有阿里云相关的配置信息。

package cn.zpark.utils;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.Serializable;
@Setter
@Getter
@Component
@ConfigurationProperties(prefix = "zparkoss.blog")
public class Properties implements Serializable {
    // 会将 zparkoss.oss-cn-shenzhen.aliyuncs.com 绑定到 AliyunProperties 对象上。
    private AliyunProperties aliyun; }
  1. 在 util 下创建一个 阿里云配置信息类:

AliyunProperties

package cn.zpark.utils;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.Serializable;


@Data
public class AliyunProperties implements Serializable {
    /**
     * 阿里云地域端点
     */
    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    /**
     * 存储空间名称
     */
    private String bucketName;
    /**
     * Bucket域名,访问文件时作为URL前缀
     */
    private String bucketDomain;
}

配置 OSS 相关信息

在创建一个application.yml 文件在文件中添加如下配置:

zparkoss:
  blog:
  # 阿里云配置
    aliyun:
      endpoint: http://oss-cn-shenzhen.aliyuncs.com # OSS 端点,根据自己地域替换
      accessKeyId: LTAI5tBZzwox73f4X2dvBrrv # 根据自己的配置
      accessKeySecret: zWPePjWjHDBgHmg8Tdwal6HcRUDFWR # 根据自己的配置
      bucketName: zparkoss # 存储空间名称
      # Bucket域名,访问文件时作为URL前缀,注意前面加上 https 和结尾加上 /
      bucketDomain: https://zparkoss.oss-cn-shenzhen.aliyuncs.com/

工具类 AliyunUtil

  1. 在 util 添加阿里云工具类 AliyunUtil

涉及功能:上传、删除图片文件

package cn.zpark.utils;

import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.comm.ResponseMessage;
import com.aliyun.oss.model.PutObjectResult;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;


import java.util.UUID;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.comm.ResponseMessage;
import com.aliyun.oss.model.PutObjectResult;
import org.springframework.web.multipart.MultipartFile;
import java.util.Date;
import java.util.UUID;
/**
 * 阿里云工具类
 */
@Data
@Component
public final class AliyunUtil {
    @Autowired
    private  Properties properties;

    /**
     * 上传图片文件
     *
     * @param file MultipartFile文件对象
     * @return
     */
    public  R uploadFileToOss(MultipartFile file) {
      // 上传
        // 上传文件所在目录名,当天上传的文件放到当天日期的目录下。
        String folderName =DateFormatUtils.format(new Date(), "yyyyMMdd");
        // 保存到 OSS 中的文件名,采用 UUID 命名。
        String fileName = UUID.randomUUID().toString().replace("-", "");
        // 从原始文件名中,获取文件扩展名
        String fileExtensionName = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
        // 文件在 OSS 中存储的完整路径
        String filePath = folderName + "/" + fileName + fileExtensionName;
        OSS ossClient = null;
        try {

            // 获取 OSS 客户端实例
            ossClient = new OSSClientBuilder().build(properties.getAliyun().getEndpoint(),properties.getAliyun().getAccessKeyId(),properties.getAliyun().getAccessKeySecret());
            // 上传文件到OSS 并响应结果
            PutObjectResult putObjectResult = ossClient.putObject(properties.getAliyun().getBucketName(), filePath, file.getInputStream());
            ResponseMessage response = putObjectResult.getResponse();
            if(response == null) {
                // 上传成功
                // 返回上传文件的访问完整路径
                System.out.println("____________"+properties.getAliyun().getBucketDomain() + filePath );
                return R.ok().data("",properties.getAliyun().getBucketDomain() + filePath );
            }else {
                // 上传失败,OOS服务端会响应状态码和错误信息
                String errorMsg = "响应的错误状态码是【" + response.getStatusCode() +"】," + "错误信息【"+response.getErrorResponseAsString()+"】";
                return R.error();
            }
        } catch (Exception e) {
            return R.error();
        } finally {
            if (ossClient != null) {
                // 关闭OSSClient。
                ossClient.shutdown();
            }
        }
    }
    /**
     * 根据文件url删除
     * @param fileUrl
     */
    public  R delete(String fileUrl) {
        // 去除文件 url 中的 Bucket域名
        String filePath = fileUrl.replace(properties.getAliyun().getBucketDomain(), "");
        OSS ossClient = null;
        try {
            ossClient = new OSSClientBuilder().build(properties.getAliyun().getEndpoint(),properties.getAliyun().getAccessKeyId(),properties.getAliyun().getAccessKeySecret());
            // 删除
            ossClient.deleteObject(properties.getAliyun().getBucketName(), filePath);
            return R.ok();
        } catch (Exception e) {
            return R.error();
        }finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}

创建文件管理控制层

创建 FileController类

package cn.zpark.controller;


import cn.zpark.utils.AliyunUtil;
import cn.zpark.utils.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

/**
 * 图片文件控制器
 */
@Api(value = "文件管理接口", description = "文件管理接口,上传或删除图片文件")
@RequestMapping("/file")
@RestController
public class FileController {
    @Autowired
    private AliyunUtil aliyunUtil;

    @ApiOperation("上传文件到OSS")
    @PostMapping("/upload")
    public R upload(@RequestParam("file") MultipartFile file) {
        System.out.println(file);
        R r = aliyunUtil.uploadFileToOss(file);
        return   R.ok();
    }
    @ApiOperation("删除OOS服务器的文件")
    @ApiImplicitParam(name="fileUrl", value="要删除的文件URL", required=true)
    @DeleteMapping("/delete")
    public R delete(@RequestParam(value = "fileUrl", required = false) String fileUrl) {
        return aliyunUtil.delete(fileUrl);
    }
}

最后进行测试

第八章:添加订单

1后端的实现

创建接口

  //添加的功能
    public  void  add(Bill  bill);

业务接口‘

 //添加的功能
    public  void  add(Bill  bill);

接口实现类

 @Override
    public void add(Bill bill) {
        baseMapper.add(bill);
    }

控制器

    @ApiOperation(value = "用户登录")
    @PostMapping("add")
    public R add(@RequestBody Bill bill){
        System.out.println("添加----"+bill);
        return R.ok();
    }

2前端实现

定义的接口

 

    // 上传图片
    uploadImg(data = {}) {
        return request({
            url: `/file/upload`,
            method: 'post',
            data
        })
    },


    // 删除图片 /article/file/delete?fileUrl=http://xxxxx
    deleteImg(imageUrl) {
        return request({
            url: `/file/delete`,
            method: 'delete',
            params: { 'fileUrl': imageUrl }
        })
    },
    


    add(bill) {
        return request({
            url: `/bill/add`,
            method: 'post',
            data: bill
        })
    },

 

        
             <el-upload
                class="avatar-uploader"
                action=""
                :show-file-list="false"
                 :http-request="uploadMainImg">
                <img v-if="imageUrl" :src="imageUrl" class="avatar">
                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
                </el-upload>

 

 

   // 上传图片, file 上传的图片对象
        uploadMainImg(file) {
           console.log('file', file)
            const data = new FormData()
            data.append('file', file.file)
            bill.uploadImg(data).then(response => {
                 console.log("-------------------",response)
                // 回显图片
                this.formData.imageUrl = response.data
            }).catch(error => {
                this.$message({
                    type: 'error',
                    message: '上传失败'
                })
            }) 
        },
    
<template>
 <div class="app-container">
    供应商列表

        <el-dialog title="订单编辑" :visible.sync="dialogFormVisible" width="500px">
            <el-form 
            ref="billForm"
            label-width="100px"
            label-position="right"
            style="width: 400px;"
            :model="bill">
                <el-form-item label="商品名称" prop="productName" >
                <el-input v-model="bill.productName" ></el-input>
                </el-form-item>

                <el-form-item>
              
             <el-upload
                class="avatar-uploader"
                action=""
                :show-file-list="false"
                 :http-request="uploadMainImg">
                <img v-if="imageUrl" :src="imageUrl" class="avatar">
                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
                </el-upload>


    


            </el-form-item>

             
                <el-form-item label="订单金额" prop="totalPrice" >
                <el-input v-model="bill.totalPrice" ></el-input>
                </el-form-item>                         
                  <el-form-item label="是否付款">
                     <el-select v-model="bill.isPayment" placeholder="请选择是否付款">
                      <el-option label="已付款" value="1"></el-option>
                      <el-option label="未付款" value="2"></el-option>
                    </el-select> 
                  </el-form-item>
            </el-form>
            <div slot="footer" class="dialog-footer">
                <el-button @click="dialogFormVisible = false">取 消</el-button>
                 <el-button type="primary" @click="add()">确 定</el-button>
            </div>
        </el-dialog>
 </div>
</template>


<script>
import  bill  from '@/api/bill'
  export default {
    data() {
        
      return {
           imageUrl: '',
             formData: { // 提交表单数据
        }, 
      dialogFormVisible: true, //控制对话框
      bill:{}
      }
     
    },
   
    methods: {
          add(){
           bill.add(this.bill).then(resp=>{
               
           })
          },

   // 上传图片, file 上传的图片对象
        uploadMainImg(file) {
           console.log('file', file)
            const data = new FormData()
            data.append('file', file.file)
            bill.uploadImg(data).then(response => {
                 console.log("-------------------",response)
                // 回显图片
                this.formData.imageUrl = response.data
            }).catch(error => {
                this.$message({
                    type: 'error',
                    message: '上传失败'
                })
            }) 
        },
    
    }
  }
</script>
<style>
  .avatar-uploader .el-upload {
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
  }
  .avatar-uploader .el-upload:hover {
    border-color: #409EFF;
  }
  .avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 178px;
    height: 178px;
    line-height: 178px;
    text-align: center;
  }
  .avatar {
    width: 178px;
    height: 178px;
    display: block;
  }
</style>

 

        {
            path: '/edit',
            component: Layout,
            children: [{
                path: '/',
                component: Edit,
                meta: { title: '订单管理' }
            }]
        },

标签:return,管理系统,value,超市,订单,import,response,data,public
From: https://www.cnblogs.com/chengfaxing/p/16997908.html

相关文章